---
notionId: "3045cac2430f48c69b04b27ce29c83d5"
product: "developer"
cluster: "apis"
intent: "developer.api-pivot"
docSlug: "api-pivot"
task: "Pivot in the API"
title: "Pivot in the API | VirtualPBX"
meta_description: "Pivot is the API that lets a developer control inbound call handling. Dash sends the call details to the developer's web server over HTTP. The server answers"
answer: "Pivot is the API that lets a developer control inbound call handling. Dash sends the call details to the developer's web server over HTTP. The server answers with JSON that describes the callflow, and Pivot runs that callflow for the caller. Use it to send a specific call to a destination your application chooses. The examples on this page show a callflow response and how to debug a Pivot request."
audience: "user"
updated: "2022-12-06"
legacy: ["pivot"]
headings: [{"depth":2,"text":"About Pivot","id":"about-pivot"},{"depth":3,"text":"Use Cases","id":"use-cases"},{"depth":3,"text":"Example Callflow","id":"example-callflow"},{"depth":3,"text":"Debugging","id":"debugging"},{"depth":3,"text":"Summary","id":"summary"},{"depth":3,"text":"Specific Call","id":"specific-call"},{"depth":3,"text":"Failback","id":"failback"},{"depth":2,"text":"The Request Payload","id":"the-request-payload"},{"depth":3,"text":"Payload","id":"payload"},{"depth":2,"text":"JSON Overview","id":"json-overview"},{"depth":3,"text":"Building Time-based routing","id":"building-time-based-routing"},{"depth":3,"text":"Example of Building on Pivot","id":"example-of-building-on-pivot"},{"depth":3,"text":"Build main_number_tod.php","id":"build-main_number_todphp"},{"depth":2,"text":"TwiML Format","id":"twiml-format"},{"depth":3,"text":"Core Supported","id":"core-supported"},{"depth":3,"text":"Default Request Data included","id":"default-request-data-included"},{"depth":3,"text":"Optional/Conditional Request Data","id":"optionalconditional-request-data"},{"depth":3,"text":"TwiML Verbs","id":"twiml-verbs"},{"depth":3,"text":"Custom Verbs","id":"custom-verbs"},{"depth":3,"text":"Core TwiML Nouns","id":"core-twiml-nouns"},{"depth":3,"text":"Custom Nouns","id":"custom-nouns"},{"depth":2,"text":"Bridging","id":"bridging"},{"depth":3,"text":"Devices","id":"devices"},{"depth":3,"text":"Users","id":"users"},{"depth":3,"text":"Ring Group","id":"ring-group"},{"depth":3,"text":"Dialing outside the account","id":"dialing-outside-the-account"},{"depth":3,"text":"Using Global resources","id":"using-global-resources"},{"depth":3,"text":"Using Local resources","id":"using-local-resources"},{"depth":2,"text":"Collect","id":"collect"},{"depth":2,"text":"Collecting DTMF","id":"collecting-dtmf"},{"depth":3,"text":"Processing collected DTMF","id":"processing-collected-dtmf"},{"depth":2,"text":"Conferencing","id":"conferencing"},{"depth":3,"text":"Pre-built example","id":"pre-built-example"},{"depth":3,"text":"Ad-Hoc example","id":"ad-hoc-example"},{"depth":3,"text":"Conference Per Area Code","id":"conference-per-area-code"},{"depth":2,"text":"DTMF","id":"dtmf"},{"depth":3,"text":"Example","id":"example"},{"depth":2,"text":"Hang Ups","id":"hang-ups"},{"depth":3,"text":"Example","id":"example-1"},{"depth":2,"text":"Play","id":"play"},{"depth":3,"text":"Play sample","id":"play-sample"},{"depth":2,"text":"Presence","id":"presence"},{"depth":3,"text":"Example","id":"example-2"},{"depth":2,"text":"Recording","id":"recording"},{"depth":3,"text":"Recording Caller/Callee situations","id":"recording-callercallee-situations"},{"depth":3,"text":"Starting recording","id":"starting-recording"},{"depth":3,"text":"Stop recording","id":"stop-recording"},{"depth":3,"text":"Sample Recording Per User","id":"sample-recording-per-user"},{"depth":3,"text":"Recording just the caller","id":"recording-just-the-caller"},{"depth":3,"text":"Receiving a recording","id":"receiving-a-recording"},{"depth":2,"text":"Say","id":"say"},{"depth":2,"text":"Play sample","id":"play-sample-1"}]
lint: []
---

## About Pivot

The Pivot gives developers control over Inbound Call Handling. Pivot send call details via HTTP to the developer's web server. Pivot expects a response with appropriate JSON, and will execute the callflow returned on behalf of the developer.

A Pivot can be used in callflows in anywhere you want the external application to determine call routing and manage the call experience.

