Webhooks
Receive real-time notifications for message events.
Setting up webhooks
Configure webhooks in the Dashboard → Settings → Webhooks. Each webhook configuration includes:
- URL - The endpoint that will receive POST requests
- Events - Which events to subscribe to
- Secret - Used to sign payloads for verification (auto-generated, AES-encrypted at rest)
Event types
| Event | Direction | Description |
|---|---|---|
sms_received | Incoming | An SMS was received by a device |
sms_sent | Outgoing | A device confirmed the SMS was sent |
sms_delivered | Outgoing | The carrier confirmed the SMS was delivered |
sms_failed | Outgoing | The SMS could not be sent or delivered |
Webhook payloads
Vendel sends a POST request with a JSON body. Keys are sorted alphabetically for deterministic HMAC signing. The payload fields depend on the event type.
sms_received
{
"body": "Hello, this is a test message",
"event": "sms_received",
"from": "+1234567890",
"message_id": "a1b2c3d4e5f6g7h",
"timestamp": "2024-01-15 10:30:05.000Z"
} sms_sent / sms_delivered
{
"body": "Your verification code is 847293",
"delivered_at": "2024-01-15 10:31:12.000Z",
"event": "sms_delivered",
"message_id": "a1b2c3d4e5f6g7h",
"sent_at": "2024-01-15 10:30:08.000Z",
"status": "delivered",
"timestamp": "2024-01-15 10:30:05.000Z",
"to": "+1234567890"
} sms_failed
{
"body": "Your verification code is 847293",
"error_message": "FCM not initialized",
"event": "sms_failed",
"message_id": "a1b2c3d4e5f6g7h",
"status": "failed",
"timestamp": "2024-01-15 10:30:05.000Z",
"to": "+1234567890"
} Verifying webhooks
Webhooks include an HMAC-SHA256 signature in the X-Webhook-Signature header. The signature is computed over the entire JSON body using your webhook secret:
X-Webhook-Signature: a1b2c3d4e5f6... Verify it using your webhook secret:
import hmac
import hashlib
def verify_signature(payload_bytes, signature, secret):
"""Verify the webhook signature.
Important: Use the raw request body (not re-serialized JSON)
since the signature is computed over sorted-key JSON.
"""
expected = hmac.new(
secret.encode(),
payload_bytes,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature) import { createHmac } from "crypto";
function verifySignature(payloadBuffer, signature, secret) {
const expected = createHmac("sha256", secret)
.update(payloadBuffer)
.digest("hex");
return expected === signature;
} Retry policy
Failed webhook deliveries are retried with exponential backoff:
- 3 retries with increasing delays
- Webhook marked as failed after all retries are exhausted
Related
- Send SMS - Send messages
- Message Status - Poll for status