CCXML 1.0-W3C Development GuideHome  |  Frameset Home

  tutorial Connecting Call Legs  |  TOC  |  tutorial Multi-party Conferencing in CCXML 1.0  

Tutorial: Outbound CCXML Calls with Tokens

This Lesson will show you how to create your first outbound CCXML 1.0 application using an http-token initiated session. This lesson also assumes that you have completed the previous tutorials, and is not recommended as a starting point for learning CCXML 1.0: This is important, as the concepts covered in this lesson are based on topics that have been covered thoroughly in previous tutorials, and making this your first lesson on CCXML 1.0 will doubtlessly cause confusion for new developers.

Note: This tutorial requires the enabling of outdial privileges on the Voxeo network, and the provisioning of a alphanumeric token string to your application. In order to get hooked up with all these neat features, check our Support Guide for all the juicy details.

In this tutorial, we will:


Note: The code presented within this tutorial is intended for the "Prophecy - CCXML 1.0" application type only. Attempts to utilize this code on the "CCXML - Voxeo" application type will not function, as the latter platform is based on the W3C specification from 2002.  Prophecy CCXML 1.0 is compliant to the most recent specification from 2006.


Step 1: creating our initial CCXML structure

Once again, we will start out coding our fledgling CCXML 1.0 document with our initial <xml> and <ccxml> headers. As we will be using a Voxeo-specific dialog extension in this lesson, it is very important that we specify the Voxeo XML namespace, otherwise, our called party will hear a fat, juicy error message when we test it the first time:


<?xml version="1.0" encoding="UTF-8"?>
<!-- NOTE THAT WE *MUST* DECLARE THE xmlns ATTRIBUTE -->
<ccxml version="1.0" xmlns:voxeo="http://community.voxeo.com/xmlns/ccxml">
</ccxml>



Step 2: Basic error handing

Our next step is to add in the basic "catch-all" error handling routine that we learned about in the previous tutorials: Won't do us any good to jump the gun, and start out by testing sloppy code with gaping holes in error handling logic, right? Right! Further on in the tutorial, we will need to be able to access an ECMAScript variable that defines our "connection ID", and our application state so let's also declare values for these so we won't have to retrace our steps later on:


<?xml version="1.0" encoding="UTF-8"?>
<!-- NOTE THAT WE *MUST* DECLARE THE xmlns ATTRIBUTE -->
<ccxml version="1.0" xmlns:voxeo="http://community.voxeo.com/xmlns/ccxml">

  <var name="myState" expr="'init'"/>
  <var name="myOutboundConnectionID" expr="'someOutboundID'"/>


  <eventprocessor statevariable="myState">

    <transition event="error.*">
      <log expr="'an error has occurred (' + event$.reason + ')'"/>
              <voxeo:sendemail to="'yourEmail@there.com'"
                        from="'myApp@here.com'"
                        type="'debug'"
                        body=" 'generic error detected ! ' "/>
      <exit/>
    </transition>

  </eventprocessor>   
</ccxml>



Step 3: Finding the initial event

One of the first things that you will see that is very likely new to you is the "ccxml.loaded" event: Whats this all about? We haven't been concerned with this particular event up until now, because all of the previous tutorials were fairly straightforward inbound calls: we dial, the app answers, and we then output some data and hang up. For the topic of programmatically placing an outbound call, we will want to know when the CCXML 1.0 document is first loaded, and ready to execute, so that we can then fire off that outbound call with a minimum of delay: hence, the aptly-named "ccxml.loaded" event handler.

We also want to know when our outbound call fails, so we will need to throw in a handler for the "connection.failed" event, (which as all error handlers will do, will promptly <exit> after we log some values). Since this is only going to happen during the period that we are dialing the outbound destination, we will want to do some state management by setting the CCXML 1.0 application state to "dialing" during this period, and set the "state" attribute of this handler accordingly. And of course, we are all hotshot CCXML developers by now, so we also add in the inevitable <log> statements that will display our connection object and event properties when this event handler is executed:



