Skip to main content

Overview

Webhooks allow you to receive real-time HTTP POST notifications when delivery sessions start, stop, and when Desmo generates insights. Instead of polling our API, configure a webhook endpoint to receive updates automatically.
Webhooks are the recommended way to integrate Desmo into your backend systems. They provide real-time notifications for every delivery across your entire fleet.

Events

Desmo sends three types of webhook events:
EventWhen it firesUse case
session.startedDriver starts a delivery sessionTrack active deliveries in your system
session.stoppedDriver ends a delivery sessionMark delivery as complete
insight.updatedProcessing generates/updates insightsGet fraud scores, verification data

How It Works

Setting Up Webhooks

You can configure webhooks via the Dashboard UI or programmatically via API.

Option 1: Dashboard

  1. Go to Desmo DashboardSettings
  2. Enter your Webhook URL (must be HTTPS)
  3. Copy and securely store the generated secret
Configure webhooks programmatically using your secret key (sk_):
# Create webhook
curl -X POST https://api.getdesmo.io/v1/webhooks \
  -H "Authorization: Bearer sk_live_your_secret_key" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://api.yourcompany.com/webhooks/desmo"}'
Response:
{
  "id": "64a1b2c3d4e5f6789",
  "url": "https://api.yourcompany.com/webhooks/desmo",
  "enabled": true,
  "createdAt": "2026-01-12T10:00:00Z",
  "secret": "whsec_abc123..."
}
The secret is only returned once during creation. Store it securely—you’ll use it to verify webhook signatures.

API Endpoints

MethodEndpointDescription
POST/v1/webhooksCreate webhook configuration
GET/v1/webhooksGet current webhook configuration
PUT/v1/webhooksUpdate webhook (URL, enabled)
DELETE/v1/webhooksDelete webhook configuration
POST/v1/webhooks/rotateRotate webhook secret
Update webhook:
curl -X PUT https://api.getdesmo.io/v1/webhooks \
  -H "Authorization: Bearer sk_live_your_secret_key" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://new-url.com/webhook", "enabled": true}'
Rotate secret:
curl -X POST https://api.getdesmo.io/v1/webhooks/rotate \
  -H "Authorization: Bearer sk_live_your_secret_key"

Webhook Payloads

session.started

Sent when a driver starts a new delivery session.
{
  "event": "session.started",
  "timestamp": "2026-01-12T10:30:00Z",
  "data": {
    "sessionId": "sess_abc123",
    "deliveryId": "DEL-001",
    "organizationId": "org_xyz",
    "externalRiderId": "driver_456",
    "sessionType": "drop",
    "status": "recording",
    "env": "live",
    "address": {
      "line1": "123 Main St",
      "city": "Bangalore",
      "country": "IN",
      "lat": 12.9716,
      "lng": 77.5946
    },
    "createdAt": "2026-01-12T10:30:00Z",
    "endedAt": null
  }
}

session.stopped

Sent when a driver ends a delivery session.
{
  "event": "session.stopped",
  "timestamp": "2026-01-12T10:45:00Z",
  "data": {
    "sessionId": "sess_abc123",
    "deliveryId": "DEL-001",
    "organizationId": "org_xyz",
    "externalRiderId": "driver_456",
    "sessionType": "drop",
    "status": "completed",
    "env": "live",
    "address": {
      "line1": "123 Main St",
      "city": "Bangalore",
      "country": "IN",
      "lat": 12.9716,
      "lng": 77.5946
    },
    "createdAt": "2026-01-12T10:30:00Z",
    "endedAt": "2026-01-12T10:45:00Z"
  }
}

insight.updated

Sent when Desmo generates or updates insights for a session.
{
  "event": "insight.updated",
  "timestamp": "2026-01-12T10:46:00Z",
  "data": {
    "sessionId": "sess_abc123",
    "deliveryId": "DEL-001",
    "organizationId": "org_xyz",
    "status": "ready",
    "summary": {
      "rightDoorstep": true,
      "suspiciousBehavior": false,
      "floorsClimbed": 3,
      "elevatorUsed": true,
      "frictionScore": 0.15,
      "fraudScore": 0.05,
      "visibilityScore": 0.92
    },
    "reasonCodes": ["FULL_ATTEMPT", "DELIVERED_TO_RIGHT_DOORSTEP"],
    "events": [
      {
        "type": "WALKING",
        "tStart": 0,
        "tEnd": 45,
        "confidence": 0.95
      },
      {
        "type": "ELEVATOR",
        "tStart": 50,
        "tEnd": 65,
        "metadata": { "floors": 3, "direction": "up" }
      },
      {
        "type": "GATE",
        "tStart": 70,
        "confidence": 0.88
      }
    ]
  }
}

