WEBHOOKS

Webhooks

Receive real-time notifications when message status changes. Configure webhook URLs to get delivery confirmations, failures, and other events pushed to your server.

How Webhooks Work

1

Configure your URL

Set a webhook URL in your API request or client settings.

2

We send a POST request

When an event occurs (delivery, failure, etc.), we POST JSON to your URL.

3

Your server responds

Return HTTP 200 within 5 seconds to acknowledge receipt.

4

Retry on failure

If your server doesn't respond with 200, we retry up to 5 times with exponential backoff.

Webhook Payload

All webhook payloads are sent as JSON via HTTP POST. The payload includes:

Webhook payload — SMS delivery
{
  "event": "sms.delivered",
  "timestamp": "2026-01-13T10:30:15Z",
  "data": {
    "message_id": "msg_abc123xyz789",
    "recipient": "+265888123456",
    "status": "delivered",
    "sent_at": "2026-01-13T10:30:00Z",
    "delivered_at": "2026-01-13T10:30:15Z",
    "cost": 0.05,
    "currency": "MWK",
    "reference": "txn_12345",
    "client_id": "client_abc123"
  }
}

Event Types

EventDescription
sms.sentMessage sent to carrier
sms.deliveredMessage delivered to recipient
sms.failedDelivery failed
sms.expiredDelivery timeout
whatsapp.sentWhatsApp message sent
whatsapp.deliveredWhatsApp message delivered
whatsapp.readWhatsApp message read
whatsapp.failedWhatsApp delivery failed

Setting Up Webhooks

Option 1: Per-Request Webhook

Pass the webhook_url parameter when sending a message:

Per-request webhook
curl -X POST "https://api.yourdomain.com/api/v1/sms/send" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "+265888123456",
    "message": "Hello!",
    "webhook_url": "https://yourserver.com/webhooks/sms"
  }'

Option 2: Account-Level Webhook

Configure a default webhook URL in your client settings. All messages will trigger callbacks to this URL.

Verifying Webhook Signatures

Each webhook includes an X-Webhook-Signature header to verify the request is authentic.

Python — Verify webhook signature
import hmac
import hashlib

def verify_webhook(payload_body, signature_header, webhook_secret):
    """Verify the webhook signature"""
    expected = hmac.new(
        webhook_secret.encode("utf-8"),
        payload_body,
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature_header)

# In your Flask/FastAPI handler:
@app.post("/webhooks/sms")
async def handle_webhook(request: Request):
    body = await request.body()
    signature = request.headers.get("X-Webhook-Signature", "")
    
    if not verify_webhook(body, signature, WEBHOOK_SECRET):
        return {"error": "Invalid signature"}, 401
    
    event = await request.json()
    print(f"Received event: {event['event']}")
    return {"status": "ok"}

Retry Policy

AttemptDelay
1st retry30 seconds
2nd retry2 minutes
3rd retry10 minutes
4th retry1 hour
5th retry6 hours

After 5 failed attempts, the webhook is marked as failed and an alert is logged. You can view failed webhooks in the admin dashboard under Activity Logs.