Skip to main content

Webhook Events

This page lists every event type AfriRoute can deliver to your webhook endpoint, along with example payloads. All events share the common envelope: id, event, created_at, and data.

📋 Event Catalog​

EventTrigger
sms.deliveredSMS confirmed delivered by the carrier
sms.failedSMS could not be delivered
payment.completedA charge succeeded
payment.failedA charge failed or was declined
payout.completedA payout/disbursement settled
voice.completedA voice call ended normally
voice.failedA voice call failed to connect
verification.completedAn identity verification finished successfully
verification.failedAn identity verification was rejected

📨 SMS Events​

{
"id": "evt_sms_001",
"event": "sms.delivered",
"created_at": "2026-05-10T10:30:05Z",
"data": {
"message_id": "msg_abc123",
"to": "+251911234567",
"status": "delivered",
"segments": 1,
"delivered_at": "2026-05-10T10:30:05Z"
}
}
{
"id": "evt_sms_002",
"event": "sms.failed",
"created_at": "2026-05-10T10:30:09Z",
"data": {
"message_id": "msg_def456",
"to": "+251911234567",
"status": "failed",
"error_code": "UNDELIVERABLE",
"error": "Number unreachable"
}
}

💰 Payment Events​

{
"id": "evt_pay_001",
"event": "payment.completed",
"created_at": "2026-05-10T11:02:00Z",
"data": {
"transaction_id": "txn_7g8h9",
"amount": 1500,
"currency": "KES",
"method": "mpesa",
"status": "completed",
"completed_at": "2026-05-10T11:02:00Z"
}
}
{
"id": "evt_pay_002",
"event": "payment.failed",
"created_at": "2026-05-10T11:05:00Z",
"data": {
"transaction_id": "txn_1a2b3",
"amount": 1500,
"currency": "KES",
"status": "failed",
"error_code": "INSUFFICIENT_FUNDS"
}
}

📞 Voice Events​

{
"id": "evt_voice_001",
"event": "voice.completed",
"created_at": "2026-05-10T12:00:30Z",
"data": {
"call_id": "call_xy12",
"to": "+254712345678",
"duration_seconds": 73,
"status": "completed"
}
}

🪪 Identity Events​

{
"id": "evt_id_001",
"event": "verification.completed",
"created_at": "2026-05-10T14:30:45Z",
"data": {
"verification_id": "ver_abc123xyz",
"status": "verified",
"results": {
"document_authentic": true,
"face_match": true,
"liveness_passed": true,
"overall_risk": "low"
}
}
}
{
"id": "evt_id_002",
"event": "verification.failed",
"created_at": "2026-05-10T14:31:10Z",
"data": {
"verification_id": "ver_zzz999",
"status": "rejected",
"failure_reasons": ["face_mismatch", "liveness_failed"]
}
}

🧭 Routing Events (Node.js)​

const handlers = {
'sms.delivered': (d) => updateMessage(d.message_id, 'delivered'),
'sms.failed': (d) => updateMessage(d.message_id, 'failed'),
'payment.completed': (d) => fulfillOrder(d.transaction_id),
'verification.completed': (d) => approveCustomer(d.verification_id)
};

app.post('/webhook', (req, res) => {
const { event, data } = req.body; // verify signature first
(handlers[event] || (() => console.log('unhandled', event)))(data);
res.sendStatus(200);
});

💡 Best Practices​

  • Switch on event and default-handle unknown types so new events don't break your endpoint.
  • Treat data as additive — AfriRoute may add fields without a version bump.
  • Key idempotency on id, not on resource IDs that can repeat across events.
  • Subscribe narrowly in configuration to only the events you process.

Last Updated: May 2026 | Need help? [email protected]