A Pivot can build complex call handling using a wide range of callflow elements. Complex call control may requiere muliple pivots.

To configure Pivots you need to have Advance Call Flows feature enabled on your account.

### Use Cases

<ul>
<li><p>Collecting input from callers to determine routing</p>
</li>
<li><p>Automatically assigning inbound calls to the last agent they spoke with</p>
</li>
<li><p>Dynamically routing calls based on phone number</p>
</li>
<li><p>Automatically routing calls when a location is closed</p>
</li>
</ul>

### Example Callflow

The most basic callflow for Pivot:

JSON

<pre tabindex="0"><code class="language-json">{
 &quot;flow&quot;:{
     &quot;module&quot;:&quot;pivot&quot;
     ,&quot;data&quot;:{
         &quot;voice_url&quot;:&quot;http://your.pivot.server/path/to/script.php&quot;
         ,&quot;req_format&quot;:&quot;kazoo&quot;
         ,&quot;method&quot;:&quot;get&quot;
         ,&quot;debug&quot;:false
     }
 }
}</code></pre>

### Debugging

You can set the `debug` flag to "true" to log the requests and responses Pivot receives from your Pivot Callflows.

### Summary

Get a list of recent Pivot attempts:

Shell

<pre tabindex="0"><code class="language-json">$ curl -H &quot;X-Auth-Token: {AUTH_TOKEN} \
     -H &quot;Content-Type: application/json&quot; \
     'http://api.virtualpbx.net/v2/accounts/{ACCOUNT_ID}/pivot/debug'</code></pre>

Response:

JSON

<pre tabindex="0"><code class="language-json">{
   &quot;data&quot;:{
      &quot;name&quot;:&quot;Missed Call&quot;,
      &quot;hook&quot;:&quot;notifications&quot;,
      &quot;include_subaccounts&quot;:false,
      &quot;http_verb&quot;:&quot;get&quot;,
      &quot;retries&quot;:4,
      &quot;uri&quot;:&quot;https://hooks.zapier.com/hooks/catch/591668/xlwslm/&quot;,
      &quot;type&quot;:&quot;account&quot;,
      &quot;action&quot;:&quot;doc_created&quot;,
      &quot;custom_data&quot;:{
         &quot;type&quot;:&quot;missed_call&quot;
      },
      &quot;enabled&quot;:true,
      &quot;include_internal_legs&quot;:true,
      &quot;id&quot;:&quot;51840ffc8bde832a1f5477def32cd665&quot;
   }
}</code></pre>

### Specific Call

Get details of a specific Pivot attempt:

Shell

<pre tabindex="0"><code class="language-json">$curl -H &quot;X-Auth-Token: {AUTH_TOKEN} \
    -H &quot;Content-Type: application/json&quot; \
    'http://api.virtualpbx.net/v2/accounts/{ACCOUNT_ID}/pivot/debug/{CALL_ID}'</code></pre>

Response:

JSON

