Webhooks
RafikiSMS pushes real-time delivery status events to a URL you control. Configure your endpoint once and receive a POST every time an SMS delivery status changes.
How It Works
- Register your HTTPS endpoint URL in Profile -> Integration -> Delivery Webhook in the dashboard, or via the API below.
- When an SMS delivery status changes, RafikiSMS POSTs a JSON payload to your URL.
- Your endpoint must respond
2xxwithin 8 seconds. - Non-2xx or timeout triggers automatic retries up to 5 times with exponential backoff.
Registering Your URL
Send a PUT request to /v1/vendors/delivery-webhook with a Bearer access token. Only the parent vendor account can set this --- sub-accounts inherit it. Pass null to disable.
curl -X PUT "https://api.rafikisms.com/v1/vendors/delivery-webhook" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"delivery_webhook_url": "https://yourapp.com/webhooks/sms"}'On success the API responds 200 with {"delivery_webhook_url": "..."}.
Event Payload
RafikiSMS sends Content-Type: application/json:
{
"event": "sms.delivery_status",
"event_time": "2025-07-08T14:08:02+00:00",
"vendor": {
"id": 42,
"uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"email": "vendor@example.com"
},
"data": {
"sms_log_id": 19725260,
"status": "delivered",
"recipient": "255712345678",
"sender_id": "MYBRAND",
"transaction_id": "19725260",
"message_id": "msg_abc123",
"message": "Hello from RafikiSMS!",
"delivered_at": "2025-07-08T14:08:02+00:00",
"failure_reason": null,
"source_addr": "MYBRAND",
"dest_addr": "255712345678",
"processing_time_ms": 1240,
"created_at": "2025-07-08T14:07:56+00:00",
"updated_at": "2025-07-08T14:08:02+00:00"
}
}Field Reference
| Field | Type | Description |
|---|---|---|
event | string | Always "sms.delivery_status" |
event_time | ISO 8601 | When the event was emitted by the server |
vendor.id | integer | Your vendor account ID |
vendor.email | string | Your vendor account email |
data.sms_log_id | integer | Internal SMS log ID --- use as idempotency key |
data.status | string | "delivered" | "pending" | "failed" | "undelivered" |
data.recipient | string | Destination phone number |
data.sender_id | string | Sender name used for this message |
data.transaction_id | string | Carrier/gateway transaction reference |
data.message | string | The SMS text content |
data.delivered_at | ISO 8601 | null | Confirmed delivery timestamp, or null |
data.failure_reason | string | null | Reason for failure if status is failed |
data.processing_time_ms | integer | End-to-end processing time in milliseconds |
Handling Webhooks
Always respond 200 immediately, then process asynchronously to avoid timeouts:
// Express.js example
app.post('/webhooks/sms', express.json(), (req, res) => {
// Respond immediately --- process async
res.sendStatus(200);
const { event, data } = req.body;
if (event === 'sms.delivery_status') {
console.log(`SMS ${data.sms_log_id} -> ${data.status}`);
// Use data.sms_log_id as idempotency key
}
});Retry Policy
Non-2xx responses and timeouts trigger retries. Only 5xx and 429 are retried --- 4xx is treated as permanent and not retried.
| Attempt | Delay (approx.) |
|---|---|
| 1st retry | ~10 s |
| 2nd retry | ~30 s |
| 3rd retry | ~2 min |
| 4th retry | ~5 min |
| 5th (final) | no further retries |
Delays include up to 25% random jitter to spread load.
Circuit Breaker
If your endpoint repeatedly fails, a circuit breaker temporarily pauses delivery to that URL to protect your server. Webhooks resume automatically once the circuit resets. Make sure your endpoint responds within 8 seconds.
Best Practices
- Return
200immediately, then handle the payload in a background job or queue. - Use
data.sms_log_idas an idempotency key --- the same event may arrive more than once. - Your URL must use HTTPS.
- Log all incoming payloads before processing so you can replay events if needed.
- Keep your handler fast --- offload any DB writes or API calls to a queue.