Callflow Webhook and Pivot¶
Two callflow actions send an HTTP request from a live call to your own application:
| Webhook | Pivot | |
|---|---|---|
| What it is for | Notify an external system that a call reached this point (create a ticket, log the call, trigger an automation). | Let an external application decide what the call does next. |
| Does the call wait for your answer? | No. The request is sent in the background and the callflow moves on to the next action immediately. | Yes. The call waits for your response and then executes it. |
| Is your response used? | No. The status and body are ignored by the call. | Yes. Your response becomes the rest of the callflow. |
| Body format | application/x-www-form-urlencoded (POST) or query string (GET). |
Form (application/x-www-form-urlencoded) or JSON, your choice. |
Use Webhook when the call must continue no matter what your system does — for example, to open a helpdesk ticket for an after-hours call and then route the caller to a standby number. Use Pivot when your system has to choose where the call goes.
Custom HTTP headers are not supported
Neither action lets you add your own HTTP headers (an API key, an Authorization header or a
User-Agent). The request carries only the headers listed below. If your endpoint requires a
specific header, put a small proxy in front of it, or authenticate the request with a value
carried in the URL or in the body (see custom_data below).
Webhook action¶
Settings¶
| Field | Required | Description |
|---|---|---|
uri |
Yes | The URL the request is sent to. |
http_verb |
Yes | post or get. |
retries |
No | How many times the request is attempted when your server cannot be reached (connection error or timeout). Attempts are 2 seconds apart. A response with any HTTP status, including an error status, is not retried. |
custom_data |
No | A JSON object whose keys are added to the request body. Values may contain variables (see below). |
Headers sent¶
| Header | Value |
|---|---|
Content-Type |
application/x-www-form-urlencoded (POST only) |
X-Account-ID |
The ID of the account that owns the call. |
X-Hook-ID |
An identifier of this webhook execution. |
No User-Agent header is sent.
Request body¶
On post the call data is sent form-encoded in the body; on get the same data is sent in the query
string. It is not JSON. Keys are lower-case with underscores; nested objects use bracket notation,
for example custom_channel_vars[owner_id]=....
The call data includes, among others:
| Key | Meaning |
|---|---|
call_id |
Unique ID of the call. |
account_id |
Account that owns the call. |
caller_id_name, caller_id_number |
Who is calling. |
callee_id_name, callee_id_number |
Who is being called, when known. |
from, to, request |
SIP From, To and Request URIs of the call. |
call_direction |
inbound or outbound. |
custom_channel_vars[...] |
Channel variables set on the call. |
dtmf_collections[...] |
Digits collected earlier in the callflow, by collection name. |
The exact set of keys depends on the call; treat unknown keys as optional.
custom_data and variables¶
Every key in custom_data is added to the body next to the call data. A string value can reference
call data with ${key}, which is replaced by that key's value from the call data (an empty string if
the call has no such key). For example:
{
"module": "webhook",
"data": {
"uri": "https://helpdesk.example.com/api/tickets",
"http_verb": "post",
"retries": 2,
"custom_data": {
"source": "after-hours",
"caller": "${caller_id_number}",
"token": "your-shared-secret"
}
},
"children": {
"_": { "module": "device", "data": { "id": "<standby device id>" }, "children": {} }
}
}
custom_data is the place to pass a shared secret your endpoint can check, since a header cannot be
added.
Response¶
Return 200. The body is ignored, and nothing in it can change the call: by the time your server
answers, the call has already moved on to the next action. Only a connection error or timeout is
retried (up to retries); a non-200 status is recorded as a failure and not retried.
Pivot action¶
Settings¶
| Field | Required | Description |
|---|---|---|
voice_url |
Yes | The URL of your application. |
method |
No | get (default) or post. |
req_body_format |
No | For post: form (default) or json. |
req_format |
No | The format of your response: kazoo (callflow JSON) or twiml. |
req_timeout |
No | Milliseconds to wait for your response. Default 5000. |
cdr_url |
No | A URL that receives the call record (form-encoded POST) when the call ends. |
debug |
No | true records each request and response for troubleshooting. |
Headers sent¶
Only Content-Type (application/x-www-form-urlencoded or application/json, according to
req_body_format). No account header, no User-Agent and no custom headers.
Request¶
| Key | Meaning |
|---|---|
Call-ID |
Unique ID of the call. |
Account-ID |
Account that owns the call. |
Caller-ID-Name, Caller-ID-Number |
Who is calling. |
From, From-Realm, To, To-Realm, Request, Request-Realm |
SIP addressing of the call. |
Call-Status |
Current state of the call. |
Direction |
Always inbound. |
User-ID |
Owner of the device that placed the call, when there is one. |
Language |
Call language. |
Digits |
Digits collected by a previous Pivot response. |
Recording-Url, Recording-Duration, Recording-ID |
Set after a recording made by a previous Pivot response. |
Custom-Channel-Vars, Custom-Application-Vars, Custom-SIP-Headers |
Variables and SIP headers carried by the call. |
With get, every key is sent in the query string; large variable sets can exceed URL length limits,
so prefer post when the call carries many variables.
Response (req_format: kazoo)¶
Answer 200 with Content-Type: application/json and a callflow object: module, data and
children, exactly as a node of a callflow is written. It is validated as a callflow and then
executed in place of the rest of the call. For example, to send the call to an existing callflow:
{ "module": "callflow", "data": { "id": "<callflow id>" }, "children": {} }
To play a prompt and hang up:
{
"module": "play",
"data": { "id": "<media id>" },
"children": { "_": { "module": "hangup", "data": {}, "children": {} } }
}
Any callflow action can be returned (play, tts, menu, callflow, device, user, ring_group,
voicemail, hangup, …), with the same data fields it takes in a callflow. If the request to your
application cannot be sent, the call continues with the Pivot action's own children in the callflow.