<?xml version="1.0" encoding="UTF-8"?>
<!-- NOTE THAT WE *MUST* DECLARE THE xmlns ATTRIBUTE -->
<ccxml version="1.0" xmlns:voxeo="http://community.voxeo.com/xmlns/ccxml">

  <var name="myState" expr="'init'"/>
  <var name="myOutboundConnectionID" expr="'someOutboundID'"/>

  <eventprocessor statevariable="myState">

    <transition event="ccxml.loaded" state="init">

    <log expr="'***** EVENT$.NAME = ' + event$.name"/>
    <log expr="'***** EVENT$.SESSIONID = ' + event$.sessionid"/>
    <log expr="'***** EVENT$.PARENT = ' + event$.parent"/>
    <log expr="'***** EVENT$.EVENTID = ' + event$.eventid"/>
    <log expr="'***** EVENT$.EVENTSOURCE = ' + event$.eventsource"/>
    <log expr="'***** EVENT$.EVENTSOURCETYPE = ' + event$.eventsourcetype"/>

    <assign name="myState" expr="'dialing'"/>         
    </transition>

    <transition event="connection.failed" state="dialing">
    <log expr="'***** CONNECTION FAILED *****'"/>
    <log expr="'***** EVENT$.NAME = ' + event$.name"/>
    <log expr="'***** EVENT$.SESSIONID = ' + event$.sessionid"/>
    <log expr="'***** EVENT$.PARENT = ' + event$.parent"/>
    <log expr="'***** EVENT$.EVENTID = ' + event$.eventid"/>
    <log expr="'***** EVENT$.EVENTSOURCE = ' + event$.eventsource"/>
    <log expr="'***** EVENT$.EVENTSOURCETYPE = ' + event$.eventsourcetype"/>
    <exit/>
    </transition>


    <transition event="error.*">
      <log expr="'an error has occurred (' + event.$.reason + ')'"/>
              <voxeo:sendemail to="'yourEmail@there.com'"
                        from="'myApp@here.com'"
                        type="'debug'"
                        body=" 'generic error detected ! ' "/>
      <exit/>
    </transition>

  </eventprocessor>   
</ccxml>



Step 4: making the outbound call

Now we have error handling routines in place for application errors, as well as for outbound call failures....sheesh, what a pessimistic lot we are! Let's now get on the Sunny Side, and plan optimistically for our outbound call to succeed! In order to do this, we should probably start by figuring out how we can, um, place the outbound call. Astute readers of the Voxeo CCXML 1.0 markup guide will doubtlessly think to use the <creatcall> element in this case, and they'd be right. In order to do this successfully, we will want to remember that "myOutboundConnectionID" variable that we set earlier, and populate the "connectionID" attribute of the <createcall> element with it. Since we are now planning on a successful call, we will want to think about doing something when the call when it's actually answered, (such as playing a dialog extension...not just yet, you eager beaver), so we will want to employ the use of our old friend, the "connection.connected" handler to tell us when this happens. And when this does happen, we will want to change our state once again to indicate to the app that we are no longer dialing. Once we have all this in place, we can start with the fun stuff:



