Twilio for .NET Developers Part 6: Creating SMS & Voice Application Flows using ASP.NET Web Forms

February 10, 2012
Written by

Twilio Bug Logo

This is a series of blog posts that introduce and walk you through using the different.NET Helper Libraries that Twilio provides.

These libraries simplify using the Twilio REST API for .NET developers, and provide as set of utilities that make it easy to work with TwiML and Twilio Client. Take a look at part one,twothree,  four, and five of the series.

Creating SMS and Voice Application Flows using ASP.NET Web Forms

So far in this series we’ve looked at the tools that Twilio provides .NET developers to make integrating Twilio services into .NET easy.   In the next two posts we will look at some ways to use these tools to create multi-step flows for both SMS and voice applications.

In this first post we will look at how you can create this application using an ASP.NET Web Application, including how you can divide up the logic of the application based on the required call flow.  In the next post we’ll look at creating the same application using ASP.NET MVC 3.

Multi-step Voice Call Work Flows

While it is easy to set up applications that accept respond to a single incoming SMS message or Voice call, most applications require a more complex work flow where your Twilio and your application work together to create more complex application work flows.

One way to think about work flows between your application and Twilio is in terms of a conversation between two parties who do not speak the same language.  The first party is the caller and the second party is your application.  In order to be able to talk to each other, the parties need a translator, which is where Twilio fits in.  Twilio takes the signals from a carrier (like Verizon) and translates that into information your application can use to make decision.  Your application then tells Twilio what to do, and Twilio translates that into actions which are passed back to the carrier.  Below is a flow diagram that shows a simple flow for a voice call using Twilio:

image05

To see how this conversation works in more complex applications, lets create a simple order status application.  This application, known as an Interactive Voice Response (IVR) system allows users to perform a number of different actions including check the status of a package, set up automatic status notifications or speak to customer service agents.   Below is flow chart that shows the different paths the user can take through the system:

image05

On the surface the flow looks fairly complex, but if you break it down there are three main flows through the application:
  • Status Check – Allows the user to look up the status information for a specific package
  • Notification Signup – Allows a user to register to recive text messages when the status of their package changes
  • Customer Service – Allows the user to be connected to a customer service agent.

In this post we will look at how you can create this application using an ASP.NET Web Application, including how you can divide up the logic of the application based on the required call flow.

Setting up the ASP.NET Web Application
Lets get started building the application by creating a new ASP.NET Web Application in Visual Studio.  Once you have the base application created, add the Twilio REST and Twilio TwiML libraries from NuGet as was described in the Nth post of this series.

Next, open the Add New Item dialog and add a new Generic Handler to the project:

image18

The Generic Handler template adds a new generic HTTP handler class to the project.  We’ll use this handler to receive requests from Twilio, process the requests based on our application logic and then return TwiML responses to Twilio.

Creating a HTTP Handler endpoint is a good approach to follow when using an ASP.NET Web Application to build an IVR system.  It provides a way to handle incoming request without the processing overhead required by an ASPX webpage, and simplifies the programming model because you only have to deal with one method: the handler’s ProcessRequest method.

It also provides a way to group related areas of a complex IVR application into a smaller number of endpoints.  For example, in our sample application we will use a single HTTP handler that contains the work flow for our entire IVR.  Another option would be to create a handler for each branch of the call flow.

Of course one problem with combining multiple parts of a call flow into single endpoint is that it requires the application to understand which branch of of the call flow a given request needs to execute.  As you will see as we work through creating the sample application, a common way indicate to the application which part of the call flow to execute is to append parameters to the URLs that we tell Twilio to request as the caller works their way through the call flow.  By evaluating the parameter values, we can change the logic that gets executed by the handler in order to return different TwiML instructions to Twilio.

Now that you have the base project set up you need to deploy it out to a publicly accessible location on the Internet.  For example you could deploy the application to a traditional hosting provider, or a cloud hosting provider like Windows Azure or AppHarbor.

Finally, once you have the project deployed, you configure Twilio to know about the application endpoint.

Setting up Twilio

Before we get started writing the application, lets go set up a phone number for your users to call and tell Twilio about the endpoint you created in the previous section.

In this post we walk you through purchasing and and configuring a new 10 digit phone number, however buying a number is not a requirement to utilize Twilio.  Instead of purchasing a new number, you can use the free sandbox number that is included with every Twilio account.  If you decide to use the sandbox number, then users of this system would need to know and enter the sandbox PIN code after dialing the sandbox number.  Additionally in order to send text messages you will need to verify an outgoing number with Twilio, which you can do from your account dashboard.

Start by loading twilio.com in your browser and logging into your account.  Once you are logged in, load the account dashboard and open the Numbers tab and click the Buy Number button:

image10

