In this guide, we'll show you how to gather user input during a phone call through the phone's keypad (using DTMF tones) in your ASP.NET application. By applying this technique, you can create interactive voice response (IVR) systems and other phone based interfaces for your users. The code snippets in this guide are written using modern C# language features and require the .NET Framework version 4.5 or higher. They also make use of the Twilio C# SDK. If you need help creating a new ASP.NET MVC project, check out our guide on the topic.
Let's get started!
This guide assumes you have already set up your web application to receive incoming phone calls. If you still need to complete this step, check out this guide. It should walk you through the process of buying a Twilio number and configuring your app to receive incoming calls from it.
The <Gather> TwiML verb allows us to collect input from the user during a phone call. Gathering user input through the keypad is a core mechanism of Interactive Voice Response (IVR) systems where users can press "1" to connect to one menu of options and press "2" to reach another. These prompts can be accompanied by voice prompts to the caller, using the TwiML <Say> and <Play> verbs. In this example, we will prompt the user to enter a number to connect to a certain department within our little IVR system.
1// In Package Manager, run:2// Install-Package Twilio.AspNet.Mvc -DependencyVersion HighestMinor34using System.Web.Mvc;5using Twilio.AspNet.Mvc;6using Twilio.AspNet.Common;7using Twilio.TwiML;8using System;910public class VoiceController : TwilioController11{12[HttpPost]13public TwiMLResult Index()14{15var response = new VoiceResponse();1617// Use the <Gather> verb to collect user input18response.Gather(numDigits: 1)19.Say("For sales, press 1. For support, press 2.");20// If the user doesn't enter input, loop21response.Redirect(new Uri("/voice", UriKind.Relative));2223return TwiML(response);24}25}
If the user doesn't enter any input after a configurable timeout, Twilio will continue processing the TwiML in the document to determine what should happen next in the call. When the end of the document is reached, Twilio will hang up the call. In the above example, we use the <Redirect> verb to have Twilio request the same URL again, repeating the prompt for the user
If a user were to enter input with the example above, the user would hear the same prompt over and over again regardless of what button you pressed. By default, if the user does enter input in the <Gather>, Twilio will send another HTTP request to the current webhook URL with a POST parameter containing the Digits entered by the user. In the sample above, we weren't handling this input at all. Let's update that logic to also process user input if it is present.
1// In Package Manager, run:2// Install-Package Twilio.AspNet.Mvc -DependencyVersion HighestMinor34using System.Web.Mvc;5using Twilio.AspNet.Mvc;6using Twilio.AspNet.Common;7using Twilio.TwiML;8using System;910public class VoiceController : TwilioController11{12[HttpPost]13public TwiMLResult Index(VoiceRequest request)14{15var response = new VoiceResponse();1617if (!string.IsNullOrEmpty(request.Digits))18{19switch (request.Digits)20{21case "1":22response.Say("You selected sales. Good for you!");23break;24case "2":25response.Say("You need support. We will help!");26break;27default:28response.Say("Sorry, I don't understand that choice.").Pause();29RenderMainMenu(response);30break;31}32}33else34{35// If no input was sent, use the <Gather> verb to collect user input36RenderMainMenu(response);37}3839return TwiML(response);40}4142private static void RenderMainMenu(VoiceResponse response)43{44response.Gather(numDigits: 1)45.Say("For sales, press 1. For support, press 2.");4647// If the user doesn't enter input, loop48response.Redirect(new Uri("/voice", UriKind.Relative));49}50}
You may want to have an entirely different endpoint in your application handle the processing of user input. This is possible using the "action" attribute of the <Gather> verb. Let's update our example to add a second endpoint that will be responsible for handling user input.
1// In Package Manager, run:2// Install-Package Twilio.AspNet.Mvc -DependencyVersion HighestMinor34using System.Web.Mvc;5using Twilio.AspNet.Mvc;6using Twilio.AspNet.Common;7using Twilio.TwiML;8using System;910public class VoiceController : TwilioController11{12[HttpPost]13public TwiMLResult Index()14{15var response = new VoiceResponse();16response.Gather(numDigits: 1, action: new Uri("/voice/gather", UriKind.Relative))17.Say("For sales, press 1. For support, press 2.");1819// If the user doesn't enter input, loop20response.Redirect(new Uri("/voice", UriKind.Relative));2122return TwiML(response);23}242526[HttpPost]27public TwiMLResult Gather(VoiceRequest request)28{29var response = new VoiceResponse();3031// If the user entered digits, process their request32if (!string.IsNullOrEmpty(request.Digits))33{34switch (request.Digits)35{36case "1":37response.Say("You selected sales. Good for you!");38break;39case "2":40response.Say("You need support. We will help!");41break;42default:43response.Say("Sorry, I don't understand that choice.").Pause();44response.Redirect(new Uri("/voice", UriKind.Relative));45break;46}47}48else49{50// If no input was sent, redirect to the /voice route51response.Redirect(new Uri("/voice", UriKind.Relative));52}5354return TwiML(response);55}56}
The action attribute takes a relative URL which would point to another route your server is capable of handling. Now, instead of conditional logic in a single route, we use actions and redirects to handle our call logic with separate code paths.
If you're building call center type applications in C# and ASP.NET, you might enjoy stepping through full sample applications that implement a full IVR system.