← 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.submittedmessage.failedcontact.opted_outcampaign.completedqueue.alertbalance.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.