Locate a number and click the Buy button.  Twilio will let you know that it costs 1$ per month for a standard long number and ask you to confirm your purchase.  Once you confirm, Twilio will let you configure your new number:

image01

To configure the number you need to enter a URL into the Voice URL text box.  This URL represents the point in your application that Twilio can talk to when it needs to know how to handle an incoming phone call.
image03

Enter the URL that points to the deployed HTTP Handler you created in the previous section and click the Save button.  When Twilio receives an incoming call it will make an HTTP call to this URL with the call details.

Call Greeting

Now that we have the basic infrastructure set up we can get to work adding the work flow logic to our application.  When a call is made to our number, Twilio will answer and tell our application about it by making an HTTP request to the Voice Handler URL.  When that request is made we want to tell Twilio to play a greeting sound file, which we will do using the Twilio TwiML Library as shown below:

 public void ProcessRequest(HttpContext context)
{
     context.Response.ContentType = "application/xml";

     TwilioResponse twiml = new TwilioResponse();
     twiml.Play("http://example.com/greeting.mp3");

     context.Response.Write(twiml.ToString());
}

In the ProcessRequest method we set the Response.ContentType property to application/xml to tell Twilio that the response is in an XML format.  Next we call the Play method which adds a TwiML verb to the response and provide the sound file we want to play.  Finally we output the string value of the TwilioResponse object to the Response stream.

You can test the app by calling the phone number we bought earlier.  You should hear the audio file play, then the call will disconnect.

Next we want to add a menu to our application that allows users to select what they want to do.  We want the menu to be separate from the greeting so that later we can allow users can return to the main menu without having to hear the greeting repeated.

Adding the menu will create a new branch in the applications work flow, so in order to know what TwiML to return we need to add logic to the ProcessRequest method that looks for a request parameter named src.  We will use the value (or lack thereof) of the src parameter to help tell us what branch of the logic should handle the request.

The code below shows how the ProcessRequest method has been modified to include the menu logic:

context.Response.ContentType = "application/xml";

TwilioResponse twiml = new TwilioResponse();

string source = context.Request["src"] ?? String.Empty;

if (source)
{
     twiml.Play("http://example.com/greeting.mp3");
     twiml.Redirect("http://example.com/status.ashx?src=mainmenu");
}
else
{
     twiml.Say("To check the status of an order, press one");
     twiml.Say("To receive automated status updates via text message, press two");
     twiml.Say("To speak with a customer service representative, press three");
}

context.Response.Write(twiml.ToString());

The first modification we made is to add conditional logic to check to see if the src request parameter exists.  When a call is initially made, that parameter won’t be in the request from Twilio, so the conditional will evaluate to true and the TwiML used to play the greeting will be returned.

Also notice that now, in addition to the verb being used, we are using the Redirect method to add a verb to the greeting TwiML response.  The verb directs Twilio to continue processing the call using a new URL.  In this case the URL we’ve provided appends the src parameter with the value ‘mainmenu’.

When Twilio calls the redirect URL, the conditional logic will now fail and the app will fall into the else block.  In that block we are using the Say method to add the main menu text.

You can test the modifications to the app by dialing the phone number again.  This time you should hear the greeting play, followed immediately by the main menu content.

Accepting Menu Input

Once we’ve added the main menu we need to add a way to listen for the selection that the user makes.  In IVR applications users make selections by pressing the keys on their phones, which causes the device to emit a DTMF tone.  By including the verb in our TwiML we can instruct Twilio to listen for DTMF tones and tell us when the user enters them.  The code below shows how we can add the verb to our main menu:

twiml.BeginGather(new {
     action = "http://example.com/status.ashx?src=mainmenu",
     numDigits = "1" });
twiml.Say("To check the status of an order, press one");
twiml.Say("To receive automated status updates via text message, press two");
twiml.Say("To speak with a customer service representative, press three");
twiml.EndGather();
twiml.Say("Goodbye");
twiml.Hangup();

Notice how we start the gather before the first call to the Say method by using the BeginGather method, then end it after the last Say using the EndGather method.  By surrounding the verbs with a verb, we are telling Twilio to listen for input while each Say is executed, allowing someone to press a key to move on before the prompt finishes.

Also notice that we are using an anonymous type to set two attributes on the Gather verb.  The first, the action attribute, tells Twilio what URL to request once the Gather completes.  This URL is called the action handler.  The second attribute tells Twilio how many digits to gather from the user before making a request to the action handler.

Based on the TwiML we are returning to Twilio, during the call if DTMF tones are detected during a , Twilio will convert the tones into the corresponding numeric values and call the action handler, passing the converted values as a request parameter named ‘Digits’.  If no input is detected, then Twilio will continue to execute the remaining TwiML, in our case executing the remaining and verbs.

