Webhooks
Subscribe to platform events and SIP.IO POSTs them to your HTTPS endpoint as they happen, signed so you can verify they came from SIP.IO, with automatic retry on transient failures and a full delivery log you can query.
Subscribe
Section titled “Subscribe”Manage subscriptions via the /v1/webhooks endpoint (scope webhooks):
curl -X POST https://api.sip.io/v1/webhooks \ -H 'x-api-key: sk_…' -H 'content-type: application/json' \ -d '{ "url": "https://acme.com/hooks/sipio", "events": "call.started,call.ended", "secret": "optional" }'| Field | Purpose |
|---|---|
url | Your endpoint; must be https://. |
events | Comma-separated event names, or * for all (default). |
secret | HMAC signing secret. If omitted, one is generated and returned once at creation. |
GET /v1/webhooks lists your subscriptions (without secrets); DELETE /v1/webhooks with { id } removes one. POST /v1/webhooks/test fires a test event so you can verify your endpoint. GET /v1/webhooks/events returns the full event catalog with a sample payload for each.
Events
Section titled “Events”13 events across the platform:
| Event | Fires when |
|---|---|
call.started / call.answered / call.ended | A call is routed, answered, or ends (call.ended carries direction, answered, billsec, hangup_cause, queue_id, agent_id). |
agent.state | An agent’s state changes (login, pause, ready, and so on). |
message.received / message.status | An inbound message arrives, or a delivery status updates. |
recording.available | A call recording finishes processing and is ready to play. |
fax.received / fax.sent / fax.failed | An inbound fax arrives, or an outbound fax completes or fails. |
campaign.started / campaign.completed / campaign.contact | An outbound campaign starts, finishes, or a contact reaches a terminal disposition. |
test | You call /v1/webhooks/test. |
Payload
Section titled “Payload”Every delivery is a JSON POST in this shape:
{ "event": "call.ended", "account_id": "acc_acme", "data": { "callId": "…", "direction": "inbound", "answered": 1, "billsec": 184, "hangup_cause": "NORMAL_CLEARING", "queue_id": "q_support", "agent_id": "us_dana", "ts": 1719600000000 }, "ts": 1719600000000}Each request also carries x-sipio-event: <event> and content-type: application/json.
Verifying the signature
Section titled “Verifying the signature”If the subscription has a secret, SIP.IO signs the raw request body with HMAC-SHA256 and sends it in the x-sipio-signature header as sha256=<hex>. Recompute it and compare:
import { createHmac, timingSafeEqual } from 'node:crypto';
function verify(rawBody, header, secret) { const expected = 'sha256=' + createHmac('sha256', secret).update(rawBody).digest('hex'); const a = Buffer.from(header || ''), b = Buffer.from(expected); return a.length === b.length && timingSafeEqual(a, b);}Delivery
Section titled “Delivery”- Each event is POSTed to every matching active subscription, with a 5-second timeout per attempt.
- A failed delivery retries automatically, up to 3 attempts with backoff (immediate, then 0.5s, then 2s), for network failures,
429s, and5xxresponses. A4xx(a bad URL, an auth failure on your side) fails fast instead, since retrying won’t fix it. Make your handler idempotent regardless, and return2xxquickly. - Endpoints must be
https://.
Delivery log
Section titled “Delivery log”GET /v1/webhooks/deliveries returns the outcome of every attempt-set: status, number of attempts, and the last error if it failed. Filter with ?webhook_id=, ?event=, ?failed=1, and ?limit=, so you can see exactly what was delivered, when, and why anything didn’t go through.