How to Gather User Input (IVR)
In this guide we will show you how to collect digits inputted by a user on their phone's keypad for a period of time. Please ensure you have followed our earlier guide on how to make an outbound call with Bandwidth.
You can gather a user's input to create an interactive voice response (IVR) system.
Gathering User Input
The <Gather>
verb is used to collect digits inputted by the user.
The gather is terminated when one of these conditions is met:
- The user presses any one of the
terminatingDigits
(if specified/) - The user has pressed at least one key and more than
interDigitTimeout
seconds have elapsed - Any nested audio has ended and
firstDigitTimeout
seconds have elapsed without the user pressing any digits - The user presses
maxDigits
digits - If the
gatherUrl
attribute is specified, the Gather event is sent to thegatherUrl
upon completion of the gather. BXML returned by that callback are then executed. IfgatherUrl
is specified, verbs following the<Gather>
will be ignored.
If no gatherUrl
attribute is specified, the gathered digits are discarded and execution of verbs following the <Gather>
continues.
- XML
- Java
- C#
- Ruby
- NodeJS
- Python
- PHP
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<SpeakSentence>Input a digit to hear it read back to you.</SpeakSentence>
<Gather gatherUrl="/digitsCallback" maxDigits="1"/>
</Response>
And endpoint /digitsCallback
should return:
//NOTE: This section of the application may look different depending on how your endpoints receive callback events
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<SpeakSentence>You input the number {digit}</SpeakSentence>
</Response>
**Note: The endpoint headers are pseudocoded. Your implementation will look different
@POST /gatherDigits endpoint
public String gatherDigits() {
SpeakSentence speakSentence = new SpeakSentence("Input a digit to hear it read back to you.");
Gather gather = new Gather().builder()
.gatherUrl("/digitsCallback")
.maxDigits(1)
.build();
Response response = new Response().builder().build()
.withVerbs(speakSentence, gather);
String bxml = response.toBXML();
return bxml;
}
@POST /digitsCallback endpoint
public String digitsCallback(VoiceCallback callback) {
//NOTE: This section of the application may look different depending on how your endpoints receive callback events
String digits = callback.getDigits();
String message = "You input the number " + digits;
SpeakSentence speakSentence = new SpeakSentence(message);
Response response = new Response()
.with(speakSentence);
String bxml = response.toBXML();
return bxml;
}
**Note: The endpoint headers are pseudocoded. Your implementation will look different
using System;
using System.Collections.Generic;
using System.IO;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
@POST [/gatherDigits]
public ActionResult gatherDigits() {
SpeakSentence speakSentence = new SpeakSentence
{
Sentence = "Input a digit to hear it read back to you.",
};
Gather gather = new Gather
{
GatherUrl = "/digitsCallback",
MaxDigits = 1
};
Response response = new Response();
response.Add(speakSentence);
response.Add(gather);
return new OkObjectResult(response.ToBXML());
}
@POST [/digitsCallback]
public ActionResult digitsCallback() {
//NOTE: This section of the application may look different depending on how your endpoints receive callback events
using var reader = new StreamReader(Request.Body, Encoding.UTF8);
var body = await reader.ReadToEndAsync();
var callback = JObject.Parse(body);
var digits = (string)callback["digits"];
string message = "You input the number " + digits;
SpeakSentence speakSentence = new SpeakSentence
{
Sentence = message,
};
Response response = new Response();
response.Add(speaksentence);
return new OkObjectResult(response.ToBXML());
}
**Note: The endpoint headers are pseudocoded. Your implementation will look different
post '/gatherDigits' do
speak_sentence = Bandwidth::Bxml::SpeakSentence.new('Input a digit to hear it read back to you.')
gather = Bandwidth::Bxml::Gather.new([speak_sentence], {
gather_url: '/digitsCallback',
max_digits: 1,
})
response = Bandwidth::Bxml::Response.new([gather])
return response.to_bxml
end
post '/digitsCallback' do
data = JSON.parse(request.body.read)
speak_sentence = Bandwidth::Bxml::SpeakSentence.new(data['digits'])
response = Bandwidth::Bxml::Response.new([speak_sentence])
return response.to_bxml
end
**Note: The endpoint headers are pseudocoded. Your implementation will look different
@POST ['/gatherDigits'] (req, res) => {
const speakSentence = new Bxml.SpeakSentence('Input a digit to hear it read back to you.');
const gather = new Bxml.Gather({
gatherUrl: "/digitsCallback",
maxDigits: 1,
});
const response = new Bxml.Response([speakSentence, gather]);
request_response.status(200).send(response.toBxml());
}
@POST ['/digitsCallback'] (req, res) => {
//NOTE: This section of the application may look different depending on how your endpoints receive callback events
const callback = req.body;
const digits = callback.digits;
const message = "You input the number " + digits;
const speakSentence = new Bxml.SpeakSentence(message);
const response = new Bxml.Response(speakSentence);
request_response.status(200).send(response.toBxml());
}
**Note: The endpoint headers are pseudocoded. Your implementation will look different
import json
@POST '/gatherDigits'
def gatherDigits():
speak_sentence = SpeakSentence(
text="Input a digit to hear it read back to you."
)
gather = Gather(
gather_url="/digitsCallback",
max_digits=1
)
response = Response()
response.add_verb(speak_sentence)
response.add_verb(gather)
return response.to_bxml()
@POST '/digitsCallback'
def digitsCallback():
#NOTE: This section of the application may look different depending on how your endpoints receive callback events
callback_data = json.loads(request.data)
digits = callback_data['digits']
message = "You input the number " + digits
speak_sentence = SpeakSentence(
text=message
)
response = Response()
response.add_verb(speak_sentence)
return response.to_bxml()
**Note: The endpoint headers are pseudocoded. Your implementation will look different
@POST('/gatherDigits)
function gatherDigits(Request $request, Response $response) {
$speakSentence = new BandwidthLib\Voice\Bxml\SpeakSentence("Input a digit to hear it read back to you.");
$gather = new BandwidthLib\Voice\Bxml\Gather();
$gather->gatherUrl("/digitsCallback");
$gather->maxDigits(1);
$response = new BandwidthLib\Voice\Bxml\Response();
$response->addVerb($speakSentence);
$response->addVerb($gather);
$bxml = $bxmlResponse->toBxml();
$response = $response->withStatus(200)->withHeader('Content-Type', 'application/xml');
$response->getBody()->write($bxml);
return $response;
}
@POST('/digitsCallback)
function digitsCallback(Request $request, Response $response) {
#NOTE: This section of the application may look different depending on how your endpoints receive callback events
$data = $request->getParsedBody();
$digits = $data['digits'];
$speakSentence = new BandwidthLib\Voice\Bxml\SpeakSentence("You input the number $digits");
$response = new BandwidthLib\Voice\Bxml\Response();
$response->addVerb($speakSentence);
$bxml = $bxmlResponse->toBxml();
$response = $response->withStatus(200)->withHeader('Content-Type', 'application/xml');
$response->getBody()->write($bxml);
return $response;
}
The above number prompts the caller to input a number, after which the gather ends and a <SpeakSentence>
says the pressed number back to the caller.
Advanced IVR System
To further enhance your IVR system, additional verbs can be nested inside of a <Gather>
tag. These include playing media and text to speech. In this new example, the sentence "Please press a digit" will be said at the start of the call and after each press, instead of only at the beginning of the call.
- XML
- Java
- C#
- Ruby
- NodeJS
- Python
- PHP
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Gather gatherUrl="/gatherCallback" repeatCount="3">
<SpeakSentence>Please listen to the menu of options.</SpeakSentence>
<PlayAudio>http://audio.test/englishMenuOptions.mp3</PlayAudio>
<PlayAudio>http://audio.test/spanishMenuOptions.mp3</PlayAudio>
</Gather>
</Response>
And endpoint /gatherCallback should return:
//NOTE: This section of the application may look different depending on how your endpoints receive callback events
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<SpeakSentence>You input the number {digit}</SpeakSentence>
</Response>
**Note: This application is pseudocoded. Your implementation will look different
@Post /gatherDigits endpoint
public String gatherDigits() {
SpeakSentence speakSentence = new SpeakSentence("Please listen to the menu of options.");
PlayAudio playAudio = new PlayAudio().builder()
.audioUri("http://audio.test/englishMenuOptions.mp3")
.build();
PlayAudio playAudio2 = new PlayAudio().builder()
.audioUri("http://audio.test/spanishMenuOptions.mp3")
.build();
Gather gather = new Gather().builder()
.gatherUrl("/gatherCallback")
.repeatCount(3)
.maxDigits(1)
.children(List.of(speakSentence, playAudio, playAudio2))
.build();
Response response = new Response()
.with(gather);
return response.toBXML();
}
@Get /gatherCallback endpoint
public byte[] gatherCallback() {
//NOTE: This section of the application may look different depending on how your endpoints receive callback events
String digits = callback.getDigits();
String message = "You input the number " + digits;
SpeakSentence speakSentence = new SpeakSentence(message);
Response response = new Response()
String bxml = response.with(speakSentence).toBXML();
return bxml;
}
**Note: The endpoint headers are pseudocoded. Your implementation will look different
@Post [/gatherDigits]
public ActionResult gatherDigits() {
List<AudioProducer> list = new ArrayList<>();
list.add(SpeakSentence.builder()
.text("Please listen to the menu of options.")
.build());
list.add(PlayAudio.builder()
.audioUri("http://audio.test/englishMenuOptions.mp3")
.build());
list.add(PlayAudio.builder()
.audioUri("http://audio.test/spanishMenuOptions.mp3")
.build());
Gather gather = Gather.builder()
.gatherUrl("/gatherCallback")
.repeatCount(3)
.audioProducer(list)
.build();
Response response = Response.builder().build()
.add(gather);
return new OkObjectResult(response.ToBXML());
}
@Get [/gatherCallback]
public ActionResult gatherCallback() {
//NOTE: This section of the application may look different depending on how your endpoints receive callback events
using var reader = new StreamReader(Request.Body, Encoding.UTF8);
var body = await reader.ReadToEndAsync();
var callback = JObject.Parse(body);
var digits = (string)callback["digits"];
string message = "You input the number " + digits;
SpeakSentence speakSentence = new SpeakSentence
{
Sentence = message,
};
Response response = new Response();
response.Add(speaksentence);
return new OkObjectResult(response.ToBXML());
}
**Note: This application is pseudocoded. Your implementation will look different
post '/gatherDigits' do
speak_sentence_1 = Bandwidth::Bxml::SpeakSentence.new('Please listen to the menu of options.')
play_audio_1 = Bandwidth::Bxml::PlayAudio.new('http://audio.test/englishMenuOptions.mp3')
play_audio_2 = Bandwidth::Bxml::PlayAudio.new('http://audio.test/spanishMenuOptions.mp3')
gather = Bandwidth::Bxml::Gather.new([speak_sentence_1, play_audio_1, play_audio_2], {
gather_url: '/gatherCallback',
repeat_count: 3
})
response = Bandwidth::Bxml::Response.new([gather])
return response.to_bxml
end
get '/gatherCallback' do:
data = JSON.parse(request.body.read)
speak_sentence = Bandwidth::Bxml::SpeakSentence.new(data['digits'])
response = Bandwidth::Bxml::Response.new([speak_sentence])
return response.to_bxml
end
**Note: The endpoint headers are pseudocoded. Your implementation will look different
@post ['/gatherDigits'] (request, request_response) => {
const speakSentence1 = new Bxml.SpeakSentence('Please listen to the menu of options.');
const playAudio1 = new Bxml.PlayAudio('http://audio.test/englishMenuOptions.mp3');
const playAudio2 = new Bxml.PlayAudio('http://audio.test/spanishMenuOptions.mp3');
const gather = new Bxml.Gather(
{
gatherUrl: "/gatherCallback",
repeatCount: 3
},
[speakSentence1, playAudio1, playAudio2]
);
const response = new Bxml.Response(gather);
console.log(response.toBxml());
request_response.status(200).send(response.toBxml());
}
@get ['/gatherCallback'] => {
const callback = req.body;
const digits = callback.digits;
const message = `You input the number ${digits}`;
const speakSentence = new Bxml.SpeakSentence(message);
const response = new Bxml.Response(speakSentence);
request_response.status(200).send(response.toBxml());
}
**Note: The endpoint headers are pseudocoded. Your implementation will look different
@post '/gatherDigits'
def gatherDigits():
speak_sentence_1 = SpeakSentence(
text="Please listen to the menu of options."
)
play_audio_1 = PlayAudio(
audio_uri="http://audio.test/englishMenuOptions.mp3"
)
play_audio_2 = PlayAudio(
audio_uri="http://audio.test/spanishMenuOptions.mp3"
)
gather = Gather(
gather_url="/gatherCallback",
repeat_count=3,
audio_verbs=[speak_sentence_1, play_audio_1, play_audio_2]
)
response = Response()
response.add_verb(gather)
return response.to_bxml()
@get '/gatherCallback'
def gatherCallback():
#NOTE: This section of the application may look different depending on how your endpoints receive callback events
callback_data = json.loads(request.data)
digits = callback_data['digits']
message = "You input the number " + digits
speak_sentence = SpeakSentence(
text=message
)
response = Response()
response.add_verb(speak_sentence)
return response.to_bxml()
**Note: The endpoint headers are pseudocoded. Your implementation will look different
@post('/gatherDigits)
function gatherDigits(Request $request, Response $response) {
$speakSentence1 = new BandwidthLib\Voice\Bxml\SpeakSentence("Please listen to the menu of options.");
$playAudio1 = new BandwidthLib\Voice\Bxml\PlayAudio("http://audio.test/englishMenuOptions.mp3");
$playAudio2 = new BandwidthLib\Voice\Bxml\PlayAudio("http://audio.test/spanishMenuOptions.mp3");
$gather = new BandwidthLib\Voice\Bxml\Gather();
$gather->gatherUrl("/gatherCallback");
$gather->speakSentence($speakSentence1);
$gather->repeatCount(3);
$gather->playAudio($playAudio1);
$gather->playAudio($playAudio2);
$response = new BandwidthLib\Voice\Bxml\Response();
$response->addVerb($gather);
return $response->toBxml();
}
@get('/gatherCallback)
function gatherCallback(Request $request, Response $response) {
#NOTE: This section of the application may look different depending on how your endpoints receive callback events
$data = $request->getParsedBody();
$digits = $data['digits'];
$speakSentence = new BandwidthLib\Voice\Bxml\SpeakSentence("You input the number $digits");
$response = new BandwidthLib\Voice\Bxml\Response();
$response->addVerb($speakSentence);
return $response->toBxml();
}
In this example, there is one <SpeakSentence>
and two <PlayAudio>
nested within the gather. If, after playing, the caller doesn't press a button, the audio will replay two more times.
Where to next?
Now that you have learnt how to build an IVR tree, check out some of the available actions in the following guides: