Gather User Input via Keypad (DTMF Tones) in Python
In this guide, we'll show you how to gather user input during a phone call through the phone's keypad (using DTMF tones) in your Python application. By applying this technique, you can create interactive voice response (IVR) systems and other phone based interfaces for your users.
The code snippets in this guide are written using the Flask web framework and the Twilio Python SDK. Let's get started!
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.
The
1from flask import Flask2from twilio.twiml.voice_response import VoiceResponse, Gather34app = Flask(__name__)567@app.route("/voice", methods=['GET', 'POST'])8def voice():9"""Respond to incoming phone calls with a menu of options"""10# Start our TwiML response11resp = VoiceResponse()1213# Start ourverb 14gather = Gather(num_digits=1)15gather.say('For sales, press 1. For support, press 2.')16resp.append(gather)1718# If the user doesn't select an option, redirect them into a loop19resp.redirect('/voice')2021return str(resp)2223if __name__ == "__main__":24app.run(debug=True)
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
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 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.
1from flask import Flask, request2from twilio.twiml.voice_response import VoiceResponse, Gather34app = Flask(__name__)567@app.route("/voice", methods=['GET', 'POST'])8def voice():9"""Respond to incoming phone calls with a menu of options"""10# Start our TwiML response11resp = VoiceResponse()1213# If Twilio's request to our app included already gathered digits,14# process them15if 'Digits' in request.values:16# Get which digit the caller chose17choice = request.values['Digits']1819#a different message depending on the caller's choice 20if choice == '1':21resp.say('You selected sales. Good for you!')22return str(resp)23elif choice == '2':24resp.say('You need support. We will help!')25return str(resp)26else:27# If the caller didn't choose 1 or 2, apologize and ask them again28resp.say("Sorry, I don't understand that choice.")2930# Start ourverb 31gather = Gather(num_digits=1)32gather.say('For sales, press 1. For support, press 2.')33resp.append(gather)3435# If the user doesn't select an option, redirect them into a loop36resp.redirect('/voice')3738return str(resp)3940if __name__ == "__main__":41app.run(debug=True)
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
1from flask import Flask, request2from twilio.twiml.voice_response import VoiceResponse, Gather34app = Flask(__name__)567@app.route("/voice", methods=['GET', 'POST'])8def voice():9"""Respond to incoming phone calls with a menu of options"""10# Start our TwiML response11resp = VoiceResponse()1213# Start ourverb 14gather = Gather(num_digits=1, action='/gather')15gather.say('For sales, press 1. For support, press 2.')16resp.append(gather)1718# If the user doesn't select an option, redirect them into a loop19resp.redirect('/voice')2021return str(resp)222324@app.route('/gather', methods=['GET', 'POST'])25def gather():26"""Processes results from theprompt in /voice""" 27# Start our TwiML response28resp = VoiceResponse()2930# If Twilio's request to our app included already gathered digits,31# process them32if 'Digits' in request.values:33# Get which digit the caller chose34choice = request.values['Digits']3536#a different message depending on the caller's choice 37if choice == '1':38resp.say('You selected sales. Good for you!')39return str(resp)40elif choice == '2':41resp.say('You need support. We will help!')42return str(resp)43else:44# If the caller didn't choose 1 or 2, apologize and ask them again45resp.say("Sorry, I don't understand that choice.")4647# If the user didn't choose 1 or 2 (or anything), send them back to /voice48resp.redirect('/voice')4950return str(resp)5152if __name__ == "__main__":53app.run(debug=True)
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 Python, you might enjoy our IVR Phone Tree (Flask) tutorial, which implements a full sample application using these techniques.