Skip to content
← API marketplace

Webhooks & signatures

Register an HTTPS endpoint in the portal or with POST /v1/webhooks. We POST a JSON envelope for each event you subscribe to:

  • message.submitted
  • message.failed
  • contact.opted_out
  • campaign.completed
  • queue.alert
  • balance.low

Respond with any 2xx within 10 seconds. Anything else is retried 8 times over about 23 hours (30 s, 2 m, 10 m, 30 m, 1 h, 3 h, 6 h, 12 h), then moved to the dead-letter list where you can replay it. Redirects are not followed. Use the event id to de-duplicate.

Payload

{
  "id": "evt_01a19900000070008000000000000001",
  "type": "message.submitted",
  "tenant_id": "01a19900-0000-7000-8000-0000000000aa",
  "occurred_at": "2026-11-01T08:00:01.000Z",
  "data": {
    "message_id": "01a19900-0000-7000-8000-00000000abcd",
    "to": "+27821234567",
    "from": "ACME",
    "status": "submitted",
    "error_code": null,
    "client_ref": "order-1042",
    "campaign_id": null,
    "parts": 1
  }
}

Verify every request

Headers: X-Timestamp (unix seconds) and X-Signature: sha256=<hex> = HMAC-SHA256 of "<X-Timestamp>.<raw body>" with your webhook secret. Reject anything older than 5 minutes and compare in constant time. Verify against the raw bytes, before JSON parsing.

# Signature = "sha256=" + hex(HMAC_SHA256(secret, "<X-Timestamp>.<raw body>"))
# Reject requests whose X-Timestamp is more than 5 minutes old, and compare in constant time.