Skip to contentSkip to navigationSkip to topbar
On this page

Gather User Input via Keypad (DTMF Tones) in Ruby


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 Ruby 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 the Ruby language version 2.0.0 or higher, and make use of the following libraries:

Let's get started!


Set up your Sinatra application to receive incoming phone calls

set-up-your-sinatra-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)Link to code sample: Use <Gather> to collect user input via the keypad (DTMF tones)
1
# Get twilio-ruby from twilio.com/docs/ruby/install
2
require 'sinatra'
3
require 'twilio-ruby'
4
5
post '/voice' do
6
Twilio::TwiML::VoiceResponse.new do |r|
7
r.gather numDigits: 1 do |g|
8
g.say(message: 'For sales, press 1. For support, press 2.')
9
end
10
r.redirect('/voice')
11
end.to_s
12
end

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 userLink to code sample: Branch your call logic based on the digits sent by the user
1
# Get twilio-ruby from twilio.com/docs/ruby/install
2
require 'sinatra'
3
require 'twilio-ruby'
4
5
post '/voice' do
6
if params['Digits']
7
case params['Digits']
8
when '1'
9
Twilio::TwiML::VoiceResponse.new do |r|
10
r.say(message: 'You selected sales. Good for you!')
11
end.to_s
12
when '2'
13
Twilio::TwiML::VoiceResponse.new do |r|
14
r.say(message: 'You need support. We will help!')
15
end.to_s
16
else
17
Twilio::TwiML::VoiceResponse.new do |r|
18
r.say(message: 'Sorry, I don\'t understand that choice.')
19
r.pause
20
r.gather(numDigits: 1) do |g|
21
g.say(message: 'For sales, press 1. For support, press 2.')
22
end
23
r.redirect('/voice')
24
end.to_s
25
end
26
else
27
Twilio::TwiML::VoiceResponse.new do |r|
28
r.gather(numDigits: 1) do |g|
29
g.say(message: 'For sales, press 1. For support, press 2.')
30
end
31
r.redirect('/voice')
32
end.to_s
33
end
34
end

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 userLink to code sample: Add another route to handle the input from the user
1
# Get twilio-ruby from twilio.com/docs/ruby/install
2
require 'sinatra'
3
require 'twilio-ruby'
4
5
post '/voice' do
6
Twilio::TwiML::VoiceResponse.new do |r|
7
r.gather(numDigits: 1, action: '/gather') do |g|
8
g.say(message: 'For sales, press 1. For support, press 2.')
9
end
10
11
# If the user doesn't enter input, loop
12
r.redirect('/voice')
13
end.to_s
14
end
15
16
post '/gather' do
17
if params['Digits']
18
case params['Digits']
19
when '1'
20
Twilio::TwiML::VoiceResponse.new do |r|
21
r.say(message: 'You selected sales. Good for you!')
22
end.to_s
23
when '2'
24
Twilio::TwiML::VoiceResponse.new do |r|
25
r.say(message: 'You need support. We will help!')
26
end.to_s
27
else
28
Twilio::TwiML::VoiceResponse.new do |r|
29
r.say(message: 'Sorry, I don\'t understand that choice.')
30
r.pause
31
r.redirect('/voice')
32
end.to_s
33
end
34
else
35
Twilio::TwiML::VoiceResponse.new do |r|
36
r.redirect('/voice')
37
end.to_s
38
end
39
end

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

Need some help?

Terms of service

Copyright © 2024 Twilio Inc.