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

Gather User Input via Keypad (DTMF Tones) in Node.js


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 Node.js 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 JavaScript language features in Node.js version 6 or higher, and make use of the following node modules:

Let's get started!


Set up your Node.js Express application to receive incoming phone calls

set-up-your-nodejs-express-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.


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 <Gather> tag. The user will be prompted to choose sales or support.

Node.js

_30
const express = require('express');
_30
const VoiceResponse = require('twilio').twiml.VoiceResponse;
_30
const urlencoded = require('body-parser').urlencoded;
_30
_30
const app = express();
_30
_30
// Parse incoming POST params with Express middleware
_30
app.use(urlencoded({ extended: false }));
_30
_30
// Create a route that will handle Twilio webhook requests, sent as an
_30
// HTTP POST to /voice in our application
_30
app.post('/voice', (request, response) => {
_30
// Use the Twilio Node.js SDK to build an XML response
_30
const twiml = new VoiceResponse();
_30
_30
// Use the <Gather> verb to collect user input
_30
const gather = twiml.gather({ numDigits: 1 });
_30
gather.say('For sales, press 1. For support, press 2.');
_30
_30
// If the user doesn't enter input, loop
_30
twiml.redirect('/voice');
_30
_30
// Render the response as XML in reply to the webhook request
_30
response.type('text/xml');
_30
response.send(twiml.toString());
_30
});
_30
_30
// Create an HTTP server and listen for requests on port 3000
_30
console.log('Twilio Client app HTTP server running at http://127.0.0.1:3000');
_30
app.listen(3000);

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.

Node.js

_37
app.post('/voice', (request, response) => {
_37
// Use the Twilio Node.js SDK to build an XML response
_37
const twiml = new VoiceResponse();
_37
_37
/** helper function to set up a <Gather> */
_37
function gather() {
_37
const gatherNode = twiml.gather({ numDigits: 1 });
_37
gatherNode.say('For sales, press 1. For support, press 2.');
_37
_37
// If the user doesn't enter input, loop
_37
twiml.redirect('/voice');
_37
}
_37
_37
// If the user entered digits, process their request
_37
if (request.body.Digits) {
_37
switch (request.body.Digits) {
_37
case '1':
_37
twiml.say('You selected sales. Good for you!');
_37
break;
_37
case '2':
_37
twiml.say('You need support. We will help!');
_37
break;
_37
default:
_37
twiml.say("Sorry, I don't understand that choice.");
_37
twiml.pause();
_37
gather();
_37
break;
_37
}
_37
} else {
_37
// If no input was sent, use the <Gather> verb to collect user input
_37
gather();
_37
}
_37
_37
// Render the response as XML in reply to the webhook request
_37
response.type('text/xml');
_37
response.send(twiml.toString());
_37
});


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.

Node.js

_49
// Create a route that will handle Twilio webhook requests, sent as an
_49
// HTTP POST to /voice in our application
_49
app.post('/voice', (request, response) => {
_49
// Use the Twilio Node.js SDK to build an XML response
_49
const twiml = new VoiceResponse();
_49
_49
const gather = twiml.gather({
_49
numDigits: 1,
_49
action: '/gather',
_49
});
_49
gather.say('For sales, press 1. For support, press 2.');
_49
_49
// If the user doesn't enter input, loop
_49
twiml.redirect('/voice');
_49
_49
// Render the response as XML in reply to the webhook request
_49
response.type('text/xml');
_49
response.send(twiml.toString());
_49
});
_49
_49
// Create a route that will handle <Gather> input
_49
app.post('/gather', (request, response) => {
_49
// Use the Twilio Node.js SDK to build an XML response
_49
const twiml = new VoiceResponse();
_49
_49
// If the user entered digits, process their request
_49
if (request.body.Digits) {
_49
switch (request.body.Digits) {
_49
case '1':
_49
twiml.say('You selected sales. Good for you!');
_49
break;
_49
case '2':
_49
twiml.say('You need support. We will help!');
_49
break;
_49
default:
_49
twiml.say("Sorry, I don't understand that choice.");
_49
twiml.pause();
_49
twiml.redirect('/voice');
_49
break;
_49
}
_49
} else {
_49
// If no input was sent, redirect to the /voice route
_49
twiml.redirect('/voice');
_49
}
_49
_49
// Render the response as XML in reply to the webhook request
_49
response.type('text/xml');
_49
response.send(twiml.toString());
_49
});

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 Node.js, you might enjoy stepping through full sample applications written in Node.js.


Rate this page: