Skip to content

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

ParameterTypeRequiredDescription
urlstringYesYour HTTPS endpoint that will receive webhook POSTs.
eventsarrayYesEvent 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

ParameterTypeRequiredDescription
eventstringYesEvent 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:

HeaderValue
X-SendAPI-EventThe event type (e.g., message.delivered).
X-SendAPI-SignatureHMAC-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.*"].

EventFired when
message.sentWhatsApp/SMS dispatched to the carrier/WhatsApp servers
message.deliveredDelivery confirmed
message.readRecipient read (WhatsApp only)
message.failedPermanent delivery failure
email.sentEmail accepted by the upstream MTA
email.deliveredRecipient mail server confirmed delivery
email.openedTracking pixel loaded
email.clickedTracked link clicked
email.bouncedHard bounce
email.complainedMarked as spam
email.bulk_completedBulk send job finished (total/sent/failed counts in payload)
session.connectedWhatsApp session paired successfully
session.disconnectedWhatsApp 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}.

Released under the MIT License.