Skip to content

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.