SendAfrica logoSendAfricaDocs

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

POST/v1/sms/callbackWebhook token
Delivery report payload (handled by SendAfrica)
json
{
  "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, or undelivered based on the report.

#Inbound SMS callback

POST/v1/sms/inboundWebhook token

Wired 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:

webhook.py
python
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.