<?xml version="1.0" encoding="UTF-8"?>
<!-- NOTE THAT WE *MUST* DECLARE THE xmlns ATTRIBUTE -->
<ccxml version="1.0" xmlns:voxeo="http://community.voxeo.com/xmlns/ccxml">

  <var name="myState" expr="'init'"/>
  <var name="myOutboundConnectionID" expr="'someOutboundID'"/>

  <eventprocessor statevariable="myState">

    <transition event="ccxml.loaded" state="init">

    <log expr="'***** EVENT$.NAME = ' + event$.name"/>
    <log expr="'***** EVENT$.SESSIONID = ' + event$.sessionid"/>
    <log expr="'***** EVENT$.PARENT = ' + event$.parent"/>
    <log expr="'***** EVENT$.EVENTID = ' + event$.eventid"/>
    <log expr="'***** EVENT$.EVENTSOURCE = ' + event$.eventsource"/>
    <log expr="'***** EVENT$.EVENTSOURCETYPE = ' + event$.eventsourcetype"/>

    <assign name="myState" expr="'dialing'"/>           
    <createcall dest="'tel:4072223333'" connectionid="myOutboundConnectionID" timeout="'45s'"/>
    </transition>

    <transition event="connection.connected" state="dialing">
    <assign name="myState" expr="'connected'"/>

    <log expr="'***** CALL WAS ANSWERED *****'"/>
    <log expr="'***** EVENT$.CONNECTION.CONNECTIONID = ' + event$.connection.connectionid"/>
    <log expr="'***** EVENT$.CONNECTION.PROTOCOL.NAME = ' + event$.connection.protocol.name"/>
    <log expr="'***** EVENT$.CONNECTION.PROTOCOL.VERSION = ' + event$.connection.protocol.version"/>
    <log expr="'***** EVENT$.CONNECTION.STATE = ' + event$.connection.state"/>
    <log expr="'***** EVENT$.CONNECTION.LOCAL = ' + event$.connection.local"/>
    <log expr="'***** EVENT$.CONNECTION.REMOTE = ' + event$.connection.remote"/>
    <log expr="'***** EVENT$.CONNECTION.ORIGINATOR = ' + event$.connection.originator"/> 
    </transition>

    <transition event="connection.failed" state="dialing">
    <log expr="'***** CONNECTION FAILED *****'"/>
    <log expr="'***** EVENT$.NAME = ' + event$.name"/>
    <log expr="'***** EVENT$.SESSIONID = ' + event$.sessionid"/>
    <log expr="'***** EVENT$.PARENT = ' + event$.parent"/>
    <log expr="'***** EVENT$.EVENTID = ' + event$.eventid"/>
    <log expr="'***** EVENT$.EVENTSOURCE = ' + event$.eventsource"/>
    <log expr="'***** EVENT$.EVENTSOURCETYPE = ' + event$.eventsourcetype"/>
    <exit/>
    </transition>

    <transition event="error.*">
      <log expr="'an error has occurred (' + event$.reason + ')'"/>
              <voxeo:sendemail to="'yourEmail@there.com'"
                        from="'myApp@here.com'"
                        type="'debug'"
                        body=" 'generic error detected ! ' "/>
      <exit/>
    </transition>

  </eventprocessor>   
</ccxml>



Step 5: playing content to the called party

In this hypothetical example, we don't care about user input: Our little application is intended to blast out a message to our recipients, ("Eat at Joe's!", perhaps, or maybe something along the lines of "Welcome to the Auto-Stalker 3000 Service: You are being watched!"), and then hangup. We *could* use a full VoiceXML dialog for this, but why bother, when Voxeo makes it easy to just stay within the confines of the CCXML 1.0 markup? Something else that we will want to take into account is whether or not the called party hangs up on us before we are done playing out notification message: We want to be sure that the called party heard all of the message so we can maybe try again later.

For the first part of the equation, we will use the <dialogstart type="audio/wav"> extension element, which is used exclusively on the Voxeo platform. The beauty of this extension is that we don't need to invoke the VoiceXML interpreter at all, we just specify an audio file to play, and backup TTS, and the CCXML 1.0 interpretr handles the rest for us. And since this is still just a dialog, we want to make sure that we catch when the dialog exits programmatically, so we once again utilize our friend the "dialog.exit" handlers. Note carefully that we set the "state" attribute to this handler so that the only way it can be matched is when the state manager says that we are in the "connected" state.

For the second part of the equation, we add an event handler for the "connection.disconnected" event, and do our usual <log> followed by an <exit>. To review, when a  caller hangs up midway through message delivery, we will expect to receive a "connection.disconnected" event. When the callee listens to the complete message, we can expect to see the "dialog.exit" event pop up.



