Delivery Reports
Track the real-time delivery status of every message you send. AfriRoute supports two complementary approaches: pull the current status with the status endpoint, or push status changes to your server via webhooks. Webhooks are recommended for production.
๐ Delivery Statusesโ
| Status | Meaning |
|---|---|
queued | Accepted by AfriRoute, awaiting dispatch |
sent | Handed off to the mobile operator |
delivered | Confirmed delivered to the handset |
failed | Could not be delivered (see Error Codes) |
๐ก Get Message Status (Pull)โ
GET /v1/sms/{message_id}
| Parameter | In | Required | Description |
|---|---|---|---|
message_id | path | Yes | The ID returned when the message was sent |
Example Requestโ
curl https://api.afriroute.ai/api/v1/sms/msg_abc123xyz \
-H "Authorization: Bearer $AFRIROUTE_API_KEY"
const res = await fetch('https://api.afriroute.ai/api/v1/sms/msg_abc123xyz', {
headers: { 'Authorization': 'Bearer $AFRIROUTE_API_KEY' }
});
const status = await res.json();
console.log(status.status); // delivered
import requests
res = requests.get(
'https://api.afriroute.ai/api/v1/sms/msg_abc123xyz',
headers={'Authorization': 'Bearer $AFRIROUTE_API_KEY'},
)
print(res.json()['status'])
Responseโ
{
"message_id": "msg_abc123xyz",
"status": "delivered",
"to": "+251911234567",
"from": "AFRIROUTE",
"parts": 1,
"cost": 0.05,
"currency": "ETB",
"created_at": "2026-05-28T10:30:00Z",
"delivered_at": "2026-05-28T10:30:05Z"
}
๐ Delivery Webhooks (Push)โ
Provide a callback_url when sending a message (see Send SMS). AfriRoute will POST a JSON payload to that URL each time the message's status changes.
Webhook Payloadโ
{
"event": "sms.status",
"message_id": "msg_abc123xyz",
"status": "delivered",
"to": "+251911234567",
"from": "AFRIROUTE",
"parts": 1,
"error_code": null,
"metadata": { "order_id": "ord_9981" },
"timestamp": "2026-05-28T10:30:05Z"
}
Handling the Webhookโ
app.post('/webhooks/sms', async (req, res) => {
const { message_id, status, error_code } = req.body;
// Respond quickly with 200 so AfriRoute does not retry
res.sendStatus(200);
await updateMessageStatus(message_id, status, error_code);
});
from flask import Flask, request
app = Flask(__name__)
@app.post('/webhooks/sms')
def sms_webhook():
payload = request.get_json()
update_message_status(payload['message_id'], payload['status'])
return '', 200
Retries & Reliabilityโ
- Your endpoint must return a
2xxwithin 5 seconds, or the delivery is treated as failed. - Failed deliveries are retried with exponential backoff for up to 24 hours.
- The same status may be delivered more than once โ make your handler idempotent using
message_id.
Verifying Authenticityโ
Each webhook includes an X-AfriRoute-Signature header containing an HMAC-SHA256 of the raw body signed with your webhook secret. Recompute and compare it before trusting the payload.
const crypto = require('crypto');
function verify(rawBody, signature, secret) {
const expected = crypto.createHmac('sha256', secret)
.update(rawBody).digest('hex');
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
}
๐ก Best Practicesโ
- Prefer webhooks over polling for accurate, low-latency status.
- Respond
200immediately, then process asynchronously. - Make handlers idempotent to tolerate duplicate deliveries.
- Always verify the signature before acting on a payload.
- Use
metadatato correlate callbacks with your own records.
๐ Related Resourcesโ
Last Updated: May 2026 ยท Need help? Contact Support โ