Using <merge> to Hand Off Calls

Blueworx Voice Response supports the CCXML merge element to merge two calls together. Unlike using <join> Blueworx Voice Response will no longer be connected to the two calls if it succeeds

This works by using SIP REFER with the Replaces header to let the other endpoint know that we're changing a leg of the call rather than making a brand new connection.

For this to function with the target of the merge (the second call) you might need to specify other headers that you want call1 to send to call2. To do this, supply a refer_to_values hints object containing key-value pairs for values you want to be become headers on that INVITE message.

Here is an example of using <merge> supplying two custom headers via refer_to_values:

<ccxml version="1.0">
        <meta http-equiv="Cache-Control" content="no-cache"/> <!-- This means we fetch the CCXML application every time we get a new call - useful for debugging -->
        <!-- These variables are used to track state and objects this session owns -->
        <var name="call_state" expr="'PreCall'"/> <!-- The state variable - we use this to respond differently to events depending on context -->
        <var name="call1" expr="''"/> <!-- The connection ID of the initial call -->
        <var name="call2" expr="''"/> <!-- The connection ID of the second call -->
        <var name="prefix" expr="'merge_sample ['"/>
        <var name="target_uri" expr="'sip:YOUR_ADDRESS_HERE@REPLACE_THIS'"/> <!-- this is the second leg of the merge we will make with the inbound caller -->
        <var name="count" expr="0"/>
<script>
<![CDATA[
function printObject(toPrint) {
    var output = "";
    var count = 0;
    var total = 0;
    for (var i in toPrint) {
        ++total;
    }
    --total;
    //Total now has the last index value for the list of attributes in toPrint
    for (var i in toPrint) {
      if (toPrint[i] == undefined || toPrint[i] == null)
        continue;
      if (toPrint[i].toString() == "[object Object]") {
        output += i + ' = {' + printObject(toPrint[i]) + '}';
      } else {
        output +=  i + ' = ' + toPrint[i] ;
      }
      output += ((total != count) ? ', ' : '');
      if (toPrint[i].toString() == "[object Object]")
        output += "\n";
      ++count;
    }
    return output;
}
]]>
</script>

        <eventprocessor statevariable="call_state">
                <transition event="ccxml.loaded">
                        <log expr="prefix + event$.name + '], ready to run a merge scenario on sessionid ' + session.id"/>
                </transition>

                <transition event="connection.alerting">
                        <log expr="prefix + event$.name + '] for connection ' + event$.connectionid + ' in state ' + call_state"/>
                        <accept/>
                </transition>

                <transition event="connection.progressing">
                        <log expr="prefix + event$.name + '] for connection ' + event$.connectionid + ' in state ' + call_state"/>
                </transition>

                <transition event="connection.connected" state="PreCall">
                        <log expr="prefix + event$.name + '] for connection ' + event$.connectionid + ' in state ' + call_state"/>
                        <assign name="call_state" expr="'Connected'"/>
                        <assign name="call1" expr="event$.connectionid"/>
                        <createcall dest="target_uri" connectionid="call2"/>
                </transition>

                <transition event="connection.connected">
                        <!-- This should be the bridge transfer call completing - the mergeid on the createcall will then complete the transfer -->
                        <log expr="prefix + event$.name + '] merging calls for connection ' + event$.connectionid + ' in state ' + call_state"/>
                        <var name="hints" expr="{refer_to_values: {uui: 'MyCustomUUIData', MyCustomHeader: 'my_custom_value'}}"/>
                        <merge connectionid1="call1" connectionid2="call2" hints="hints"/>
                </transition>

                <transition event="send.successful"></transition>

                <transition event="connection.merged">
                        <log expr="prefix + event$.name + '] Success! for connection ' + event$.connectionid + ', obj ' + printObject(event$.connection) + ' in state ' + call_state"/>
                        <assign name="count" expr="count + 1"/>
                        <if cond="count == 2">
                                <log expr="'Both calls report merged, ending session'"/>
                                <exit/>
                        </if>
                </transition>

                <transition event="connection.merged.failed">
                        <log expr="prefix + event$.name + '] Failure with reason ' + event$.reason + '! for connection ' + event$.connectionid + ', obj ' + printObject(event$.connection) + ' in state ' + call_state + '; Exiting due to failure'"/>
                        <exit/><!-- this will hang up the calls automatically -->
                </transition>

                <transition event="connection.disconnected">
                        <log expr="prefix + event$.name + '], sending delayed exit for connection ' + event$.connectionid + ' in state ' + call_state"/>
                        <send target="session.id" targettype="'ccxml'" name="'delayed.exit'" delay="'60s"/>
                </transition>

                <transition event="delayed.exit">
                        <log expr="'CCXML delayed exit triggered, exiting'"/>
                        <exit/>
                </transition>

                <transition event="connection.failed">
                        <!-- Bridge transfer failed, notify the connection so that the VoiceXML dialog can exit cleanly -->
                        <log expr="prefix + event$.name + '] for connection ' + event$.connectionid + ' in state ' + call_state"/>
                        <assign name="call_state" expr="'Failed'"/>
                        <assign name="call2" expr="''"/>
                        <if cond="call1 != ''">
                                <disconnect connectionid="call1"/>
                                <else/>
                                <exit/>
                        </if>
                </transition>

                <transition event="connection.signal"/><!-- we don't care about these -->

                <!-- catchall event to log out other events that aren't expected -->
                <transition event="*">
                        <log expr="prefix + event$.name + '] reason ' + event$.reason + ' in state ' + call_state"/>
                </transition>
        </eventprocessor>
</ccxml>