Skip to contentSkip to navigationSkip to topbar
Rate this page:
On this page

Gather User Input via Keypad (DTMF Tones) in C#


In this guide, we'll show you how to gather user input during a phone call through the phone's keypad (using DTMF(link takes you to an external page) tones) in your ASP.NET application. By applying this technique, you can create interactive voice response (IVR(link takes you to an external page)) 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(link takes you to an external page). If you need help creating a new ASP.NET MVC project, check out our guide on the topic.

Let's get started!


Set up your ASP.NET MVC application to receive incoming phone calls

set-up-your-aspnet-mvc-application-to-receive-incoming-phone-calls page anchor

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.


Collect user input with the <Gather> TwiML verb

collect-user-input-with-the-gather-twiml-verb page anchor

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.

Use <Gather> to collect user input via the keypad (DTMF tones)

use-gather-to-collect-user-input-via-the-keypad-dtmf-tones page anchor

This example returns TwiML containing a <Say> tag nested within a <Gather> tag. The user will be prompted to choose sales or support.

C#

_25
// In Package Manager, run:
_25
// Install-Package Twilio.AspNet.Mvc -DependencyVersion HighestMinor
_25
_25
using System.Web.Mvc;
_25
using Twilio.AspNet.Mvc;
_25
using Twilio.AspNet.Common;
_25
using Twilio.TwiML;
_25
using System;
_25
_25
public class VoiceController : TwilioController
_25
{
_25
[HttpPost]
_25
public TwiMLResult Index()
_25
{
_25
var response = new VoiceResponse();
_25
_25
// Use the <Gather> verb to collect user input
_25
response.Gather(numDigits: 1)
_25
.Say("For sales, press 1. For support, press 2.");
_25
// If the user doesn't enter input, loop
_25
response.Redirect(new Uri("/voice", UriKind.Relative));
_25
_25
return TwiML(response);
_25
}
_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.

Branch your call logic based on the digits sent by the user

branch-your-call-logic-based-on-the-digits-sent-by-the-user page anchor

By default, <Gather> will 'loop' on the current TwiML URL, requesting the same URL every time input is entered.

C#

_50
// In Package Manager, run:
_50
// Install-Package Twilio.AspNet.Mvc -DependencyVersion HighestMinor
_50
_50
using System.Web.Mvc;
_50
using Twilio.AspNet.Mvc;
_50
using Twilio.AspNet.Common;
_50
using Twilio.TwiML;
_50
using System;
_50
_50
public class VoiceController : TwilioController
_50
{
_50
[HttpPost]
_50
public TwiMLResult Index(VoiceRequest request)
_50
{
_50
var response = new VoiceResponse();
_50
_50
if (!string.IsNullOrEmpty(request.Digits))
_50
{
_50
switch (request.Digits)
_50
{
_50
case "1":
_50
response.Say("You selected sales. Good for you!");
_50
break;
_50
case "2":
_50
response.Say("You need support. We will help!");
_50
break;
_50
default:
_50
response.Say("Sorry, I don't understand that choice.").Pause();
_50
RenderMainMenu(response);
_50
break;
_50
}
_50
}
_50
else
_50
{
_50
// If no input was sent, use the <Gather> verb to collect user input
_50
RenderMainMenu(response);
_50
}
_50
_50
return TwiML(response);
_50
}
_50
_50
private static void RenderMainMenu(VoiceResponse response)
_50
{
_50
response.Gather(numDigits: 1)
_50
.Say("For sales, press 1. For support, press 2.");
_50
_50
// If the user doesn't enter input, loop
_50
response.Redirect(new Uri("/voice", UriKind.Relative));
_50
}
_50
}


Specify an action to take after user input is collected

specify-an-action-to-take-after-user-input-is-collected page anchor

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.

Add another route to handle the input from the user

add-another-route-to-handle-the-input-from-the-user page anchor

This technique creates a separate route to handle user input.

C#

_56
// In Package Manager, run:
_56
// Install-Package Twilio.AspNet.Mvc -DependencyVersion HighestMinor
_56
_56
using System.Web.Mvc;
_56
using Twilio.AspNet.Mvc;
_56
using Twilio.AspNet.Common;
_56
using Twilio.TwiML;
_56
using System;
_56
_56
public class VoiceController : TwilioController
_56
{
_56
[HttpPost]
_56
public TwiMLResult Index()
_56
{
_56
var response = new VoiceResponse();
_56
response.Gather(numDigits: 1, action: new Uri("/voice/gather", UriKind.Relative))
_56
.Say("For sales, press 1. For support, press 2.");
_56
_56
// If the user doesn't enter input, loop
_56
response.Redirect(new Uri("/voice", UriKind.Relative));
_56
_56
return TwiML(response);
_56
}
_56
_56
_56
[HttpPost]
_56
public TwiMLResult Gather(VoiceRequest request)
_56
{
_56
var response = new VoiceResponse();
_56
_56
// If the user entered digits, process their request
_56
if (!string.IsNullOrEmpty(request.Digits))
_56
{
_56
switch (request.Digits)
_56
{
_56
case "1":
_56
response.Say("You selected sales. Good for you!");
_56
break;
_56
case "2":
_56
response.Say("You need support. We will help!");
_56
break;
_56
default:
_56
response.Say("Sorry, I don't understand that choice.").Pause();
_56
response.Redirect(new Uri("/voice", UriKind.Relative));
_56
break;
_56
}
_56
}
_56
else
_56
{
_56
// If no input was sent, redirect to the /voice route
_56
response.Redirect(new Uri("/voice", UriKind.Relative));
_56
}
_56
_56
return TwiML(response);
_56
}
_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.


Rate this page: