Building an IVR using Bandwidth’s BXML

Notice: This post uses a legacy version of our API. Visit out developer portal to view current API documentation.
A few months ago, I wrote this blog post discussing the benefits of Interactive Voice Response (IVR) systems, and how you can use Bandwidth’s Voice API to build an IVR. Today, I want to revisit this, and show how to build an IVR system using Bandwidth’s BXML.
What is Bandwidth’s BXML?
Bandwidth’s BXML is a custom XML language that defines operations for phone calls, giving an alternative to Bandwidth’s Voice API for handling phone calls on Bandwidth’s network.
Documentation for Bandwidth’s BXML can be found here.
Recap: How do you update phone calls using Bandwidth’s Voice API?
Using Bandwidth’s Voice API, phone calls are updated by making POST requests on various endpoints of Bandwidth’s API. For example, to create a gather on a phone call, you would need to make a POST request on the following URL with your “userId” and “callId” credentials, your authentication credentials (API token and API secret), and information for the gather as JSON data.
https://api.catapult.inetwork.com/v1/users/{userId}/calls/{callId}/gather
Once the gather is complete, you would need to retrieve the gather’s information from Bandwidth by making a GET request on this endpoint, again with the same credentials along with the “gatherId” used above.
https://api.catapult.inetwork.com/v1/users/{userId}/calls/{callId}/gather/{gatherId}
How does Bandwidth’s BXML work?
Using Bandwidth’s BXML, phone calls are updated by Bandwidth making a GET request on your server to find BXML that tells Bandwidth how to update the phone call, and if necessary where to look next to continue updating the phone call once the current operation is complete. Let’s again create a gather on a phone call, but instead use BXML. To create a gather on a phone call using BXML, you would need to set up an endpoint on your server that, when accessed via HTTP GET, returns the following BXML:
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Gather requestUrl="<next_url>" requestUrlTimeout="10000"
terminatingDigits="#">
<SpeakSentence gender="female" locale="en_US" voice="susan">Please, press a digit.</SpeakSentence>
</Gather>
</Response>
Once the gather is complete, Bandwidth makes a GET request on the URL defined in the gather (the value <next_url> is a placeholder for a real URL) and sends the gather information to this URL.
Big Picture: Bandwidth’s Voice API vs BXML
On a very high level, Bandwidth’s Voice API works by having a server that continually makes requests on Bandwidth’s API endpoints to update phone calls and retrieve call information from Bandwidth, while BXML works by having a server that continually receives requests (along with call information) from Bandwidth and returns information as BXML telling Bandwidth how to update phone calls. BXML servers don’t necessarily need to keep track of call information such as the “callId” to update calls; Bandwidth knows what call to update when retrieving BXML from your server.
Here’s some pseudocode demonstrating the differences:
Bandwidth’s Voice API
call = receive_bandwidth_call()
make_gather_on_call(call, generic_gather_information())
hangup_call(call)
BXML
endpoint “/answer_call” = <BXML that redirects to /gather>
endpoint “/gather” = <BXML that creates a gather, and redirects to /hangup>
endpoint “/hangup” = <BXML to hangup call>
BXML is capable of doing all of the operations that Bandwidth’s Voice API can perform, except for starting a phone call. You will need to use Bandwidth’s Voice API if your application initiates the phone call. If you want to initiate the phone call, you will need to set “callbackUrl” to the first endpoint of your BXML, and “callbackHttpMethod” to “GET”. Documentation on creating a phone call can be found here.
BXML IVR
Building an IVR with BXML is quite simple. All you need is an endpoint on your server that starts the IVR, and various endpoints on your server to handle certain steps in the IVR. Let’s demonstrate this with a simple use case. You have a plumbing service and you want to perform a survey via phone call on the satisfaction of your recent customers. You want the customers to rate on a scale of 1 to 5 their satisfaction on the price, quality, and timeliness of your service. The workflow will look like this:
- Answer the call and play a prompt describing the survey to the customer
- Ask for rating on price
- Ask for rating on quality
- Ask for rating on timeliness
- Prompt a thank you
- Hang-up the call
You would need an endpoint on your server for each of these operations. The first endpoint would simply speak a sentence and redirect to the first endpoint of the gather. Each endpoint in the flow of gathers would have a gather that would speak the prompt asking for user input, and redirect to the next endpoint. The final endpoint would simply speak a thank you prompt, and hang-up the call.
After each gather is complete, Bandwidth sends a callback to the URL of the next step (which may or may not be another gather) with information on the gather. Knowing this is crucial for keeping track of digit inputs from the user. Specifically for a gather, user input is stored in the “digits” parameter of the gather’s callback. More information on BXML callbacks can be found here.
A code sample of this is shown below. This code uses Ruby and Sinatra to define the server and the server endpoints. This code also uses URL parameters to keep track of the user’s input from previous steps.
get "/" do
bxml_string = '<?xml version="1.0" encoding="UTF-8"?>'
bxml_string += '<Response>'
bxml_string += '<SpeakSentence voice="susan" gender="female">Thank you for taking our customer satisfaction survey. You will be asked a question, and please rate your response by using your keypad with a scale of 1 to 5 with 1 being low satisfaction and 5 being high satisfaction</SpeakSentence>'
bxml_string += '<Redirect requestUrl="%s/startSurvey"></Redirect>' % [SERVER]
bxml_string += '</Response>'
return bxml_string
end
get "/startSurvey" do
bxml_string = '<?xml version="1.0" encoding="UTF-8"?>'
bxml_string += '<Response>'
bxml_string += '<SpeakSentence voice="susan" gender="female">Your survey will begin now</SpeakSentence>'
bxml_string += '<Redirect requestUrl="%s/price"></Redirect>' % [SERVER]
bxml_string += '</Response>'
end
get "/price" do
bxml_string = '<?xml version="1.0" encoding="UTF-8"?>'
bxml_string += '<Response>'
bxml_string += '<Gather requestUrl="%s/quality" maxDigits="1">' % [SERVER]
bxml_string += '<SpeakSentence voice="susan" gender="female">Rate your satisfaction on price</SpeakSentence>'
bxml_string += '</Gather>'
bxml_string += '</Response>'
return bxml_string
end
get "/quality" do
previous_response = params['digits']
bxml_string = '<?xml version="1.0" encoding="UTF-8"?>'
bxml_string += '<Response>'
bxml_string += '<Gather requestUrl="%s/timeliness?previousResponse=%s" maxDigits="1">' % [SERVER, previous_response]
bxml_string += '<SpeakSentence voice="susan" gender="female">Rate your satisfaction on quality</SpeakSentence>'
bxml_string += '</Gather>'
bxml_string += '</Response>'
return bxml_string
end
get "/timeliness" do
previous_response = params['previousResponse'] + params['digits']
bxml_string = '<?xml version="1.0" encoding="UTF-8"?>'
bxml_string += '<Response>'
bxml_string += '<Gather requestUrl="%s/hangup?previousResponse=%s" maxDigits="1">' % [SERVER, previous_response]
bxml_string += '<SpeakSentence voice="susan" gender="female">Rate your satisfaction on timeliness</SpeakSentence>'
bxml_string += '</Gather>'
bxml_string += '</Response>'
return bxml_string
end
get "/hangup" do
bxml_string = '<?xml version="1.0" encoding="UTF-8"?>'
bxml_string += '<Response>'
bxml_string += '<SpeakSentence voice="susan" gender="female">Thank you for taking our survey. Your response has been recorded. Goodbye</SpeakSentence>'
bxml_string += '<Hangup></Hangup>'
bxml_string += '</Response>'
response = params['previousResponse'] + params['digits']
log_response(response)
return bxml_string
end
The full code sample, along with another BXML IVR for an ordering system, can be found here.