API reference

Webhooks & callbacks

Connect calls your endpoints over HTTPS. You set each URL in one of three places: on the number in the dashboard, in the body of POST /api/calls, or as an attribute in your markup. There are no subscriptions to create and no webhooks API. This page lists every request Connect sends. For a walkthrough of how they fit together, read Webhooks and callbacks.

Connect is live. The API is v0.x, so field names may change before v1.

Every request Connect sends

Voice webhooks, action URLs and wait URLs expect markup back (XML, or the smaller JSON form; see Call-control markup). Callbacks only need a 2xx; Connect ignores their response body.

RequestWhere it is setBodyX-CPaaS-Event
Inbound voice webhookWebhook URL on the number, in the dashboard{from, to, direction: "inbound"}. Reply with markup.webhook.inbound
Outbound voice webhookurl on POST /api/calls{call_sid, to, from, direction: "outbound"}, sent once the callee answers. Reply with markup.webhook.outbound
Gather actionaction on <Gather>{"digit": "1"}, or {"digit": ""} on timeout. Always a POST. Reply with markup.gather.action
Dial actionaction on <Dial>DialCallStatus, DialCallDuration, plus RecordingUrl and the Recording fields when the leg was recorded, and ConferenceSid for a conference. Reply with markup.dial.action, dial.queue.action, dial.conference.action
Record actionaction on <Record>RecordingName, RecordingUrl, RecordingDuration and the Recording fields. Reply with markup.record.action
Wait URLwaitUrl on <Enqueue> or <Conference>An empty JSON object. Reply with Say, Play or Wait; Connect loops them while the caller waits.wait.url
Status callbackstatusCallback on POST /api/calls, or the Hangup URL on the numberJSON with Twilio-style names (CallSid, CallStatus...). See Calls for every field.call.status, call.hangup, status.callback.initiated
Recording status callbackrecordingStatusCallback on <Dial>, <Record> or <Conference>, or the recording callback URL on the numberRecordingStatus, RecordingName, RecordingUrl... See Recordings for every field.recording.status.<status>
Conference status callbackstatusCallback on <Conference>StatusCallbackEvent, ConferenceSid, CallSid... See Conferences for every field.conference.<event>

Bodies are JSON on a POST. When a method attribute is set to GET (Dial and Record action URLs, wait URLs, conference and recording callbacks), the same fields go in the query string instead.

Headers

Every request carries the same identifying headers, so you can route or log by header before you parse the body. X-CPaaS-Call-Sid is set whenever the request belongs to one call. Wait URL fetches and conference start and end events do not have it.

Status callback → your endpoint
POST /voice/status HTTP/1.1
Host: your.app
Content-Type: application/json
User-Agent: cPaaS-Platform/1.0
X-CPaaS-Event: call.status
X-CPaaS-Call-Sid: CA7Q2M9X4K1B8N3T6R0W5Y2Z9C1V4H7JL5
X-CPaaS-EventSent for
webhook.inboundThe inbound voice webhook on a number.
webhook.outboundThe url you passed to POST /api/calls, after the callee answers.
gather.actionA Gather action URL.
dial.actionA Dial action URL after a number or SIP leg.
dial.queue.actionA Dial action URL after <Dial><Queue>.
dial.conference.actionA Dial action URL after <Dial><Conference>.
record.actionA Record action URL.
wait.urlA waitUrl on Enqueue or Conference.
dial.screeningThe url on a <Number> inside Dial, fetched before that leg is connected.
status.callback.initiatedThe initiated status callback, sent only when you subscribe to it.
call.statusA status callback while the call is live (ringing, in-progress).
call.hangupThe final status callback for a call.
status.callback.<event>A statusCallback set on a <Number> inside Dial.
recording.status.<status>A recording status callback: in-progress, completed, absent or failed.
conference.<event>A conference status callback: start, end, join or leave.

Delivery rules

  • 5 second timeout. This applies to the inbound webhook, action URLs, wait URLs and every callback. The outbound webhook has no fixed limit, but the person who answered hears silence until you reply.
  • Sent once, no retries. If your endpoint is down, slow or returns an error, that request is lost. Use GET /api/calls/{callSid} to catch up on a call.
  • Not signed. There is no signature header. See below for how to protect your endpoint.
  • If the inbound webhook fails, the caller hears a short message and the call ends. If an action URL fails, the call has nothing left to run and ends.
  • Ordering. Status callbacks carry SequenceNumber and CallbackId (CallSid plus SequenceNumber). IsFinal is true on the last one.

Securing your endpoint

Because requests are not signed, anyone who learns one of your URLs can post to it. Three checks keep that safe:

  1. Put a long random token in each URL you give Connect (in the path or the query string), serve it over HTTPS, and reject any request without it.
  2. Check X-CPaaS-Call-Sid (or CallSid in the body) against the calls you placed or received.
  3. Before you act on something that matters, such as billing or fulfilling an order, confirm the call with GET /api/calls/{callSid} using your API key.
Status callback handler
// Express. The token is part of the URL you gave Connect, for example
// statusCallback: "https://your.app/voice/status/" + process.env.CALLBACK_TOKEN
app.post("/voice/status/:token", express.json(), async (req, res) => {
  if (req.params.token !== process.env.CALLBACK_TOKEN) return res.sendStatus(404);

  const callSid = req.get("X-CPaaS-Call-Sid");
  if (!callSid || !(await isOurCall(callSid))) return res.sendStatus(404);

  res.sendStatus(204);   // answer fast: there are no retries
  jobs.push(req.body);   // do the slow work after replying
});