Interactive Voice Response (IVR) systems are everywhere – when you call the bank, when you use the hotel room telephone, even when you call the Queen! Okay – that last one probably doesn’t happen very often, but I bet the Windsors have an IVR for routing to the appropriate member of royalty. If you’re still a little bit unsure what an IVR is – you have probably used one when you’ve called a number and heard something similar to this:
“To talk to a human, press 1. To talk to an Owl, press 2.”
This type of service is called an Interactive Voice Response. It provides a solution that would otherwise leave the telephony services of companies and large call centers in complete and utter chaos. By routing people through to the correct humans, IVRs help speed up the process of getting things done. Even if you’re routing between two separate people, or a thousand call center agents, IVRs are an invaluable part of telephony systems.
What You’ll Learn
With Twilio building an IVR system is as simple as writing software that listens to the digits a caller types into their phone. In this blog post we will create a simple IVR with Django, a popular MVC framework built in Python. We will learn to:
- Set up the django-twilio library in our Django project
- Link up a Twilio phone number a Django view
- Prompt users to enter some digits into their handset
- Programmatically discover and handle the response based on the input
To get started you will need:
- A free Twilio trial account. Sign up here to get started, it takes one minute.
- An existing Django 1.6 project. If you’ve never used Django before, check out the tutorial on their website. If you would rather read through an example, check out a demo project here.
To learn more about the high-level advantages of building an IVR on Twilio, check out our IVR page.
The django-twilio library
A fantastic Twilio community member, Randall Degges, built the django-twilio library to work seamlessly in Django projects. It’s a pretty cool tool and we’ll be using it today.
We’re going to need to start a new Django project for this, so once you have installed Django 1.6 with pip like so:
$ pip install django
run the following terminal command to set up a Django project:
$ python django-admin.py startproject djtwilio
We’re using the project name djtwilio for this project, but you’re welcome to use your own name. Just remember to substitute djtwilio for your project name throughout the code.
Installing django-twilio can be done with pip through the command line:
$ pip install django-twilio
If you are using Django 1.6, you will also need to install South:
$ pip install South
For those of you unfamiliar with South, it is the de facto database schema management tool for Django. I’d recommend reading the documentation and adding South to the must-have list of libraries you use with Django.
We’ll need to add both of these packages to the list of our installed apps, in the djtwilio/settings.py file of our Django project:
INSTALLED_APPS = ( 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.sites', 'django.contrib.messages', 'django.contrib.staticfiles', # Add the new apps here 'django_twilio', 'south', ... ) TWILIO_ACCOUNT_SID = 'ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' TWILIO_AUTH_TOKEN = 'YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY'
We’re also adding our account sid and authentication token from Twilio. You can find these tokens on your Twilio account dashboard. An important note: if you’re going to use django-twilio in production, I strongly recommend you do not keep your account tokens in the settings.py file, we’re doing it here to make it easier to debug for this tutorial. Instead, add them as environment variables, django-twilio will search for them automatically for you.
Now we can set up our database:
$ python manage.py syncdb
This will build the database and set up the superuser. Finally, using South, we can build the django-twilio models:
$ python manage.py migrate django_twilio
If you are using Django 1.7, Django-twilio is now supported. Django 1.7 introduced some major changes to how database schema management is done. You can read more in the release notes. In order to get django-twilio working with Django 1.7 you need to run this command in the terminal:
$ python manage.py migrate
With Django 1.7, you also need to manually create a superuser account with the following terminal command:
$ python manage.py createsuperuser
If any of this configuration does not work properly, don’t forget that the settings.py file in the Github example project will show you how it should be done.
Linking a Twilio phone number to Django View
Now that we have set up django-twilio, let’s use it to create a basic Twilio view that returns some TwiML code. TwiML is Twilio Markup Language, a collection of XML tags that Twilio will magically transform into SMS message and voice call instructions. Django-twilio has been designed to handle all the nitty-gritty of returning XML encoded data; all we have to worry about is what instructions we want to return.
All the magic of IVR systems with Twilio is performed by the
In order to point a Twilio phone number to a Django view we need to:
- Create the Django view
- Configure Django’s URL configuration to link the view to a URL
- Link that URL to the Voice Request URL in our Twilio phone number
Creating the Django view is the easiest bit. In our Django project, we can add views into the default a file called views.py. You will need to create this file and it should be in the same directory (djtwilio) as the urls.py and settings.py files.
You can view the commit for this code in the example project:
from django_twilio.decorators import twilio_view from twilio.twiml import Response @twilio_view def gather_digits(request): twilio_response = Response() with twilio_response.gather(action='/respond/', numDigits=1) as g: g.say('Press one to hear a song, two to receive an SMS') g.pause(length=1) g.say('Press one to hear a song, two to receive an SMS') return twilio_response
Before we continue, let’s decompose what we’ve written here so we understand it all. The first few lines add a decorator to the Django view (just a plain old Python function). The @twilio_view decorator is part of the django-twilio library and correctly formats the requests and responses so you don’t have to. Within the function, we are creating a new response, adding the Gather and Say verbs, and then returning it all back. We’re doing all of this with the Python helper library, which turns the creation of TwiML markup into lovely pythonic code.
In the gather tag we are setting numDigits to 1. We don’t want to accept any more than one digit for this demo. We’ve said the same thing twice with a pause in between. This is just in case the user doesn’t hear the first time.
We’ll handle the action URL of the gather tag later on, first we need to make our TwiML markup is accessible by linking it up to it’s own URL, otherwise we’d never be able to address it!
In Django, we use the djtwilio/urls.py file (in the same directory as the views.py file that you just created) to set up the URL routing for the application:
from django.conf.urls import patterns, include, url from django.contrib import admin admin.autodiscover() urlpatterns = patterns('', url(r'^admin/', include(admin.site.urls)), # Here we add our Twilio URLs url(r'^gather/$', 'djtwilio.views.gather_digits'), )
What’s happening here? In Django, we provide a tuple of URL paths that link routes (such as /gather/) to views in our django application. The admin URL is a default for Django, but on the last line of the tuple we add our own URL:
Remember that based on the naming of your project, ‘djtwilio’ might be different. The example project shows you how your urls.py file should look.
The last step needed is to link this URL to the Voice Request URL on your Twilio phone number.
The URL we want to add needs to point to the publicly addressable URL for your server. Don’t have a server handy? At Twilio we use an awesome tool called NGrok, which allows us to run our local dev server on the web with a publicly addressable link. Using NGrok, we can run our Django server through the command line:
$ python manage.py runserver 0:8000
and in another terminal, we can tell NGrok to route our local 8000 port (the default for Django) out to the web. You can tell NGrok which port to route with a single command line instruction:
$ ./ngrok 8000
If you’re running on a Mac, you’ll need to prepend bash at the beginning of the line.
You can now make a HTTP post request (using cURL or similar) to the URL to see the TwiML markup we just created:
<?xml version="1.0" encoding="UTF-8"?> <Response> <Gather action="/respond/" numDigits="1"> <Say>Press one to hear a song, two to receive an SMS</Say> <Pause length="1"/> <Say>Press one to hear a song, two to receive an SMS</Say> </Gather> </Response>
We should now be able to make a phone call to the number.
The process that happens here should be familiar to anyone who has developed on the Twilio platform before: voice calls reach Twilio, triggering an HTTP POST request to your server. You then respond to this request with a response.
Testing out the IVR system
If we give the Twilio phone number a ring we will be able to hear the response we wrote above (“Press one to hear a song, two to get an SMS message”). If we type in any digit we will hear the dreaded application error sound! The reason for this might be obvious if you’re used to working with Twilio, but if you’re not quite sure our app monitor is a fantastic tool for diagnosing errors like this. Check the error in app monitor for the previous voice call.
Our app monitor has made a request to the url /respond/ on our server (point 1), which does not exist yet. This has given us an HTTP 404 error (point 2) in response. You can see from the body of the request the page has not been found.
This second URL will be sent a request when the user types in some numbers into their telephone keypad.
Programmatically handling user input
To handle the user input we need to:
- Create a new view called handle_response that discovers the inputted digits and returns a customised response.
- Create a new URL in our URL configuration that links to the new handle_response view.
The second view requires a bit more computational logic than the first. Here is the new code in our djtwilio/views.py file, directly under the gather_digits function:
@twilio_view def handle_response(request): digits = request.POST.get('Digits', '') twilio_response = Response() if digits == '1': twilio_response.play('http://bit.ly/phaltsw') if digits == '2': number = request.POST.get('From', '') twilio_response.say('A text message is on its way') twilio_response.sms('You looking lovely today!', to=number) return twilio_response
Let’s run through this line by line and figure out what we’re doing.
First we inspect the request object (provided to use by Django) and return the Digits parameter as a string. We store this in our own variable called digits:
digits = request.POST.get('Digits', '')
The cool stuff happens when we compare the digits variable. If it is equal to 1, we add a
if digits == '1': twilio_response.play('http://bit.ly/phaltsw')
if digits == '2': number = request.POST.get('From', '') twilio_response.say('A text message is on its way') twilio_response.sms('You look lovely today!', to=number)
Two important things to note in this code:
- With voice responses, we use the
tag and not the tag to send an SMS message.
- We are also inspecting the request object for the From parameter (the number of the person who is calling). We use this in the Sms tag to ensure the message gets sent to the correct number.
Finally we return the response:
Just like the gather_digits view; django-twilio will handle the formatting of the HTTP response for us because we’ve added the twilio_view decorator above the function name:
@twilio_view def handle_response(request):
The final step is to add a single line of code in our djtwilio/urls.py file on line 10:
from django.conf.urls import patterns, include, url from django.contrib import admin admin.autodiscover() urlpatterns = patterns('', url(r'^admin/', include(admin.site.urls)), # Here we add our Twilio URLs url(r'^gather/$', 'djtwilio.views.gather_digits'), url(r'^respond/$', 'djtwilio.views.handle_response'), )
Like the URL above, this helps Django to route the incoming requests to the correct view.
Testing it out
Now we can try and ring the phone number again.
When we type in our response (by pressing 1 or 2); we will either get a fantastic song played down the phone, or a simple voice message followed by an SMS message.
Taking things further
Now you have a simple IVR telephony system in Django. The entire code for this is available in an example project on Github, with step-by-step commits on how to implement it.
This code is very easy to change. Should we wish to implement a different set of menu options, we can just change the initial
IVR systems are dead easy with Twilio, for a few ideas on how to use it, here are some examples to get you brain juices flowing:
- Use IVR to route callers through to specific departments in your company office.
- A “choose your own adventure” voice game using IVR to route the player’s choices.
- Capturing data (such as gas meter readings or date of birth) to easily gather data.
- Adding a secure pin code to a conference call line.
If you’d like any help with this, or want to ask me questions about Django implementation with Twilio, please email me at email@example.com or get in touch with me on Twitter, I’d love to help. We’ve also built an IVR page with IVR examples and code for many programming languages.
Don’t forget to post your cool IVR projects in the comments below!
- Twilio Autopilot Python Quickstart
- Custom HTTP Clients for the Twilio Python Helper Library
- Building a Simple SMS Message Application with Twilio and Django
- How to Build a Serverless API with Amazon Web Services’ API Gateway
- SMS Sentiment Analysis in Python with Flask and the IBM Watson Twilio Add-on