The URL thats been set for the Gather verbs action handler is the same URL as the one used for the redirect earlier, but this time when Twilio makes the request to that URL, the request will also include the Digits request a parameter which we can use to determine which branch in the apps work flow to use.

The code below shows how we can modify the code to leverage the Digits parameter to change the flow of our app:

string source = context.Request["src"] ?? String.Empty;
string digits = context.Request["Digits"] ?? String.Empty;

if (digits == String.Empty)
{
     twiml.BeginGather(new {
        action = "http://example.com/status.ashx?src=mainmenu",
        numDigits = "1" });
     twiml.Say("To check the status of an order, press one");
     twiml.Say("To receive automated status updates via text messa     ge, press two");
     twiml.Say("To speak with a customer service representative, p     ress three");
     twiml.EndGather();
     twiml.Say("Goodbye");
     twiml.Hangup();
}
if (digits == "1") { /* menu option one */ }
if (digits == "2") { /* menu option two */ }
if (digits == "3") { /* menu option three */ }

Using this logic if the Digits parameter is empty, we follow the mainmenu logic branch.  If the Digits parameter exists we use its value to determine which work flow branch the app should follow.

The first option in the menu allows the user to check on the status of a package.  In our application, this will be implemented as a two step process.  First, we need to allow the user to enter in a package ID number to get status information for.  Second, we need to look up the package using the ID and tell the status to the end user.

As we discussed previously, the verb lets us tell Twilio to listen for user input, so we are going to use it to let the user input their package ID.

 if (digits == "1")
{
     twiml.BeginGather(new {
       action = "http://example.com/status.ashx?src=lookup",
       numDigits = "9" });
     twiml.Say("To retrieve status information, please enter your      9 digit package ID");
     twiml.EndGather();
     twiml.Say("I'm sorry, I didn't hear the ID.");
     twiml.Redirect(
       string.Format(
         "http://example.com/status.ashx?src={0}&Digits={1}",
         source, digits)
);
}


As before, we are defining an action handler value for the Gather verb, however this time we are using a new value for the src parameter: ‘lookup’.  We can now add to our application logic that checks the value of the src parameter and changes the logic flow based on its value:

 string source = context.Request["src"] ?? String.Empty;
string digits = context.Request["Digits"] ?? String.Empty;

if (source == "mainmenu")
 {
     /* main menu logic ... removed for clarity */
 }

if (source == "lookup")
 {
     var order = Orders.FindById(digits);
     if (order != null)
     {
     twiml.Say(string.Format("Your package is currently, {0}.", or     der.Status));
     }
     else
     {
     twiml.Say("I'm sorry.  We were unable to find a package with      that ID.");
     }
 }

You can see in the code above that we’ve added two blocks of logic that will execute based on the value if the source variable.  The first executes when the source value is ‘mainmenu’, the second when the value is ‘lookup’.  Later we will add more logic blocks based on other values later. The Lookup block is responsible for locating the package and returning its status to the user and executes after the user has entered their package ID. At this point our application should now allow user to hear the menu options, select the option to check the status of their package and then hear the status.  You can test the application by dialing your number and pressing “1” when the main menu plays.

The next major option available to users in our application is to register to receive notifications via SMS when their package status changes.  If you look at the application flow diagram that was shown at the beginning of this post you can probably tell that the work flow for this branch is virtually identical to the branch for checking package status, and therefore the code to add this part of the application is virtually identical to the code to check a package status. The primary difference is that we will add anew value for the src parameter which tells our application to use the Package Notification logic branch.  A trimmed down version of the code for the Package Notification branch is shown below:

string source = context.Request["src"] ?? String.Empty;
string digits = context.Request["Digits"] ?? String.Empty;

if (source == "mainmenu")
{
    if (digits == String.Empty) { /* menu option one */ }
    if (digits == "1") { /* menu option one */ }

    if (digits == "2")
    {
        twiml.BeginGather(new {
             action = "http://example.com/status.ashx?src=register;,
             numDigits = ""; });
        twiml.Say(";To register to retrieve status information, " +
             ";please enter your 9 digit package ID";);
        twiml.EndGather();
        twiml.Say("I'm sorry, I didn't hear the ID.";);
        twiml.Redirect(
             string.Format(
                  "http://example.com/status.ashx?src={0}&Digits={1}";,
                  source, digits)
             );
    }

    if (digits == "3") { /* menu option three */ }
}

if (source=="register") {

    var order = Orders.FindById(digits);
    if (order!=null)
    {
        //save the registration
        Notifications.Create(digits, context.Request["From"]);

        twiml.Say("Thank you for registering to receive notifications.");
        twiml.Say("To disable notifications you can send the word Stop to " + "555-555-5555 at any time.");
    }
    else
    {
        twiml.Say("I'm sorry.  We were unable to find a package with that ID.");
    }
}

