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:

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, you can 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.

  1. Set the content-type to application/json.

    <?php
    
    header('content-type:application/json');
    
  2. 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();
      }
    
  3. 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 is URL/call_recording_CALL_ID.EXT where url is the supplied URL, CALL_ID is the call ID of the A-leg being recorded, and EXT is the format parameter.

NOTE: If url is not provided, IP Telecoms Hosted PBX will check the system_config/media doc for the store_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.

  1. Assume url in our data object is http://your.server.com/kzr.
  2. Create a .htaccess file in the DocumentRoot. This directs the request to /kzr/index.php with the recording query string parameter set to CALL_ID.EXT.

    <IfModule mod_rewrite.c>
        RewriteEngine On
        RewriteBase /
        RewriteRule ^kzr/call_recording_(.+)\.(.+)$  kzr/index.php?recording=$1.$2 [QSA,L]
    </IfModule>
    
  3. 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.