<?xml version="1.0" encoding="UTF-8"?>
<!-- NOTE THAT WE *MUST* DECLARE THE xmlns ATTRIBUTE -->
<ccxml version="1.0" xmlns:voxeo="http://community.voxeo.com/xmlns/ccxml">

  <var name="myState" expr="'init'"/>
  <var name="myOutboundConnectionID" expr="'someOutboundID'"/>

  <eventprocessor statevariable="myState">

    <transition event="ccxml.loaded" state="init">

    <log expr="'***** EVENT$.NAME = ' + event$.name"/>
    <log expr="'***** EVENT$.SESSIONID = ' + event$.sessionid"/>
    <log expr="'***** EVENT$.PARENT = ' + event$.parent"/>
    <log expr="'***** EVENT$.EVENTID = ' + event$.eventid"/>
    <log expr="'***** EVENT$.EVENTSOURCE = ' + event$.eventsource"/>
    <log expr="'***** EVENT$.EVENTSOURCETYPE = ' + event$.eventsourcetype"/>

    <assign name="myState" expr="'dialing'"/>           
    <createcall dest="'tel:4072223333'" connectionid="myOutboundConnectionID" timeout="'45s'"/>
    </transition>
 
    <transition event="connection.connected" state="dialing">
    <assign name="myState" expr="'connected'"/>

    <log expr="'***** CALL WAS ANSWERED *****'"/>
    <log expr="'***** EVENT$.CONNECTION.CONNECTIONID = ' + event$.connection.connectionid"/>
    <log expr="'***** EVENT$.CONNECTION.PROTOCOL.NAME = ' + event$.connection.protocol.name"/>
    <log expr="'***** EVENT$.CONNECTION.PROTOCOL.VERSION = ' + event$.connection.protocol.version"/>
    <log expr="'***** EVENT$.CONNECTION.STATE = ' + event$.connection.state"/>
    <log expr="'***** EVENT$.CONNECTION.LOCAL = ' + event$.connection.local"/>
    <log expr="'***** EVENT$.CONNECTION.REMOTE = ' + event$.connection.remote"/>
    <log expr="'***** EVENT$.CONNECTION.ORIGINATOR = ' + event$.connection.originator"/>

           
      <dialogstart src="'someFile.wav?text=This is an automated call from the H.A.L. 9000 computer system to commander David Bowman.
                                          This message is to inform you that all pod bay doors are now closed, and re entry
                                          to the Discovery spacecraft is forbidden. This message is too important to jeapordize it to human error.
  This conversation can serve no further purpose. Goodbye.'" 
  type="'audio/wav'"/>

 
    </transition>

    <transition event="connection.failed" state="dialing">
    <log expr="'***** CONNECTION FAILED *****'"/>
    <log expr="'***** EVENT$.NAME = ' + event$.name"/>
    <log expr="'***** EVENT$.SESSIONID = ' + event$.sessionid"/>
    <log expr="'***** EVENT$.PARENT = ' + event$.parent"/>
    <log expr="'***** EVENT$.EVENTID = ' + event$.eventid"/>
    <log expr="'***** EVENT$.EVENTSOURCE = ' + event$.eventsource"/>
    <log expr="'***** EVENT$.EVENTSOURCETYPE = ' + event$.eventsourcetype"/>
    <exit/>
    </transition>

    <transition event="dialog.exit" state="connected">
    <log expr="'***** DIALOG EXIT REACHED *****'"/>
    <log expr="'***** EVENT$.NAME  = ' + event$.name"/>
    <log expr="'***** EVENT$.DIALOGID  = ' + event$.dialogid"/>
    <log expr="'***** EVENT$.CONFERENCEID  = ' + event$.conferenceid"/>
    <log expr="'***** EVENT$.CONNECTIONID  = ' + event$.connectionid"/>
    <log expr="'***** EVENT$.EVENTID  = ' + event$.eventid"/>
    <log expr="'***** EVENT$.EVENTSOURCE  = ' + event$.eventsource"/>
    <log expr="'***** EVENT$.EVENTSOURCETYPE  = ' + event$.eventsourcetype"/>
     
    <log expr="'***** EVENT$.DIALOG.DIALOGID = ' + event$.dialog.dialogid"/>
    <log expr="'***** EVENT$.DIALOG.CONNECTIONID = ' + event$.dialog.connectionid"/>
    <log expr="'***** EVENT$.DIALOG.CONFERENCEID = ' + event$.dialog.conferenceid"/>
    <log expr="'***** EVENT$.DIALOG.TYPE = ' + event$.dialog.type"/>
    <log expr="'***** EVENT$.DIALOG.SRC = ' + event$.dialog.src"/>
    <log expr="'***** EVENT$.DIALOG.INPUT = ' + event$.dialog.input"/>
    <log expr="'***** EVENT$.DIALOG.OUTPUTS = ' + event$.dialog.outputs"/>
    <exit/>
    </transition>

    <transition event="connection.disconnected">
    <log expr="'***** CONNECTION.DISCONNECT EVENT CAUGHT *****'"/>
    <log expr="'***** EVENT$.NAME = ' + event$.name"/>
    <log expr="'***** EVENT$.CONNECTIONID = ' + event$.connectionid"/>
    <log expr="'***** EVENT$.PROTOCOL = ' + event$.protocol"/>
    <log expr="'***** EVENT$.REASON = ' + event$.reason"/>
    <log expr="'***** EVENT$.INFO = ' + event$.info"/>
    <log expr="'***** EVENT$.CONNECTION = ' + event$.connection"/>
    <log expr="'***** EVENT$.EVENTID = ' + event$.eventid"/>
    <log expr="'***** EVENT$.EVENTSOURCE = ' + event$.eventsource"/>
    <log expr="'***** EVENT$.EVENTSOURCETYPE = ' + event$.eventsourcetype"/>
    <log expr="'***** EVENT$.TRIGGER = ' + event$.trigger"/>
    </transition>

    <transition event="error.*">
      <log expr="'an error has occurred (' + event$.reason + ')'"/>
              <voxeo:sendemail to="'yourEmail@there.com'"
                        from="'myApp@here.com'"
                        type="'debug'"
                        body=" 'generic error detected ! ' "/>
      <exit/>
    </transition>

  </eventprocessor>   