As you can see, instead of the lookup value used to get the package status, we are now checking for the value ‘register’ which tells the application that the user wants to register to receive status information via text messages.

As an example of how this might work in a real application, our sample uses the static Create method on the mock Notifications object to create a new notification using the package ID entered by the user and their phone number (from the ‘From’ request parameter).  In a real application this notification could be saved to a database to be used by a separate monitoring application.

As with the previous sections you can test the application by calling your number.  You should now be able to both check the status of an package as well as select the second menu option and register to receive status updates.

The last option that we are giving the users of our application is to connect to a customer service representative.  To do this we want our application to call out to our customer service center and bridge the customer and agent together.

To do that our application detects the selection of the third option, and returns the TwiML instructing Twilio to connect to another call

string source = context.Request["src"] ?? String.Empty;
string digits = context.Request["Digits"] ?? String.Empty;

if (source == "mainmenu")
{
    if (digits == String.Empty) { /* menu option one */ }
    if (digits == "1") { /* menu option one */ }
    if (digits == "2") { /* menu option two */ }

    if (digits=="3") {
        twiml.Say("One moment while we connect you");
        twiml.Dial("+15555555555");
   }
}

Here we are using the Dial verb to tell Twilio to create a new call to the provided number.  Twilio will automatically bridge the existing call with the new call, allowing the customer to speak with an agent.

If you look at the call flow diagram from the beginning of this post you will see that there are several places where the caller can double back on themselves to either re-enter a package ID, or to return to the main menu.  The logic to do this is basically the same for both flow branches.

Lets add the menu options to the lookup branch first.

if (source=="lookup") {

     //using the digits entered by the end user, look up the status
     var order = Orders.FindById(digits);
     if (order != null)
     {
          twiml.Say(string.Format("Your package is currently, {0}. ", order.Status));
     }
     else
     {
          twiml.Say("I'm sorry.  We were unable to find a package with that ID.");
     }

twiml.BeginGather(new {
      action = "http://example.com/status.ashx?src=redirect&action=lookup", numDigits = "1" });
twiml.Say("To look up another package, press one");
twiml.Say("To return to the main menu, press two");
twiml.EndGather();
twiml.Say("Goodbye");
twiml.Hangup();
}

You can see that we’ve added the code to create our menu to the end of the lookup block. Again using the verb, we let the user tell us what they want to do once the lookup completes, which in this case is to either lookup another package or to return to the main menu.

In order to process their input we need to create another logic branch that will be run when the Gather completes.  We indicate this new branch as we have done before, by using a new value for the src parameter called ‘redirect’.

Additionally we are sending another parameter called ‘action’.  We’ll use this additional parameter in the redirect branch in order to determine if the user is coming from the lookup branch or the register branch.

The code below shows the code we use for the redirect branch:

if (source=="redirect")
{
     if (digits=="1")
     {
         if (context.Request["action"] == "lookup")
         {
            twiml.Redirect(
                "http://example.com/status.ashx?src=mainmenu&Digits=1");
         }
         if (context.Request["action"] == "register")
         {
            twiml.Redirect(
                "http://example.com/status.ashx?src=mainmenu&Digits=2");
         }
     }
     if (digits=="2")
     {
            twiml.Redirect("http://example.com/status.ashx?src=mainmenu");
     }
}

The logic is fairly straight forward.  If the user indicates enters “1”, we determine if they are coming from the lookup branch or the register branch and simply redirect the call to the correct place in the app.

If the user enters “2”, we redirect them back to the main menu.

Summary

As you can see, creating more complex call flows using an  HTTP Handler in and ASP.NET Web Application is easy, but does require a bit of planning up front in order to design and implement the work flow.  Once you understand the call flow, implementing the code is quite simple.

In the next post we will look at how you can implement the same application using the ASP.NET MVC 3 Framework.

Read more of the .NET series:
Twilio for .NET Developers Part 1: Introducing the Helper Libraries 
Twilio for .NET Developers Part 2: Adding Twilio Helper Libraries to your Project
Twilio for .NET Developers Part 3: Using the Twilio REST API Helper Library
Twilio for .NET Developers Part 4: Using the Twilio.TwiML Helper Library
Twilio for .NET Developers Part 5: Twilio Client, MVC and WebMatrix Helper Libraries

This series is written by Twilio Developer Evangelist Devin Rader. As a co-founder of the St. Louis .NET User Group, a current board member of the Central New Jersey .NET User Group and a former INETA board member, he’s an active supporter of the .NET developer community. He’s also the co-author or technical editor of numerous books on .NET including Wrox’s Professional Silverlight 4 and Wrox’s Professional ASP.NET 4. Follow Devin on Twitter @devinrader