Reason Codes

CodeDescription
FULL_ATTEMPTComplete delivery attempt detected
DELIVERED_TO_RIGHT_DOORSTEPGPS and movement confirm correct location
PARTIAL_ATTEMPTIncomplete delivery attempt
POSSIBLE_LOBBY_HANDOFFDelivery may have been left in lobby
POSSIBLE_NO_ATTEMPTNo delivery attempt detected
INSUFFICIENT_DATANot enough telemetry to verify

Verifying Webhook Signatures

All webhook requests include a signature header to verify authenticity:
X-Desmo-Signature: sha256=abc123...
X-Desmo-Event: session.started
X-Desmo-Delivery: sess_abc123

Verification Example (Node.js)

const crypto = require('crypto');

function verifyWebhookSignature(payload, signature, secret) {
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');
  
  const expected = `sha256=${expectedSignature}`;
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

// In your webhook handler
app.post('/webhook/desmo', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.headers['x-desmo-signature'];
  const payload = req.body.toString();
  
  if (!verifyWebhookSignature(payload, signature, process.env.DESMO_WEBHOOK_SECRET)) {
    return res.status(401).send('Invalid signature');
  }
  
  const event = JSON.parse(payload);
  
  switch (event.event) {
    case 'session.started':
      console.log(`Delivery started: ${event.data.sessionId}`);
      // Track in your system
      break;
    case 'session.stopped':
      console.log(`Delivery ended: ${event.data.sessionId}`);
      // Mark delivery complete
      break;
    case 'insight.updated':
      console.log(`Insights ready: ${event.data.reasonCodes.join(', ')}`);
      // Process fraud scores, verification data
      break;
  }
  
  res.status(200).send('OK');
});

Verification Example (Python)

import hmac
import hashlib
import json

def verify_webhook_signature(payload: bytes, signature: str, secret: str) -> bool:
    expected = hmac.new(
        secret.encode(),
        payload,
        hashlib.sha256
    ).hexdigest()
    
    expected_signature = f"sha256={expected}"
    return hmac.compare_digest(signature, expected_signature)

# In your webhook handler (FastAPI)
@app.post("/webhook/desmo")
async def handle_webhook(request: Request):
    signature = request.headers.get("X-Desmo-Signature")
    payload = await request.body()
    
    if not verify_webhook_signature(payload, signature, WEBHOOK_SECRET):
        raise HTTPException(status_code=401, detail="Invalid signature")
    
    event = json.loads(payload)
    
    if event["event"] == "session.started":
        print(f"Delivery started: {event['data']['sessionId']}")
    elif event["event"] == "session.stopped":
        print(f"Delivery ended: {event['data']['sessionId']}")
    elif event["event"] == "insight.updated":
        print(f"Insights: {event['data']['reasonCodes']}")
    
    return {"status": "ok"}

Best Practices

Respond Quickly

Return a 200 OK response within 5 seconds. Process webhook data asynchronously if needed.

Handle Duplicates

Webhooks may be retried. Use sessionId to deduplicate events idempotently.

Verify Signatures

Always verify the X-Desmo-Signature header before processing webhook data.

Use HTTPS

Your webhook endpoint must use HTTPS with a valid SSL certificate.

Retry Policy

If your webhook endpoint returns a non-2xx status code or times out, Desmo will retry the delivery:
AttemptDelay
1st retry1 minute
2nd retry5 minutes
3rd retry30 minutes
4th retry2 hours
5th retry24 hours
After 5 failed attempts, the webhook delivery is marked as failed. You can view failed deliveries in the Dashboard.

Testing Webhooks

Using the Dashboard

  1. Go to Settings → Webhooks
  2. Click Send Test Event
  3. A sample event will be sent to your configured URL

Using ngrok for Local Development

# Expose your local server
ngrok http 3000

# Use the ngrok URL as your webhook endpoint
# https://abc123.ngrok.io/webhook/desmo

Troubleshooting

  • Verify your webhook URL is correct and publicly accessible
  • Check that your server responds with 200 OK
  • Ensure your firewall allows incoming HTTPS traffic
  • Check the webhook logs in the Dashboard for error details
  • Ensure you’re using the raw request body (not parsed JSON)
  • Verify you’re using the correct webhook secret
  • Check that no middleware is modifying the request body
  • This is expected behavior for reliability
  • Use sessionId as an idempotency key
  • Store processed session IDs to skip duplicates

Next Steps

API Reference

Explore the full API for fetching sessions and insights

SDK Integration

Set up the mobile SDK to start collecting telemetry