How to Receive a Phone Call in Python Using Django and Twilio

March 10, 2021
Written by

How to Receive a Phone Call in Python Using Django and Twilio

Twilio Voice is a highly customizable and convenient service that allows you to automate your telephone workflows. In this tutorial you are going to learn how to create a Python application based on the Django web framework that can answer phone calls.

Prerequisites

To follow this tutorial you need the following items:

Project setup

In this section you are going to set up a brand new Django project. To keep things nicely organized, open a terminal or command prompt, find a suitable place and create a new directory where the project you are about to create will live:

mkdir phone-answer
cd phone-answer

Creating a virtual environment

Following Python best practices, you are now going to create a virtual environment, where you are going to install the Python dependencies needed for this project.

If you are using a Unix or Mac OS system, open a terminal and enter the following commands to do the tasks described above:

python3 -m venv venv
source venv/bin/activate
pip install django twilio pyngrok

If you are following the tutorial on Windows, enter the following commands in a command prompt window:

python -m venv venv
venv\Scripts\activate
$ pip install django twilio pyngrok

The two Python packages that this project uses are:

Creating a Django project

In this step you are going to create a brand new Django web application. Enter the following commands in the same terminal you used to create and activate the virtual environment:

django-admin startproject receptionist .
django-admin startapp answer
python manage.py migrate
python manage.py runserver

The first command above creates a Django project called receptionist. You will see a subdirectory with that name created in the top-level directory of your project. The next command defines a Django application called answer. After you run this command you will also see a subdirectory with that name added to the project. This is the application in which you will build the logic to answer phone calls.

The migrate command performs the default Django database migrations, which are necessary to fully set up the Django project. The runserver command starts the Django development web server.

It is convenient to leave the Django web server running while you write code, because it automatically detects code changes and restarts to incorporate them. So leave this terminal window alone and open a second terminal to continue.

Starting an ngrok tunnel

The problem with the Django web server is that it is local, which means that it cannot be accessed over the Internet. To be able to answer phone calls, Twilio needs to be able to send web requests to this server, so during development, a trick is necessary to make the local server available on the Internet.

On a second terminal window, activate the virtual environment and then run the following command:

ngrok http 8000

The ngrok screen should look as follows:

ngrok

Note the https:// forwarding URL. This URL is temporarily connected to the Django web server, and any requests that arrive on it will be transparently forwarded to it. The URL is active for as long as you keep ngrok running, or until the ngrok session expires. Each time ngrok is launched a new randomly generated URL will be mapped to the local server.

It is highly recommended that you create a free Ngrok account and install your Ngrok account's authtoken on your computer to avoid hitting limitations in this service. See this blog post for details.

Open the file settings.py from the receptionist directory. Find the line that has the ALLOWED_HOSTS variable and change it as follows:

ALLOWED_HOSTS = ['.ngrok.io']

This will tell Django that requests received using ngrok URLs are allowed.

While still running the Django server and ngrok on two separate terminals, type http://xxxxxx.ngrok.io on the address bar of your web browser to confirm that your Django project is up and running. Replace xxxxx with the randomly generated subdomain from your ngrok session.

Django server

Leave the Django server and ngrok running while you continue working on the tutorial. If your ngrok session expires, stop it by pressing Ctrl-C, and then start it again to begin a new session. Remember that each time you restart ngrok the forwarding URL will change.

Creating a TwiML webhook

Twilio Voice uses the concept of webhooks to enable your application to perform custom actions as a result of external events such as receiving a phone call. A webhook is nothing more than an HTTP endpoint that Twilio invokes with information about the event. The response returned to Twilio provides instructions on how to handle the event.

The webhook for an incoming phone call will include information such as the phone number of the caller. In the response, the application can provide instructions such as “hangup”, “play a recorded message”, etc. The responses use a language based on XML called TwiML.

Adding a new endpoint

Open the settings.py file from the receptionist directory once again. Find the INSTALLED_APPS variable. This is a list of several strings. At the end of the list, you need to add one more string to register the answer application that was created earlier.

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'answer.apps.AnswerConfig',   # ← new item
]

