Webhooks
Webhooks
Section titled “Webhooks”Webhooks push real-time HTTP POST notifications to your server when events happen on your SIPSTACK account — message delivery updates, live call lifecycle (ringing / answered / hangup, for CRM screen-pop), Aura AI voice-agent call outcomes, campaign completion, and custom events fired by your AI voice-agent flows. Instead of polling the API, your endpoint is called the moment the event occurs.
How Webhooks Work
Section titled “How Webhooks Work”- You register an endpoint URL and subscribe it to event types.
- When a subscribed event occurs, SIPSTACK POSTs the event JSON to your URL.
- Your server responds with a 2xx status within 10 seconds to acknowledge.
- Failed deliveries are retried with backoff; endpoints that keep failing are disabled automatically.
Configuring Webhooks
Section titled “Configuring Webhooks”Manage webhook endpoints in Switchboard at Account → Integrations → Webhooks — register an endpoint, choose the events it receives, view the delivery log, and send a test event, all from the UI. Webhooks are a plan-gated feature.
You can also manage them through the API, authenticated with an owner/admin portal session (see Authentication):
| Action | Endpoint |
|---|---|
| List endpoints | GET /api/webhooks |
| Create an endpoint | POST /api/webhooks with { "url": "https://...", "events": ["message.delivered"] } |
| Delete an endpoint | DELETE /api/webhooks/{id} |
| View delivery log | GET /api/webhooks/{id}/deliveries |
| Send a test event | POST /api/webhooks/{id}/test |
- The
eventsarray selects which events the endpoint receives;["*"]subscribes to everything (including event types added later). Discover the valid names programmatically withGET /api/v2/events. - The create response includes the endpoint’s signing secret — shown only once. Store it securely; you need it to verify deliveries.
You can also manage endpoints with an API key (x-api-key) instead of a portal session, using the /api/v2/webhooks alias — handy for server-to-server setup:
| Action | Endpoint |
|---|---|
| List endpoints | GET /api/v2/webhooks |
| Create an endpoint | POST /api/v2/webhooks with { "url": "https://...", "events": ["message.delivered"] } |
| Delete an endpoint | DELETE /api/v2/webhooks/{id} |
The API-key create response returns the signing secret once (same as the portal endpoint) and echoes the signature header/format so you know exactly what to verify. See the API Reference for full shapes.
Payload Structure
Section titled “Payload Structure”The body is the event name plus the event’s fields at the top level:
{ "event": "message.delivered", "messageId": 12345, "status": "delivered", "to": "+14165550100", "from": "+16135550200", "campaignId": 42}Request headers on every delivery:
| Header | Value |
|---|---|
Content-Type | application/json |
X-Webhook-Event | The event name |
X-Webhook-Signature | sha256=<HMAC-SHA256 hex of the raw body> |
User-Agent | SIPSTACK-Webhook/1.0 |
Events Reference
Section titled “Events Reference”The platform exposes thirteen subscribable event types today (plus the synthetic test ping). Subscribe an endpoint only to events on this list (or ["*"] for all of them); an unknown event name is rejected at create time with a 400 that lists the valid names. You can always fetch the live catalog from GET /api/v2/events.
Messaging events
Section titled “Messaging events”Fired as the carrier reports status changes on outbound SMS, or when an inbound SMS arrives:
| Event | When | Payload fields |
|---|---|---|
message.sent | Message accepted by the carrier | messageId, status, to, from, campaignId |
message.delivered | Carrier confirmed delivery | messageId, status, to, from, campaignId |
message.failed | Delivery failed or was undeliverable | messageId, status, to, from, campaignId |
message.received | An inbound SMS arrived on one of your numbers | messageId, from, to, body |
campaignId is null for conversational (non-campaign) messages.
Call events (finalized CDR)
Section titled “Call events (finalized CDR)”Fired when a call’s CDR is finalized:
| Event | When | Payload fields |
|---|---|---|
call.completed | A call was answered and ended | callId, from, to, duration, billsec, disposition, direction |
call.missed | A call ended without being answered | callId, from, to, duration, disposition, direction |
Real-time call events (Nova)
Section titled “Real-time call events (Nova)”Fired live — milliseconds after the PBX sees them, before the CDR is finalized — so a CRM can screen-pop on an incoming call. Every payload carries callId, from, to, and direction (inbound / outbound / internal), plus extension and channel when known.
| Event | When | Extra payload fields |
|---|---|---|
call.inbound | A call is arriving on one of your numbers (before it’s answered) | — |
call.ringing | A call started ringing | — |
call.answered | A call was answered | — |
call.hangup | A call ended — the live terminal signal, fired for every direction | answered, duration, billsec, disposition |
An inbound call.hangup is followed by the matching finalized call.completed or call.missed above — de-duplicate terminal handling on callId if you subscribe to both the real-time and finalized sets.
Aura AI voice-agent call events
Section titled “Aura AI voice-agent call events”Fired by Aura agent calls (distinct from the flow-defined webhook steps below):
| Event | When | Payload fields |
|---|---|---|
aura.call.started | The agent answered and began handling the call | callId, agentId, agentName, from, to, direction, startedAt |
aura.call.completed | The agent call ended | callId, agentId, agentName, from, to, direction, billsec, outcome, transferTarget, summary, sentiment, extractedFields |
Campaign events
Section titled “Campaign events”| Event | When | Payload fields |
|---|---|---|
campaign.completed | A campaign finishes sending | campaignId, name, messagesSent |
Voice-agent flow events
Section titled “Voice-agent flow events”AI voice-agent flows can include webhook steps. When a call reaches that step, the event name and payload you defined in the flow are dispatched to your subscribed endpoints. Test calls from the agent builder include "test": true in the payload so you can filter them.
Test event
Section titled “Test event”POST /api/webhooks/{id}/test delivers a test event ({ "event": "test", "message": "This is a test webhook delivery" }) to a single endpoint on demand, so you can verify connectivity and signature handling.
Verifying Signatures
Section titled “Verifying Signatures”Always verify X-Webhook-Signature before trusting a payload — it proves the request came from SIPSTACK and wasn’t tampered with.
const crypto = require('crypto');
function verifyWebhookSignature(rawBody, signature, secret) { const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(rawBody, 'utf8').digest('hex'); return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));}
// Express handler — note express.raw(): verify against the RAW body,// not a re-serialized parse of it.app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => { const signature = req.headers['x-webhook-signature']; if (!signature || !verifyWebhookSignature(req.body, signature, process.env.SIPSTACK_WEBHOOK_SECRET)) { return res.status(401).send('Invalid signature'); }
const event = JSON.parse(req.body); // hand off to a queue and respond fast res.status(200).send('OK');});Delivery, Retries & Auto-Disable
Section titled “Delivery, Retries & Auto-Disable”- Your endpoint must respond 2xx within 10 seconds — respond immediately and process asynchronously. A non-2xx response, a timeout, or a connection error all count as a failed attempt.
- A failed delivery is retried up to 2 more times: attempt 2 about 1 minute after the failure, attempt 3 about 5 minutes after that — at most 3 attempts per event.
- Every attempt is logged — event type, HTTP status, response time, attempt number — and queryable via
GET /api/webhooks/{id}/deliveries. - If you delete or disable an endpoint while retries are pending, those retries are skipped — nothing is delivered to a removed endpoint.
- When an event exhausts all 3 attempts, the endpoint’s consecutive-failure count increments; after 10 consecutive fully-failed events the endpoint is automatically disabled to protect the queue. Any successful delivery resets the count to zero. Recreate the endpoint (or contact support) once your server is healthy, and use the delivery log to backfill anything you missed.
Idempotency
Section titled “Idempotency”Retries mean your endpoint can receive the same event more than once. De-duplicate on a natural key (messageId + event, or your flow’s own correlation ID) rather than assuming exactly-once delivery.