Webhooks
We POST a signed notification to your server whenever a message reaches its final state. This is how you learn what was actually delivered.
Setting it up
Set your notification URL in Settings. It must be a public HTTPS address. URLs pointing at private networks, internal hostnames or our own servers are rejected when you save them and again before every delivery.
Payload
{
"event": "message.delivered",
"data": {
"message_id": "cmu9x2k4a0001abcd",
"recipient": "+233241234567",
"sender": "JKASMS",
"status": "DELIVERED",
"segments": 1,
"cost": "0.06",
"currency": "GHS",
"campaign_id": null,
"failure_reason": null,
"submitted_at": "2026-09-21T10:30:02.000Z",
"delivered_at": "2026-09-21T10:30:04.000Z",
"failed_at": null
},
"sent_at": "2026-09-21T10:30:04.120Z"
}Events
| Event | When |
|---|---|
message.submitted | A network accepted the message. This is when you are charged. |
message.delivered | The handset confirmed receipt. |
message.failed | The message failed, was rejected, or expired. |
voice.call.answered | A call was picked up and the recording played. This is when a call is charged. |
voice.call.no_answer | A call rang out. Its call time is released. |
voice.call.busy | Declined, engaged, unreachable or switched off. Released. |
voice.call.failed | The call could not be placed. Released. |
voice.call.cancelled | You cancelled the campaign before this call dialled. |
Voice events carry a call_id and credits_used in seconds where a message event carries a message_id and a cost in cedis — the two channels bill in different units. The envelope, the signature and the retry schedule are identical, so one handler can switch on event and treat both. See Send a voice message for a full voice payload.
Responding
Return any 2xx as soon as you have stored the event. Do the real work afterwards — we time out after 15 seconds and treat that as a failure.
Retries
Any non-2xx or a timeout is retried five times with exponential backoff: roughly 10s, 40s, 2.5min, 10min, 43min. That covers a typical deploy window. After that the delivery is marked failed and we stop.
message_id plus status.Redirects are not followed
A redirect could point us at an address the pre-flight safety check never saw, so we do not follow them. Give us the final URL.
Next: verify the signature.