</ccxml>


Step 6: place that call


I need a token:
The hard part is now done, and our code is complete. Our next step is to make sure that the Voxeo Support team has provisioned outbound dialing rights to our account, and that we have a token linked our application. Haven't thought of this yet? If you need an application token, and outbound dialing rights, then you will want to open an account ticket to request these special rights. When doing so, remember to specify the platform type, (for this tutorial, you will want to ask for a Prophecy CCXML 1.0 token), and the account ID that you will need. You may, or may not need to verify yourself by providing other credentials prior to receiving a tokenID, so we advise that you read over this link before starting the process so that you can walk into things with eyes wide open.


I have a token:
All we have to do now is  to your CCXML 1.0 application and run it. The next step is to send an http request to our initiator, which will then load and execute your CCXML 1.0 document, which you can do by entering in the following address into your web browser:

http://api.voxeo.net/SessionControl/CCXML10.start?tokenid=<your token id here>


I have a question about tokens:

* "Can I pass in any other parameters to this initial request? How do I get at them on the CCXML 1.0 side?"

You can tack on any user parameters that you want to the request, my little buddy, and we will pass them along to the start URL unchanged. Of course, you have to pull out any querystring parameters on the server side. To illustrate, let's assume that we have the following app URL mapped:

http://myserver.com/myApp.jsp

Let's further assume that your token request looks like this:

http://api.voxeo.net/SessionControl/CCXML10.start?tokenid=abc123?myVar1=foo&amp;myVar2=bar

Our resultant URL that will be loaded will look like this:

http://myserver.com/myApp.jsp?myVar1=foo&amp;myVar2=bar


* "How do I launch more than one call at a time? Do I need a token for each outbound call I want to make?"

Launching multiple outbound calls we cover later in our set of tutorials. The short answer is that we can have a manual "kickoff" page that tells some server side code to loop through a list of numbers, and make multiple http requests to session.voxeo.net where we use the same tokenID, but assign the call destination dynamically. Of course, we can always take this a step further to have a scheduled jobbie whose purpose is to "make 'X' requests to session.voxeo.net at 'Y' hour that access the list of numbers contained in 'Z' database".

* "Can I prank call the local sherriff's office using a token call? That would be funny."

  Sure you can. If you find the idea of explaining yourself to the Local Fuzz as "fun times", that is.


* "Can I dial international numbers? I'd like to be able to call Lithuania for free"

U.S. based outdial only, my friend. While we can justify soaking up the bill for long distance calls to the U.S. we can't do so when the rates are $6.00 USD per minute.

* "Will Voxeo bill me for making outbound calls?"

  If you use your token for legitimate testing purposes, then we say "have fun, and don't worry about being billed". If you want to try and launch a commercial grade application on our Staging network, then chances are, we will find out about it, and be unhappy. At the least, expect us to remove dialing access so that you cannot play Reindeer Games with outbound dialing again. And if someone runs up several thousand minutes with a token intended for test usage, then you might well receive a call from us to explain why.

  Download the CCXML source code!

What we covered:



  ANNOTATIONS: EXISTING POSTS
0 posts - click the button below to add a note to this page

login
  tutorial Connecting Call Legs  |  TOC  |  tutorial Multi-party Conferencing in CCXML 1.0  

© 2008 Voxeo Corporation  |  Voxeo IVR  |  VoiceXML & CCXML IVR Developer Site