Advanced Callflow API
If the callflow builder doesn't fully match your needs, integrate with your applications, or provide a specific experience for the caller, you can set up advanced callflows. Use the callflow API to manage advanced callflow operations.
When a call is routed to advanced callflow, the API makes a HTTP request to your web server asking for the specific callflow to process for that call (versus the one-size-fits-all approach in the normal callflow builder). It returns a JSON response as appropriate and executes the callflow.
API calls
Following are some of advanced callflow API operations. Clients can query and inspect data related to our real-time call control application:
- Bridging
- Dialing outside the account
- Collecting DTMF
- Processing collected DTMF
- Sending DTMF
- Conferencing
- Conference per area code
- Hangups
- Presence
- Recording
- Start recording
- Stop recording
- Recording just the caller
- Receiving a recording
- Text to speech
- Play files
General example
To understand when to use advanced callflows, we can use the example of a time-based callflow. Sometimes, you might want to route incoming calls differently depending on what time they are received. For the main office number, add rules based on the time of the call. Following a traditional office's hours, we are open 9am-5pm (9:00 to 17:00). During that time, we want the front desk phone to ring. Outside of those hours, we want calls to go directly to the company's voicemail box.
Based on the above goals, we need to set up:
- The front desk device
- The company voicemail box
- The main number callflow
After you set up device and voicemail box, the main number callflow should be:
[NUMBER] -> Advanced callflow
Url: http://your.webserver.com/path/to/main_number_tod.php
Now, whenever this number is called, IP Telecoms Hosted PBX queries the URL to determine which callflow to execute.
To build the main_number_tod.php
.
-
Set the
content-type
toapplication/json
.<?php header('content-type:application/json');
-
Define the current time and determine what to do:
$now = time(); $hour = date("G", $now); if ( $hour >= 9 && $hour < 17 ) { business_hours(); } else { after_hours(); }
-
Use the following two functions to build the JSON for the callflow to execute:
business_hours(): php function business_hours() { ?> {"module":"device" ,"data":{"id":"{FRONT_DESK_DEVICE_ID}"} ,"children":{ "_":{ "module":"voicemail" ,"data":{"id":"{COMPANY_VM_BOX_ID}"} } } <?php } after_hours(): php function after_hours() { ?> {"module":"voicemail" ,"data":{"id":"{COMPANY_VM_BOX_ID}"} } <?php }
Request
<?php
header('content-type:application/json');
$now = time();
$hour = date("G", $now);
if ( $hour > 8 && $hour < 17 ) {
business_hours();
} else {
after_hours();
}
function business_hours() {
?>
{"module":"device"
,"data":{"id":"{FRONT_DESK_DEVICE_ID}"}
,"children":{
"_":{
"module":"voicemail"
,"data":{"id":"{COMPANY_VM_BOX_ID}"}
}
}
<?php
}
function after_hours() {
?>
{"module":"voicemail"
,"data":{"id":"{COMPANY_VM_BOX_ID}"}
}
<?php
}
?>
You can configure whether you receive the data as a query string in a GET
or as a URL-encoded body in a POST
.
Parameters
The table shows the parameters available for advanced callflow requests. Some fields are not included in every request.
Key | Description | Type |
---|---|---|
Call-ID |
The unique call identifier. | string |
Account-ID |
The account ID receiving the call. | string |
From |
The SIP "from" username. | string |
From-Realm |
The SIP "from" realm. | string |
To |
The SIP "to" username. | string |
To-Realm |
The SIP "to" realm. | string |
Request |
The SIP "request" username. | string |
Request-Realm |
The SIP "request" realm. | string |
Call-Status |
The status of the call. | string |
Api-Version |
The version of the API. | string |
Direction |
The direction of the call, relative to the API. | string |
Caller-ID-Name |
Caller ID name. | string |
Caller-ID-Number |
Caller ID number. | integer |
User-ID |
The user ID of the caller. | string |
Language |
The caller's language preference. | string |
Recording-Url |
Specifies where the recording will be stored. | string |
Recording-Duration |
Indicates how long the recording is. | integer |
Recording-ID |
The recording ID. | string |
Digits |
Indicates the DTMF (or collection of DTMFs) pressed. | integer |
Bridging
IP Telecoms Hosted PBX API offers several ways to connect, call, or "bridge" to various endpoints. Below are some simple examples. Most of these examples can take more options, are nestable as children of other callflow actions, and can help you accomplish various tasks.
Bridge devices
Dial a single IP Telecoms Hosted PBX device.
{
"module":"device",
"data":{"id":"device_id"}
}
Bridge users
Dial any devices owned by a specific user.
{
"module": "user",
"data": {
"id": "user_id"
}
}
Bridge a ring group
Ring groups are flexible and allow several combinations of endpoints: devices, users, or groups. When you included the IDs you want to ring, IP Telecoms Hosted PBX builds the appropriate list of endpoints.
{
"module":"ring_group",
"data":{
"endpoints": ["device_1_id",
"device_2_id",
"user_1_id",
"user_2_id",
"group_1_id",
"group_2_id"
]
}
}
NOTE: You can mix and match devices, users, and groups based on the needs of this particular call.
Dialing outside the account
IP Telecoms Hosted PBX supports two types of resources, global and per-account (or local). You can route to either resource, depending on how you've configured your account and whether you use the IP Telecom Hosted PBX cluster's global resources.
The use_local_resources
flag is the key differentiator between global and local.
Using global resources
{
"module": "resources",
"data": {
"to_did": "+35316877779",
"use_local_resources": false
}
}
Using local resources
{
"module": "resources",
"data": {
"to_did": "+135316877779",
"use_local_resources": true
}
}
Collecting DTMF
You can collect DTMFs through JSON by using the cf_collect_dtmf
callflow action. After you collect the DTMFs, you can store them in the custom-named keys to distinguish between collections.
Request
<?php
header('content-type:application/json');
?>
Response
{
"module":"tts",
"data":{
"text": "Please enter up to four digits."
},
"children": {
"_": {
"module": "collect_dtmf",
"data": {
"max_digits": 4,
"collection_name": "custom_name"
},
"children": {
"_": {
"module": "pivot",
"data": {
"voice_url": "http://pivot.your.company.com/collected.php"
},
"children": {}
}
}
}
}
}
First, use the text-to-speech (TTS) engine to say the text field. Next, the operation waits for you to press up to four DTMF (# is a terminating DTMF that is not included in the collection). Finally, the system submits a second web server request using the collected.php
script on your server.
Processing collected DTMF
After the caller presses the DTMF, the following demonstrates the speech back to the customer. You can then do any necessary further call processing from the DTMF.
Request
<?php
header('content-type:application/json');
$dtmf = $_REQUEST['Digits'];
if ( empty($dtmf) ) {
?>
Response
{
"module": "tts",
"data": {
"text": "We didn't get that"
},
"children": {}
}
<?php } else if ( is_string($dtmf) ) { ?>
{
"module": "tts",
"data": {
"text": "You typed <?= $dtmf ?>"
},
"children": {}
}
<?php } else { ?>
{
"module": "tts",
"data": {
"text": "You typed <?= $dtmf['custom_name'] ?>"
},
"children": {}
}
<?php } ?>
NOTE: The
is_string($dtmf)
check supports the legacy method of returning DTMF in the request. Otherwise, you should receive an array of DTMF collections, indexed by the key name supplied. This will return "default" if you didn't specify a key name.
Sending DTMF
Sending DTMF to the caller is sometimes necessary (for example, to automate IVR navigation). Use the send_dtmf
callflow to do so.
Response
{
"module": "send_dtmf",
"data": {
"digits": "123ABC#",
"duration_ms": 2000
},
"children": {}
}
Where:
digits
is a string of DTMF to send.
duration_ms
is what duration tone to send each DTMF (optional).
The above example would send "1", "2", "3", "A", "B", "C", and finally "#", each as two-second tones.
Conferencing
There are two main ways to add a caller to a conference:
- Use pre-built IP Telecoms Hosted PBX conference rooms that are configured using the API.
- Create ad-hoc conference rooms through advanced callflows.
Pre-built example
{
"module": "conference",
"data": {
"id": "conference_id"
}
}
Ad-hoc example
{
"module": "conference",
"data": {
"config": {
"name": "My Ad-hoc Conference"
}
}
}
This method creates a minimalist conference bridge, named "My Ad-hoc Conference". Use the same name to ensure callers end up in the same conference together. The configuration object is validated against the conference JSON schema.
Conference per area code
This request sets up a conference based on area code.
Request
<?php
header('content-type: application/json');
$caller_id = $_REQUEST['Caller-ID-Number'];
$areacode = ($caller_id, 0, 3);
?>
Response
{
"module": "tts",
"data": {
"text": "Welcome to the <?= $areacode ?> conference!"
},
"children": {
"_": {
"module": "conference",
"data": {
"config": {
"name": "Areacode <?= $areacode ?>"
}
},
"children": {}
}
}
}
NOTE: This operation does not handle hidden or missing caller ID.
Hangups
Sometimes it is necessary to respond to a call with a hangup cause and code. Some phones display the hangup cause on the display.
Request
{
"module": "response",
"data": {
"code": "486",
"message": "User Busy",
"media":"id_or_url"
}
}
NOTE: If you define media, the media is played to the user before hanging up the call.
Presence
The callflow API allows you to set custom presence updates (known as manual presence).
Response
{
"module": "manual_presence",
"data": {
"presence_id": "username",
"status": "ringing"
}
}
The presence_id
without an @domain.com is appended with the account's domain. The status can be either idle
, ringing
, or busy
.
Recording
Recording the caller (or caller and callee in a bridged-call scenario) is straightforward to start. Storing the recording can be more complicated.
Start recording
Starts the call recording, limiting it to 1200 seconds, and encodes the audio into a .mp3
file (alternatively, you can use .wav
). The resulting file is sent to the URL through a HTTP PUT request. It is then up to the receiving server to properly handle the request and store the file for later use.
Response
{
"module": "record_call",
"data": {
"action": "start",
"time_limit": 1200,
"format": "mp3",
"url": "http://your.server.com/recordings"
}
}
Where:
- The time_limit
is constrained by the system_config/media doc's max_recording_time_limit entry
(default is 600 seconds). If your recordings are not long enough, you must increase the system_config setting.
url
is used as the base URL for the resulting PUT. The final URL isURL/call_recording_CALL_ID.EXT
whereurl
is the supplied URL,CALL_ID
is the call ID of the A-leg being recorded, andEXT
is the format parameter.
NOTE: If
url
is not provided, IP Telecoms Hosted PBX will check thesystem_config/media
doc for thestore_recordings
flag. If "false", the recording is not stored. If "true", IP Telecoms Hosted PBX will store the recording into the account's database.
Stop recording
Use this request to programmatically stop the current recording (versus stopping when the call ends):
Response
{
"module": "record_call",
"data": {
"action": "stop"
}
}
Sample recording per user
{
"module": "record_call",
"data": {
"action": "start",
"format": "mp3",
"url": "http://my.recording.server/{ACCOUNT_ID}/{USER_ID}",
"time_limit":360
},
"children": {
"_": {
"module": "user",
"data": {
"id": "{USER_ID}"
},
"children": {
"_": {
"module": "record_call",
"data": {
"action": "stop"
},
"children": {
"module": "voicemail",
"data": {
"id": "{VMBOX_ID}"
},
"children": {}
}
}
}
}
}
}
IMPORTANT: Call recording and voicemail do not work well together. You must stop the recording before voicemail to avoid conflict.
Recording just the caller
This action is more appropriate for recording just the caller (think voicemail or recording menu prompts).
Response
{
"module": "say",
"data": {
"text": "Please leave your message after the beep"
},
"children": {
"_": {
"module": "record_caller",
"data": {
"format": "mp3",
"url": "http://my.recording.server/voicemail/{ACCOUNT_ID}/{BOX_ID}",
"time_limit":360
}
}
}
}
Receiving a recording
This request is a simple PHP/.htaccess
combination for receiving a recording.
- Assume
url
in our data object is http://your.server.com/kzr. -
Create a
.htaccess
file in the DocumentRoot. This directs the request to/kzr/index.php
with the recording query string parameter set toCALL_ID.EXT
.<IfModule mod_rewrite.c> RewriteEngine On RewriteBase / RewriteRule ^kzr/call_recording_(.+)\.(.+)$ kzr/index.php?recording=$1.$2 [QSA,L] </IfModule>
-
Create
/kzr/index.php
to receive and store the recording.<?php /* PUT data comes in on the stdin stream */ $putdata = fopen("php://input", "r"); $r = $_REQUEST["recording"]; /* Open a file for writing */ $fp = fopen("/tmp/$r", "w"); /* Read the data 1 KB at a time and write to the file */ while ($data = fread($putdata, 1024)) fwrite($fp, $data); /* Close the streams */ fclose($fp); fclose($putdata); ?>
A file is automatically created in /tmp/ named CALL_ID.EXT
. You could then store this in MySQL, Postgres, S3, feed it to a transcription service, and so on.
Text to speech
Text-to-speech is an easy way to read text to the caller.
Request
<?php
header('content-type:application/json');
?>
Response
{
"module": "tts",
"data": {
"text": "Pivot is pretty awesome. Have a great day."
},
"children": {
"_": {
"module": "response",
"data": {}
}
}
}
The TTS engine will read the text to the caller and then hang up.
Play files
This request enables you play files, either hosted in your account or accessible through a URI.
Request
<?php
header('content-type:application/json');
?>
Response
{
"module": "play",
"data": {
"id": "media_id"
},
"children": {
"_": {
"module": "play",
"data":{
"id": "http://some.file.server/some/file.mp3"
},
"children": {}
}
}
}
There are two play actions, one that uses a media file hosted by the PBX and one that fetches the file from an HTTP server.