Webhooks
Delivery reports and inbound SMS callbacks — payload shapes, signatures, deduplication, and verification samples.
Two webhook families flow through the platform, both from the SMS gateway into SendAfrica (you configure them in your gateway dashboard). For *your own* app's webhooks about message delivery, use the SDK webhook helpers with your configured secret.
#Delivery report callback
/v1/sms/callbackWebhook token{
"id": "delivery-report",
"status": "Success",
"phoneNumber": "+255712345678",
"networkCode": "64004",
"failureReason": null,
"retryCount": 0
}- Events are deduplicated in Redis before updating the message log — replays are safe.
- The log entry transitions to
delivered,failed, orundeliveredbased on the report.
#Inbound SMS callback
/v1/sms/inboundWebhook tokenWired and deduplicated but currently logs only — there is no persistence layer for inbound messages yet. Plan two-way SMS features accordingly.
#Verifying SendAfrica webhooks in your app
When you configure a webhook secret, SendAfrica signs payloads with HMAC-SHA256 in the X-SendAfrica-Signature header. Always verify before parsing:
from fastapi import Request, HTTPException
@app.post("/webhooks/sendafrica")
async def webhook(request: Request):
event = client.webhooks.parse(
await request.body(),
signature=request.headers.get("X-SendAfrica-Signature"),
)
# raises WebhookSignatureError on mismatch
if event.type == "sms.delivered":
await mark_delivered(event.message_id)
elif event.type == "sms.failed":
await alert_ops(event.message_id, event.data)
return {"ok": True}Use raw bytes
Verify the signature against the raw request body, not a re-serialized JSON object — key order and whitespace changes break HMAC. Then respond 2xx quickly and process asynchronously if handling may be slow.