Open the views.py from the answer subdirectory. This is where you are going to create the endpoint that will handle the incoming phone calls. Replace the contents of this file with the following:

from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt

# Create your views here.

@csrf_exempt
def answer(self):
    return HttpResponse('Hello!')

The answer() function is the endpoint function that will run when Twilio notifies the application of an incoming call. For now this function returns a simple response.

To make this endpoint accessible through the web application, a URL needs to be assigned to it. Open the urls.py file from the receptionist directory and add a new entry to the urlpatterns list as shown below:

from django.contrib import admin
from django.urls import path
from answer import views    # ← new import

urlpatterns = [
    path('admin/', admin.site.urls),
    path('answer', views.answer),    # ← new item
]

The path(‘answer’, views.answer) line tells Django that the answer() function from views.py is mapped to a /answer URL on the web application.

To confirm that everything is working, go back to your web browser, and append /answer to the ngrok URL you tested earlier. You should see the “Hello!” message that the endpoint returns in the page.

Answering the phone with a message

To complete the webhook you will now change the response of the endpoint to use TwiML. The twilio package that is installed in the virtual environment provides helper classes that make generating these responses very fast.

Update the views.py file in the answer subdirectory as follows:

from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
from twilio.twiml.voice_response import VoiceResponse

# Create your views here.

@csrf_exempt
def answer(self):
    response = VoiceResponse()
    response.say('Hello, and thank you for your call.')
    return HttpResponse(str(response))

This updated implementation of the endpoint uses the VoiceResponse class from the twilio package. In particular, its say() method will instruct Twilio to use a text-to-speech engine to answer the phone call with the message given as an argument.

Getting a Twilio phone number

The final step is to configure a Twilio phone number to forward incoming calls to the answer() endpoint created above.

Buying a phone number

Log in to the Twilio Console, select Phone Numbers, and then click on the red plus sign to buy a Twilio number. Note that if you are using a free account you will be using your trial credit for this purchase.

In the Buy a Number screen you can select your country and check Voice in the capabilities field. If you’d like to request a number from your region, you can enter your area code in the Number field.

Buy a phone number

Click the “Search” button to see what numbers are available, and then click “Buy” for the number that you like from the results. After you confirm your purchase, click the “Close” button.

Congratulations, you now have your very own Twilio phone number!

Configuring the voice webhook

While on the Twilio Console, click on Phone Numbers again and then select the phone number that you purchased above. Scroll down to the “Voice & Fax” section and enter the following settings:

  • For “Accept Incoming” select Voice Calls.
  • For “Configure with” select Webhooks, TwiML Bins, Functions, Studio or Proxy.
  • For “A call comes in” select Webhook in the left dropdown. Enter the ngrok forwarding URL with /answer at the end in the text field. Select HTTP POST in the dropdown on the right side.

Configure Twilio Voice webhook

Don’t forget to click the Save button at the bottom of the page to record these changes.

Testing your Twilio phone line

And now the moment of truth! Make sure the Django server and ngrok are still running. Call the Twilio phone number that you purchased earlier from your own phone. After a ring or two, the call should be answered and you should hear a nice voice that speaks the text that you passed as an argument to the say() method.

Next steps

I hope you found this tutorial useful. The TwiML language is extremely rich and provides a large variety of options to automate incoming phone calls. If you would like to explore some of these options, here are some example commands you can use:

  • Play: instead of text-to-speech, play a message that is recorded in an audio file
  • Dial: connect the call to another phone number or conference call
  • Enqueue: put the caller on hold and add it to a queue
  • Gather: accept spoken or keyed digits from the caller
  • Record: record a message
  • Reject: reject the call

I’d love to see what you build with Twilio Voice!

Miguel Grinberg is a Python Developer for Technical Content at Twilio. Reach out to him at mgrinberg [at] twilio [dot] com if you have a cool Python project you’d like to share on this blog!