Events
Every event type, when it fires, and exactly what it contains.
Every event has the same envelope:
{
"id": "evt_6a1f0c2e9b8d4f3aa1c27e5d9b0f4e21",
"type": "sms.delivered",
"created_at": "2026-10-19T08:15:02Z",
"account_id": 1042,
"data": { "object": "sms", "...": "..." }
}Prop
Type
Event types
| Type | Fires when |
|---|---|
sms.delivered | The network confirmed the SMS reached the handset. |
sms.failed | The SMS can't be delivered: the network refused it (refunded) or reported it undeliverable. |
email.delivered | The recipient's mail server accepted the email, or the recipient opened it. Fires once per email. |
email.bounced | The email hard-bounced. The address is now on your suppression list. |
email.failed | We couldn't hand the email to the mail system. The cost is refunded. |
webhook.test | You called POST /webhook-endpoint/test. Always sent, whatever your events filter. |
Each event fires once per message, when its status changes. A message that's already delivered doesn't produce a second sms.delivered.
SMS payload
{
"object": "sms",
"id": "3fae8b8e-9c3a-4e9a-9d3a-7e8e9c3a4e9a",
"recipient": "+254712345678",
"status": "delivered",
"traffic_class": "transactional",
"sender_id": "ACMEBANK",
"batch_id": "1c9d7f2a-55e3-4b8e-9a61-0f2d3c4b5a69",
"price": "0.50",
"currency": "KES",
"delivery_description": "DeliveredToTerminal",
"delivered_at": "2026-10-19T08:15:01Z",
"created_at": "2026-10-19T08:15:00Z"
}Prop
Type
Email payload
{
"object": "email",
"id": "8b2d3c4e-1f0a-4c6b-9e8d-7a6b5c4d3e2f",
"recipient": "jane@example.com",
"subject": "Your receipt for INV-2026-0042",
"status": "bounced",
"batch_id": "2a3b4c5d-6e7f-4a8b-9c0d-1e2f3a4b5c6d",
"price": "0.25",
"currency": "KES",
"reason": "hardbounce",
"delivered_at": null,
"created_at": "2026-10-19T08:15:00Z"
}reason is set on email.bounced and holds the mail system's event name. The other fields match the SMS payload.