<pre tabindex="0"><code class="language-json">{
    &quot;auth_token&quot;: &quot;{AUTH_TOKEN}&quot;,
        &quot;data&quot;: [
        {
            &quot;call_id&quot;: &quot;{CALL_ID}&quot;,
            &quot;id&quot;: &quot;{DEBUG_ID}&quot;,
            &quot;method&quot;: &quot;get&quot;,
            &quot;req_body&quot;: &quot;&quot;,
            &quot;req_headers&quot;: {},
            &quot;uri&quot;: &quot;http://your.pivot.server/script.php?Caller-ID-Number=XXXXXXXXXX&amp;Caller-ID-Name=JoeFromIT&amp;Direction=inbound&amp;ApiVersion=2013-05-01&amp;ToRealm=your.pivot.server&amp;To=3004&amp;FromRealm=your.pivot.server&amp;From=user_sov2kt&amp;Account-ID={ACCOUNT_ID}&amp;Call-ID={CALL_ID}&quot;
        },
        {
            &quot;call_id&quot;: &quot;{CALL_ID}&quot;,
            &quot;id&quot;: &quot;{DEBUG_ID}&quot;,
            &quot;resp_body&quot;: &quot;\n    {\&quot;module\&quot;:\&quot;say\&quot;\n     ,\&quot;data\&quot;:{\&quot;text\&quot;:\&quot;Please leave your message after the beep\&quot;}\n     ,\&quot;children\&quot;:{\n         \&quot;_\&quot;:{\n           \&quot;module\&quot;:\&quot;record_caller\&quot;\n           ,\&quot;data\&quot;:{\n               \&quot;format\&quot;:\&quot;mp3\&quot;\n               ,\&quot;url\&quot;:\&quot;http://your.pivot.server/recordings\&quot;\n               ,\&quot;time_limit\&quot;:360\n           }\n         }\n        }\n    }\n&quot;
        },
        {
        &quot;call_id&quot;: &quot;{CALL_ID}&quot;,
        &quot;id&quot;: &quot;{DEBUG_ID}&quot;,
        &quot;resp_headers&quot;: {
            &quot;content-length&quot;: &quot;342&quot;,
            &quot;content-type&quot;: &quot;application/json&quot;,
            &quot;date&quot;: &quot;wed, 01 oct 2014 23:30:24 gmt&quot;,
            &quot;server&quot;: &quot;apache/2.4.7 (ubuntu)&quot;,
            &quot;x-powered-by&quot;: &quot;php/5.5.9-1ubuntu4.4&quot;
        },
        &quot;resp_status_code&quot;: &quot;200&quot;
    }
    ],
    &quot;request_id&quot;: &quot;{REQUEST_ID}&quot;,
    &quot;revision&quot;: &quot;{REVISION}&quot;,
    &quot;status&quot;: &quot;success&quot;
    }</code></pre>

Remember to URL-encode the `{CALL_ID}` before sending the request.

### Failback

You can add a children to your pivot callflow in case your server is unreachable or send back an error.

JSON

<pre tabindex="0"><code class="language-json">&quot;flow&quot;: {
    &quot;data&quot;: {
        &quot;method&quot;: &quot;GET&quot;,
        &quot;req_timeout&quot;: &quot;5&quot;,
        &quot;req_format&quot;: &quot;kazoo&quot;,
        &quot;voice_url&quot;: &quot;{SERVER_URL}&quot;
    },
    &quot;module&quot;: &quot;pivot&quot;,
    &quot;children&quot;: {
        &quot;_&quot;: {
            &quot;module&quot;: &quot;play&quot;,
            &quot;data&quot;: {
                &quot;id&quot;: &quot;{MEDIA_ID}&quot;
            },
            &quot;children&quot;: {}
        }
    }
}</code></pre>

## The Request Payload

You can configure whether you receive the data as a query string in a `GET` or as a URL-encoded body in a `POST`.

### Payload

Some of the fields may not be included in every request.

<div class="table-scroll" tabindex="0"><table><tr><th>Field</th><th>Description</th></tr><tr><td>Call-ID</td><td>The unique call identifier</td></tr><tr><td>Account-ID</td><td>The account id receiving the call</td></tr><tr><td>From</td><td>The SIP From username</td></tr><tr><td>From-Realm</td><td>The SIP From realm</td></tr><tr><td>To</td><td>The SIP To username</td></tr><tr><td>To-Realm</td><td>The SIP To realm</td></tr><tr><td>Request</td><td>The SIP Request username</td></tr><tr><td>Request-Realm</td><td>The SIP Request realm</td></tr><tr><td>Call-Status</td><td>Status of the call</td></tr><tr><td>Api-Version</td><td>The version of the API</td></tr><tr><td>Direction</td><td>The direction of the call, relative to VirtualPBX</td></tr><tr><td>Caller-ID-Name</td><td>Caller ID Name</td></tr><tr><td>Caller-ID-Number</td><td>Caller ID Number</td></tr><tr><td>User-ID</td><td>The VirtualPBX User identifier(s) of the caller</td></tr><tr><td>Language</td><td>The caller's language preference</td></tr><tr><td>Recording-Url</td><td>Where the recording will be stored</td></tr><tr><td>Recording-Duration</td><td>How long the recording is</td></tr><tr><td>Recording-ID</td><td>The recording id</td></tr><tr><td>Digits</td><td>Any DTMF (or collection of DTMFs) pressed</td></tr></table></div>

## JSON Overview

Sometimes VirtualPBX's callflow builder doesn't match your needs, integrate with your applications, or provide the experience for your caller that you desire. Fortunately, the building blocks are available for you to play with as you see fit!

By routing a call to a Pivot callflow action, Vi will make an HTTP request to your web server asking for the callflow to process for that specific call (versus the one-size-fits-all approach in the normal callflow builder).

This could be best illustrated with an example, no?

### Building Time-based routing

We want to add time-based rules when deciding how to route our office's main number (this exists with the `temporal_route` callflow action already, but we're trying to make a point!). Following a "typical" office's hours, we are open 9am-5pm (9:00 to 17:00). During that time, we'd like to ring the front desk's phone. Outside of those hours, we'd like calls to go directly to the company's voicemail box.

### Example of Building on Pivot

Based on the above goals, we need to build:

<ul>
<li><p>Front Desk's device</p>
</li>
<li><p>Company Voicemail box</p>
</li>
<li><p>Main number callflow</p>
</li>
</ul>

Once you've made those, note the IDs for the device and voicemail box (using the developer tool is a good way to find those).

The main number callflow should be:

Plain Text

<pre tabindex="0"><code>[NUMBER] -&gt; Pivot
            Url: http://your.webserver.com/path/to/main_number_tod.php</code></pre>

Now, whenever this number is called, VirtualPBX will query your URL for callflow to execute.

### Build main\_number\_tod.php

The first thing needed is to set the content-type to application/json.

PHP

<pre tabindex="0"><code class="language-php">&lt;?php
    header('content-type:application/json');</code></pre>

Now we need to know what time it is and determine what to do:

PHP

<pre tabindex="0"><code class="language-php">$now = time();
$hour = date(&quot;G&quot;, $now);
if ( $hour &gt;= 9 &amp;&amp; $hour &lt; 17 ) {
  business_hours();
} else {
  after_hours();
}</code></pre>

Now we have two functions to build the JSON for the callflow to execute:

<ul>
<li><p><code>business_hours()</code>:</p>
PHP
<pre tabindex="0"><code class="language-php">  function business_hours() {
  ?&gt;
    {&quot;module&quot;:&quot;device&quot;
     ,&quot;data&quot;:{&quot;id&quot;:&quot;{FRONT_DESK_DEVICE_ID}&quot;}
     ,&quot;children&quot;:{
       &quot;_&quot;:{
         &quot;module&quot;:&quot;voicemail&quot;
         ,&quot;data&quot;:{&quot;id&quot;:&quot;{COMPANY_VM_BOX_ID}&quot;}
       }
     }
  &lt;?php
  }</code></pre></li>
<li><p><code>after_hours()</code>:</p>
PHP
<pre tabindex="0"><code class="language-php">  function after_hours() {
  ?&gt;
    {&quot;module&quot;:&quot;voicemail&quot;
     ,&quot;data&quot;:{&quot;id&quot;:&quot;{COMPANY_VM_BOX_ID}&quot;}
    }
  &lt;?php
  }</code></pre></li>
</ul>

Bring it all together:

PHP

<pre tabindex="0"><code class="language-php">&lt;?php
    header('content-type:application/json');
    $now = time();
    $hour = date(&quot;G&quot;, $now);
    if ( $hour &gt; 8 &amp;&amp; $hour &lt; 17 ) {
      business_hours();
    } else {
      after_hours();
    }
    function business_hours() {
    ?&gt;
      {&quot;module&quot;:&quot;device&quot;
       ,&quot;data&quot;:{&quot;id&quot;:&quot;{FRONT_DESK_DEVICE_ID}&quot;}
       ,&quot;children&quot;:{
         &quot;_&quot;:{
           &quot;module&quot;:&quot;voicemail&quot;
           ,&quot;data&quot;:{&quot;id&quot;:&quot;{COMPANY_VM_BOX_ID}&quot;}
         }
       }
    &lt;?php
    }
    function after_hours() {
    ?&gt;
      {&quot;module&quot;:&quot;voicemail&quot;
       ,&quot;data&quot;:{&quot;id&quot;:&quot;{COMPANY_VM_BOX_ID}&quot;}
      }
    &lt;?php
    }
?&gt;</code></pre>

## TwiML Format

Pivot supports a subset of TwiML to help ease you into Pivot with an existing TwiML-based application.

### Core Supported

### Default Request Data included

<div class="table-scroll" tabindex="0"><table><tr><th>Request Parameter</th><th>Name</th><th>Description</th></tr><tr><td>CallerName</td><td>Caller-ID-Name</td><td>Name of the caller, if any</td></tr><tr><td>Direction</td><td>Direction</td><td>Direction of the call (outbound if VirtualPBX originated the call, inbound otherwise)</td></tr><tr><td>ApiVerson</td><td>N/A</td><td>Version string related to API changes</td></tr><tr><td>CallStatus</td><td>N/A</td><td>What state the call is currently in</td></tr><tr><td>To</td><td>To-User</td><td>Dialed number</td></tr><tr><td>From</td><td>From-User</td><td>Caller's number, if available</td></tr><tr><td>AccountSid</td><td>Account-ID</td><td>Account ID processing the call</td></tr><tr><td>CallSid</td><td>Call-ID</td><td>Unique identifier of the call leg</td></tr></table></div>

### Optional/Conditional Request Data

<div class="table-scroll" tabindex="0"><table><tr><th>Request Parameter</th><th>Name</th><th>Description</th></tr><tr><td>RecordingUrl</td><td>Recording-URL</td><td>Where a recording will be sent (via HTTP PUT request)</td></tr><tr><td>RecordingDuration</td><td>Recording-Duration</td><td>Length of the recording, if available</td></tr><tr><td>RecordingSid</td><td>Media-Name</td><td>Name of the recording file</td></tr><tr><td>Digits</td><td>DTMF-Pressed</td><td>The DTMF(s) (touch tone) pressed by the caller</td></tr><tr><td>DialCallStatus</td><td>N/A</td><td>Call status of the b-leg</td></tr><tr><td>DialCallSid</td><td>Other-Leg-Unique-ID</td><td>Call-ID of the b-leg</td></tr><tr><td>DialCallDuration</td><td>Billing-Seconds</td><td>How many billable seconds the call lasted</td></tr></table></div>

Other optional data includes user-defined key/value pairs stored using the verb below.

### TwiML Verbs

<div class="table-scroll" tabindex="0"><table><tr><th>Verb</th><th>Description</th><th>Nestable Verbs and Nouns</th></tr><tr><td></td><td>Connect the caller to other endpoints</td><td>plain text DID, , , , ,</td></tr><tr><td></td><td>Record the caller</td><td></td></tr><tr><td></td><td>Collect DTMFs from the caller</td><td>,</td></tr><tr><td></td><td>Play a media file (mp3, wav) to the caller</td><td></td></tr><tr><td>Say</td><td>Use a TTS engine to say the supplied text</td><td></td></tr><tr><td>Redirect</td><td>Like an HTTP Redirect, make another HTTP request</td><td></td></tr><tr><td>Pause</td><td>Pause callflow execution for supplied number of seconds</td><td></td></tr><tr><td>Hangup</td><td>Hangup the caller</td><td></td></tr><tr><td>Reject</td><td>Reject (and don't answer - won't start billing) the call</td><td></td></tr></table></div>

### Custom Verbs

<div class="table-scroll" tabindex="0"><table><tr><th>Verb</th><th>Description</th><th>Nestable Nouns</th></tr><tr><td></td><td>Key value pair(s) to store along-side the call</td><td></td></tr></table></div>

### Core TwiML Nouns

<div class="table-scroll" tabindex="0"><table><tr><th>Noun</th><th>Description</th></tr><tr><td></td><td>Conference room endpoint for</td></tr><tr><td></td><td>Call queue to line callers up in</td></tr><tr><td></td><td>DID with extended attributes</td></tr><tr><td></td><td>ID of an existing User (works like the User callflow element</td></tr><tr><td></td><td>ID of an existing Device</td></tr><tr><td></td><td>SIP URI to dial</td></tr></table></div>

### Custom Nouns

<div class="table-scroll" tabindex="0"><table><tr><th>Noun</th><th>Description</th></tr><tr><td></td><td>Includes 'key' and 'value' attributes; values will be put subsequent requests</td></tr></table></div>

## Bridging

VirtualPBX JSON offers a plethora of ways to call out to various endpoints! Below are some minimalist examples to whet your appetite. Most take more options, are nestable as children of other callflow actions, and are generally quite useful in accomplishing most peoples' needs.

### Devices

Dial a single device

JSON

<pre tabindex="0"><code class="language-json">{
    &quot;module&quot;:&quot;device&quot;,
    &quot;data&quot;:{&quot;id&quot;:&quot;device_id&quot;}
}</code></pre>

### Users

Dial a User (any devices owned by the user)

JSON

<pre tabindex="0"><code class="language-json">{
    &quot;module&quot;: &quot;user&quot;,
    &quot;data&quot;: {
        &quot;id&quot;: &quot;user_id&quot;
    }
}</code></pre>

### Ring Group

Ring groups are ultra-flexible in what types of endpoints you can combine: devices, users, or groups! You need only include the IDs you want to ring and VirtualPBX will build the appropriate list of endpoints.

JSON

<pre tabindex="0"><code class="language-json">{
    &quot;module&quot;:&quot;ring_group&quot;,
    &quot;data&quot;:{
        &quot;endpoints&quot;: [&quot;device_1_id&quot;,
                      &quot;device_2_id&quot;,
                      &quot;user_1_id&quot;,
                      &quot;user_2_id&quot;,
                      &quot;group_1_id&quot;,
                      &quot;group_2_id&quot;
                     ]
    }
}</code></pre>

You are free to mix/match devices, users, and groups based on the needs of this particular call.

### Dialing outside the account

It is all well and good that dialing to known VirtualPBX endpoints is so easy, but what about contacting the outside world?

VirtualPBX supports two types of resources, global and per-account (or local, as VirtualPBX refers to them). You can optionally route to either, depending on how you've configured your account and whether you utilize the VirtualPBX cluster's global resources.

The only real difference is the `use_local_resources` flag.

### Using Global resources

JSON

<pre tabindex="0"><code class="language-json">{
    &quot;module&quot;: &quot;resources&quot;,
    &quot;data&quot;: {
        &quot;to_did&quot;: &quot;+14155550000&quot;,
        &quot;use_local_resources&quot;: false
    }
}</code></pre>

### Using Local resources

JSON

<pre tabindex="0"><code class="language-json">{
    &quot;module&quot;: &quot;resources&quot;,
    &quot;data&quot;: {
        &quot;to_did&quot;: &quot;+14155550000&quot;,
        &quot;use_local_resources&quot;: true
    }
}</code></pre>

## Collect

It is possible to collect DTMFs via VirtualPBX JSON using the `cf_collect_dtmf` callflow action. You can also store the collected DTMF in the custom-named keys to differentiate different collections.

## Collecting DTMF

The initial VirtualPBX JSON to collect DTMF could look something like:

PHP

<pre tabindex="0"><code class="language-json">&lt;?php
header('content-type:application/json');
?&gt;
{
    &quot;module&quot;:&quot;tts&quot;,
    &quot;data&quot;:{
        &quot;text&quot;: &quot;Please enter up to four digits.&quot;
    },
    &quot;children&quot;: {
        &quot;_&quot;: {
            &quot;module&quot;: &quot;collect_dtmf&quot;,
            &quot;data&quot;: {
                &quot;max_digits&quot;: 4,
                &quot;collection_name&quot;: &quot;custom_name&quot;
            },
            &quot;children&quot;: {
                &quot;_&quot;: {
                    &quot;module&quot;: &quot;pivot&quot;,
                    &quot;data&quot;: {
                        &quot;voice_url&quot;: &quot;http://pivot.your.company.com/collected.php&quot;
                    },
                    &quot;children&quot;: {}
                }
            }
        }
    }
}</code></pre>

First, VirtualPBX will use the TTS engine to say the `text` field. Next, it will wait for the user to press up to 4 DTMF (with `#` being a terminating DTMF that is not included in the collection). Finally, a second pivot request will be made the the `collected.php` script on your server.

This is a basic menu! Congrats, you can build custom IVRs!

### Processing collected DTMF

Here is demonstrated speaking back the digits pressed to the caller; you could obviously key off the DTMF to do whatever further call processing.

PHP

<pre tabindex="0"><code class="language-json">&lt;?php
header('content-type:application/json');
$dtmf = $_REQUEST['Digits'];
if ( empty($dtmf) ) {
?&gt;
{
    &quot;module&quot;: &quot;tts&quot;,
    &quot;data&quot;: {
        &quot;text&quot;: &quot;We didn't get that&quot;
    },
    &quot;children&quot;: {}
}
&lt;?php } else if ( is_string($dtmf) ) { ?&gt;
{
    &quot;module&quot;: &quot;tts&quot;,
    &quot;data&quot;: {
        &quot;text&quot;: &quot;You typed &lt;?= $dtmf ?&gt;&quot;
    },
    &quot;children&quot;: {}
}
&lt;?php } else { ?&gt;
{
    &quot;module&quot;: &quot;tts&quot;,
    &quot;data&quot;: {
        &quot;text&quot;: &quot;You typed &lt;?= $dtmf['custom_name'] ?&gt;&quot;
    },
    &quot;children&quot;: {}
}
&lt;?php } ?&gt;</code></pre>

The `is_string($dtmf)` check is to support the old way of returning DTMF in the Pivot request. Otherwise, you should receive an array of DTMF collections, indexed by the key name supplied ("default" if you didn't specify one).

## Conferencing

There are two main ways to add the caller to a conference:

<ol>
<li><p>Use pre-built VirtualPBX conference rooms, configured via Crossbar</p>
</li>
<li><p>Create ad-hoc conference rooms via Pivot</p>
</li>
</ol>

### Pre-built example

JSON

<pre tabindex="0"><code class="language-json">{
    &quot;module&quot;: &quot;conference&quot;,
    &quot;data&quot;: {
        &quot;id&quot;: &quot;conference_id&quot;
    }
}</code></pre>

### Ad-Hoc example

JSON

<pre tabindex="0"><code class="language-json">{
    &quot;module&quot;: &quot;conference&quot;,
    &quot;data&quot;: {
        &quot;config&quot;: {
            &quot;name&quot;: &quot;My Ad-hoc Conference&quot;
        }
    }
}</code></pre>

This will create a minimalist conference bridge, named "My Ad-hoc Conference". Use the same name to ensure callers end up in the same conference together.

### Conference Per Area Code

PHP

<pre tabindex="0"><code class="language-json">&lt;?php
header('content-type: application/json');
$caller_id = $_REQUEST['Caller-ID-Number'];
$areacode = ($caller_id, 0, 3);
?&gt;
{
    &quot;module&quot;: &quot;tts&quot;,
    &quot;data&quot;: {
        &quot;text&quot;: &quot;Welcome to the &lt;?= $areacode ?&gt; conference!&quot;
    },
    &quot;children&quot;: {
        &quot;_&quot;: {
            &quot;module&quot;: &quot;conference&quot;,
            &quot;data&quot;: {
                &quot;config&quot;: {
                    &quot;name&quot;: &quot;Areacode &lt;?= $areacode ?&gt;&quot;
                }
            },
            &quot;children&quot;: {}
        }
    }
}</code></pre>

Obviously this doesn't handle hidden or missing caller ID.

## DTMF

Sending DTMF to the caller is sometimes necessary (automating IVR navigation, perhaps). Use the `send_dtmf` callflow to do so.

### Example

JSON

<pre tabindex="0"><code class="language-json">{
    &quot;module&quot;: &quot;send_dtmf&quot;,
    &quot;data&quot;: {
        &quot;digits&quot;: &quot;123ABC#&quot;,
        &quot;duration_ms&quot;: 2000
    },
    &quot;children&quot;: {}
}</code></pre>

<ul>
<li><p><code>digits</code> is a string of DTMF to send.</p>
</li>
<li><p><code>duration_ms</code> is how long of a tone to send each DTMF (and optional)</p>
</li>
</ul>

The above example would send "1", "2", "3", "A", "B", "C", and finally "#", each as 2 second tones.

## Hang Ups

Sometimes it is necessary to respond to a call with a hangup cause and code. For extra fun, some phones will display the hangup cause on the display, which can be a source of amusement. For instance, for a while VirtualPBX would return a "403 Insert Coin" if the account was out of money.

### Example

JSON

<pre tabindex="0"><code class="language-json">{
    &quot;module&quot;: &quot;response&quot;,
    &quot;data&quot;: {
        &quot;code&quot;: &quot;486&quot;,
        &quot;message&quot;: &quot;User Busy&quot;,
        &quot;media&quot;:&quot;id_or_url&quot;
    }
}</code></pre>

If you define `media`, the media will be played to the user before hanging up the call.

## Play

It is pretty easy to play files, either hosted in your account or accessible via a URI.

### Play sample

The initial VirtualPBX JSON to play a file could look something like:

PHP

<pre tabindex="0"><code class="language-json">&lt;?php
header('content-type:application/json');
?&gt;
{
    &quot;module&quot;: &quot;play&quot;,
    &quot;data&quot;: {
        &quot;id&quot;: &quot;media_id&quot;
    },
    &quot;children&quot;: {
        &quot;_&quot;: {
            &quot;module&quot;: &quot;play&quot;,
            &quot;data&quot;:{
                &quot;id&quot;: &quot;http://some.file.server/some/file.mp3&quot;
            },
            &quot;children&quot;: {}
        }
    }
}</code></pre>

Here we see two play actions, one that uses a media file hosted by VirtualPBX and one that fetches the file from an HTTP server.

## Presence

Pivot allows you to set custom presence updates (known as manual presence).

### Example

JSON

<pre tabindex="0"><code class="language-json">{
    &quot;module&quot;: &quot;manual_presence&quot;,
    &quot;data&quot;: {
        &quot;presence_id&quot;: &quot;username&quot;,
        &quot;status&quot;: &quot;ringing&quot;
    }
}</code></pre>

Do note, `presence_id` without an `@realm.com` will be suffixed with the account's realm.

`status` can be one of `idle`, `ringing`, or `busy`.

## Recording

Recording the caller (or caller and callee in a bridged-call scenario) is straightforward to start. What's trickier is how to store the recording.

### Recording Caller/Callee situations

### Starting recording

JSON

<pre tabindex="0"><code>{
    &quot;module&quot;: &quot;record_call&quot;,
    &quot;data&quot;: {
        &quot;action&quot;: &quot;start&quot;,
        &quot;time_limit&quot;: 1200,
        &quot;format&quot;: &quot;mp3&quot;,
        &quot;url&quot;: &quot;http://your.server.com/recordings&quot;
    }
}</code></pre>

This will start the call recording, limiting it to 1200 seconds, and will encode the audio into an MP3 file (alternatively, you can use "wav"). The `url` is where the resulting file will be sent via an HTTP PUT request. It is then up to the receiving server to properly handle the request and store the file for later use.

<aside class="note"><p><code>time_limit</code> is constrained by the <code>system_config/media</code> doc's <code>max_recording_time_limit</code> entry (default is 10800 seconds). If your recordings are not long enough, that is the setting that needs increasing.</p></aside>

<aside class="note"><p><code>url</code> will be used as the base URL for the resulting PUT. The final URL will be <code>URL/call_recording_CALL_ID. EXT</code>where <code>URL</code> is the supplied URL, <code>CALL_ID</code> is the call ID of the A-leg being recorded, and <code>EXT</code> is the <code>format</code> parameter.</p></aside>

<aside class="note"><p>If <code>url</code> is not provided, VirtualPBX will determine if call recoriding is configured on the account. If not configured, the recording will not be stored. If configured, VirtualPBX will store the recording based on the configuration.</p></aside>

### Stop recording

If you need to programmatically stop the current recording (vs implicitly when the call ends):

JSON

<pre tabindex="0"><code>{
    &quot;module&quot;: &quot;record_call&quot;,
    &quot;data&quot;: {
        &quot;action&quot;: &quot;stop&quot;
    }
}</code></pre>

### Sample Recording Per User

JSON

<pre tabindex="0"><code>{
    &quot;module&quot;: &quot;record_call&quot;,
    &quot;data&quot;: {
        &quot;action&quot;: &quot;start&quot;,
        &quot;format&quot;: &quot;mp3&quot;,
        &quot;url&quot;: &quot;http://my.recording.server/{ACCOUNT_ID}/{USER_ID}&quot;,
        &quot;time_limit&quot;:360
    },
    &quot;children&quot;: {
        &quot;_&quot;: {
            &quot;module&quot;: &quot;user&quot;,
            &quot;data&quot;: {
                &quot;id&quot;: &quot;{USER_ID}&quot;
            },
            &quot;children&quot;: {
                &quot;_&quot;: {
                    &quot;module&quot;: &quot;record_call&quot;,
                    &quot;data&quot;: {
                        &quot;action&quot;: &quot;stop&quot;
                    },
                    &quot;children&quot;: {
                        &quot;module&quot;: &quot;voicemail&quot;,
                        &quot;data&quot;: {
                            &quot;id&quot;: &quot;{VMBOX_ID}&quot;
                        },
                        &quot;children&quot;: {}
                    }
                }
            }
        }
    }
}</code></pre>

<aside class="note"><p>Call recording and Voicemail do not play well together. You will need to stop the recording before voicemail to avoid conflict.</p></aside>

### Recording just the caller

This action is more appropriate for recording just the caller (think voicemail or recording menu prompts).

JSON

<pre tabindex="0"><code>{
    &quot;module&quot;: &quot;say&quot;,
    &quot;data&quot;: {
        &quot;text&quot;: &quot;Please leave your message after the beep&quot;
    },
    &quot;children&quot;: {
        &quot;_&quot;: {
            &quot;module&quot;: &quot;record_caller&quot;,
            &quot;data&quot;: {
                &quot;format&quot;: &quot;mp3&quot;,
                &quot;url&quot;: &quot;http://my.recording.server/voicemail/{ACCOUNT_ID}/{BOX_ID}&quot;,
                &quot;time_limit&quot;:360
            }
        }
    }
}</code></pre>

### Receiving a recording

Here is a simple PHP/`.htaccess` combo for receiving a recording.

<ol>
<li><p>Assume <code>url</code> in our <code>data</code> object is &quot;<a href="http://your.server.com/kzr%22">http://your.server.com/kzr&quot;</a></p>
</li>
<li><p>Create a <code>.htaccess</code> file in the DocumentRoot. This will direct the request to <code>/kzr/index.php</code> with the <code>recording</code>query string parameter set to <code>CALL_ID. EXT</code>.</p>
</li>
</ol>

Plain Text

<pre tabindex="0"><code>        &lt;IfModule mod_rewrite.c&gt;
            RewriteEngine On
            RewriteBase /
            RewriteRule ^kzr/call_recording_(.+)\.(.+)$  kzr/index.php?recording=$1.$2 [QSA,L]
        &lt;/IfModule&gt;</code></pre>

<ol>
<li><p>Create <code>/kzr/index.php</code> to receive and store the recording.</p>
</li>
</ol>

PHP

<pre tabindex="0"><code>&lt;?php
/* PUT data comes in on the stdin stream */
$putdata = fopen(&quot;php://input&quot;, &quot;r&quot;);
$r = $_REQUEST[&quot;recording&quot;];
/* Open a file for writing */
$fp = fopen(&quot;/tmp/$r&quot;, &quot;w&quot;);
/* 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);
?&gt;</code></pre>

A file should be created in `/tmp/` named `CALL_ID. EXT`. You could, of course, store this in MySQL, Postgres, S3, feed it to a transcription service, etc.

## Say

Text-to-speech is an easy way to read text to the caller

## Play sample

The initial VirtualPBX JSON to

PHP

<pre tabindex="0"><code>&lt;?php
header('content-type:application/json');
?&gt;
{
    &quot;module&quot;: &quot;tts&quot;,
    &quot;data&quot;: {
        &quot;text&quot;: &quot;Pivot is pretty awesome. Have a great day.&quot;
    },
    &quot;children&quot;: {
        &quot;_&quot;: {
            &quot;module&quot;: &quot;response&quot;,
            &quot;data&quot;: {}
        }
    }
}</code></pre>

Here the TTS engine will read the text to the caller and then hang up.
