RafikiSMSRafikiSMS API

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

  1. Register your HTTPS endpoint URL in Profile -> Integration -> Delivery Webhook in the dashboard, or via the API below.
  2. When an SMS delivery status changes, RafikiSMS POSTs a JSON payload to your URL.
  3. Your endpoint must respond 2xx within 8 seconds.
  4. 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

FieldTypeDescription
eventstringAlways "sms.delivery_status"
event_timeISO 8601When the event was emitted by the server
vendor.idintegerYour vendor account ID
vendor.emailstringYour vendor account email
data.sms_log_idintegerInternal SMS log ID --- use as idempotency key
data.statusstring"delivered" | "pending" | "failed" | "undelivered"
data.recipientstringDestination phone number
data.sender_idstringSender name used for this message
data.transaction_idstringCarrier/gateway transaction reference
data.messagestringThe SMS text content
data.delivered_atISO 8601 | nullConfirmed delivery timestamp, or null
data.failure_reasonstring | nullReason for failure if status is failed
data.processing_time_msintegerEnd-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.

AttemptDelay (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 200 immediately, then handle the payload in a background job or queue.
  • Use data.sms_log_id as 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.