Appearance
Webhooks
Webhooks let you receive real-time notifications about events in your SendAPI account — delivery receipts, inbound messages, email open/click events, and more — instead of polling the API.
Subscription Requirement
Paid Feature
Webhook management endpoints (POST/PUT/DELETE /v1/webhooks) require a paid subscription. They're not available during the 3-day free trial. Upgrade your plan in the Billing dashboard to enable webhooks.
Manage Webhooks
You manage webhooks programmatically through the /v1/webhooks API or from the dashboard.
Create a Webhook
POST /v1/webhooks
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
url | string | Yes | Your HTTPS endpoint that will receive webhook POSTs. |
events | array | Yes | Event types to subscribe to. Use ["*"] for everything or wildcards like ["message.*"]. |
bash
curl -X POST https://sendapi.co/v1/webhooks \
-H "Authorization: Bearer sk_live_123456789" \
-H "Content-Type: application/json" \
-d '{
"url": "https://yourserver.com/webhooks/sendapi",
"events": ["message.sent", "message.delivered", "email.bounced"]
}'Response
json
{
"data": {
"id": 4,
"url": "https://yourserver.com/webhooks/sendapi",
"events": ["message.sent", "message.delivered", "email.bounced"],
"secret": "whsec_a7c8d9e0f1...",
"is_active": true,
"failure_count": 0,
"created_at": "2026-03-09T14:30:00.000000Z"
}
}The secret is returned once at creation — store it immediately, it's how you verify signature authenticity. Future GETs only return its prefix.
List Webhooks
GET /v1/webhooks
bash
curl https://sendapi.co/v1/webhooks \
-H "Authorization: Bearer sk_live_123456789"Update a Webhook
PUT /v1/webhooks/{id}
bash
curl -X PUT https://sendapi.co/v1/webhooks/4 \
-H "Authorization: Bearer sk_live_123456789" \
-H "Content-Type: application/json" \
-d '{"events": ["*"], "is_active": true}'Delete a Webhook
DELETE /v1/webhooks/{id}
bash
curl -X DELETE https://sendapi.co/v1/webhooks/4 \
-H "Authorization: Bearer sk_live_123456789"Test a Webhook
POST /v1/webhooks/{id}/test
Sends a synthetic event to your webhook URL so you can verify your endpoint is reachable and your signature verification works before relying on real events.
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
event | string | Yes | Event type to simulate (e.g., message.sent). |
bash
curl -X POST https://sendapi.co/v1/webhooks/4/test \
-H "Authorization: Bearer sk_live_123456789" \
-H "Content-Type: application/json" \
-d '{"event": "message.sent"}'View Delivery History
GET /v1/webhooks/{id}/deliveries
Returns the last N delivery attempts for a webhook, including response codes and errors. Useful for debugging when a webhook isn't firing as expected.
bash
curl https://sendapi.co/v1/webhooks/4/deliveries \
-H "Authorization: Bearer sk_live_123456789"Event Payload Format
All webhook POST requests use this envelope:
json
{
"event": "message.delivered",
"timestamp": "2026-03-09T14:35:00.000000Z",
"data": {
"id": 32,
"to": "447700900000",
"status": "delivered",
"delivered_at": "2026-03-09T14:35:00.000000Z"
}
}Signature Verification
Every webhook POST includes two headers:
| Header | Value |
|---|---|
X-SendAPI-Event | The event type (e.g., message.delivered). |
X-SendAPI-Signature | HMAC-SHA256 hex digest of the raw request body, signed with your webhook's secret. |
The signature value is the raw hex digest with no prefix — not sha256=....
Verifying in Node.js
javascript
import crypto from 'crypto';
function verify(rawBody, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature, 'hex'),
Buffer.from(expected, 'hex')
);
}Verifying in PHP
php
$expected = hash_hmac('sha256', $rawBody, $secret);
if (! hash_equals($expected, $request->header('X-SendAPI-Signature'))) {
return response('Invalid signature', 401);
}Event Types
Subscribe to any of the following — use ["*"] for all, or wildcards like ["message.*"], ["email.*"].
| Event | Fired when |
|---|---|
message.sent | WhatsApp/SMS dispatched to the carrier/WhatsApp servers |
message.delivered | Delivery confirmed |
message.read | Recipient read (WhatsApp only) |
message.failed | Permanent delivery failure |
email.sent | Email accepted by the upstream MTA |
email.delivered | Recipient mail server confirmed delivery |
email.opened | Tracking pixel loaded |
email.clicked | Tracked link clicked |
email.bounced | Hard bounce |
email.complained | Marked as spam |
email.bulk_completed | Bulk send job finished (total/sent/failed counts in payload) |
session.connected | WhatsApp session paired successfully |
session.disconnected | WhatsApp session lost connection |
Retry Behaviour
If your endpoint returns a non-2xx response or doesn't respond within 10 seconds, SendAPI retries with exponential backoff. After repeated failures, the webhook is marked unhealthy in your dashboard — fix the endpoint, then re-enable it via PUT /v1/webhooks/{id}.