Routing Incoming Phone Calls with Twilio Programmable Voice, Python and Django

February 21, 2020
Written by
David Fundakowski
Opinions expressed by Twilio contributors are their own

Routing Incoming Phone Calls with Twilio Programmable Voice, Python and Django

Call routing is an incredibly powerful tool for managing inbound calls, whether it be for personal or professional use. A person can route a call from VIP clients to a specific number, a call from their mother to a personal cell phone, and play a joke audio clip for their best friend when they call. This is the power of Twilio Programmable Voice.

In this tutorial, you will be setting up a Django project using virtual environments, creating a free Twilio account, provisioning a phone number, and finally, routing inbound calls based on the caller, to do things like play a recording, use text-to-speech to speak something, or forward the call to a personal cell phone.

By the end of this tutorial, you will be able to:

  • Set up a free Twilio account
  • Identify inbound calling numbers using Twilio Programmable Voice in a Django app
  • Answer phone calls using TwiML, the Twilio Markup Language
  • Route calls based on inbound number to say something with text-to-speech, forward to a personal cell phone, or immediately hang up

Tutorial Requirements

To follow this tutorial you need the following components:

  • Python 3.6 or newer. If your operating system does not provide a Python interpreter, you can go to to download an installer.
  • A text editor. Visual Studio Code is a great cross-platform option with extremely helpful plugins, linters, and extensions.
  • ngrok. We will use this handy utility to connect the Django application running on your system to a public URL that Twilio can connect to. This public URL will be used to expose your local web server to the public Internet. If you don’t have ngrok installed, you can download a copy for Windows, MacOS or Linux.
  • A smartphone with an active phone number to receive calls
  • A willing friend/family member or a 2nd phone to make calls to your Twilio phone number
  • A Twilio account. If you are new to Twilio create a free account now. If you use this link to register, you will receive a $10 credit when you upgrade to a paid account.

Configure Your Free Twilio Account

After you set up your free Twilio account using the link in the Tutorial Requirements, you can access the Twilio Console. The first step if you made a free trial account is to provision a phone number by clicking the “Get a Trial Number button”. Follow the prompts after clicking the link to choose your new number.

buy a twilio phone number

The Twilio dashboard should now have a phone number aligned to your account.

twilio phone number

If you already have an account, you can use an existing number or navigate to the Phone Numbers section of the Console and provision a new number. Click the ellipsis on the left side of the screen, click Phone Numbers, and then pick a number of your choosing:

twilio dashboard

phone numbers option in dashboard

Create a Python Virtual Environment

To get started, make a virtual environment where you will install both Django and the Twilio REST library.

For Linux/MacOS:

$ mkdir MyCallRouter
$ cd MyCallRouter
$ python3 -m venv callrouter-venv
$ source callrouter-venv/bin/activate
(callrouter-venv) $ pip3 install django==2.2 twilio

For Windows users:

$ md MyCallRouter
$ cd MyCallRouter
$ python -m venv callrouter-venv
$ callrouter-venv\Scripts\activate
(callrouter-venv) $ pip install django==2.2 twilio

Start Your Call Router Project

At this point, you are ready to start developing your call routing app using Django. With your virtual environment still activated, create your Django project, your call router app, do an initial model migration, and start a local server:

(callrouter-venv) $ django-admin startproject MyCallRouter .
(callrouter-venv) $ django-admin startapp callrouter
(callrouter-venv) $ python migrate
(callrouter-venv) $ python runserver

Open a browser, navigate to, and if you see a web page with a success message, you have successfully started your Django project and app!

django default application

Configure Your Project Settings and Files

Now that you have a successfully started project and provisioned a Twilio phone number, there are a few things that need to be added to the project in order for it to start receiving calls. If you haven’t stopped the local server yet, use Ctrl+C to stop your runserver in your terminal/command prompt.

First, add the callrouter app to the list of installed apps in the Django project. This list is located in the file located in the MyCallRouter directory:


    'callrouter.apps.CallrouterConfig' # new value

Next, add ngrok and your localhost IP address to the list of allowed hosts also located in


Finally, add a blank file to the callrouter app directory using your code editor. Your files and the app directory should now look like this


Add URL Routes and View for Call Logic

At this point in the project, you are ready to start writing the logic to handle inbound calls to your Twilio number. The goal here is to write a view in the callrouter app to execute the call routing code, add that view to a specific URL path, and then add that URL path as a webhook for your Twilio phone number in the Twilio Console. We will be adding the logic incrementally and testing along the way to confirm everything is working.

The first step is to get a basic voice response up and running that will say “Hello world!” to you when you call your Twilio phone number. Start by writing a basic view in callrouter/ that executes a say function:

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

from twilio.twiml.voice_response import VoiceResponse

# Create your views here.

def my_call_router(self):

    response = VoiceResponse()
    response.say("Hello world!")

    return HttpResponse(str(response))

Next up is adding URL pattern routes to generate a path to this view. First, add an entry to the URL patterns in the project-level MyCallRouter/ file:

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('callrouter/', include('callrouter.urls')) # new path

Now add a single entry in the new file in the callrouter directory that will call the my_call_router view:

from django.urls import path

from .views import my_call_router

urlpatterns = [
    path('', my_call_router, name='my_call_router')

Add a Webhook to the Twilio Phone Number

You now have a complete path to the my_call_router view and are just about ready to make your first call to your Twilio number. The next steps are to start a local server and then start ngrok to expose your local server to the public Internet. In a terminal/command prompt with your virtual environment activated, start your server:

(callrouter-venv) $ python runserver

Now open another terminal/command prompt and start ngrok:

$ ngrok http 8000

If ngrok started successfully, the terminal should look like below:

ngrok screenshot

You should now be able to open a browser and navigate to the HTTPS URL listed as Forwarding in the ngrok window, which should give you a “Page not found” error because we are asking for the / URL but the application only implements a view for /callrouter/.

page not found screenshot

With ngrok working successfully, you can now add a URL to your Twilio number in the UI. From the Twilio Console, click the ellipsis icon on the left side:

twilio dashboard

Then navigate to your Phone Numbers and select your number:

phone numbers option in dashboard

After clicking on your number, scroll down to the Voice options. In the section labelled “A Call Comes In”, add your ngrok URL and make sure the callrouter portion of the URL is included. For example, the complete URL would be similar to, but with a different ngrok subdomain:

configure voice webhook

Click “Save” at the bottom of the screen.

Now, pick up your personal phone, and call your Twilio number! If you are using a trial account, there will be a recorded message that gets played at the start of the call. Press any number to continue after this message.

If all of the directions were followed, ngrok is running, your local server is running, and your webhook is properly configured, you should be greeted with a “Hello world” message generated by the Django app!

Add More Complex View Logic

The last step of this tutorial is to add some basic if/else logic to determine what action should happen when different people call your Twilio number. In this section, you will add a condition to pass a call to your personal cell number, a condition to say a joke to an inbound caller, and a condition to hang up on all other numbers.

In the file in the callrouter app, replace the existing my_call_router code with the following code and swap out the 0’s for real phone numbers as indicated:

def my_call_router(request):
    response = VoiceResponse()

    if request.POST['From'] == '+10000000000': # Number of a friend
        response.say("What is brown and sticky? A stick.")
    elif request.POST['From'] == '+10000000000': # Number of a family member
        response.dial('+10000000000') # Your personal cell number

    return HttpResponse(str(response))

Note that the phone numbers must be given in the standard E.164 format.

Once you save the updated file, the Django app should automatically recognize the changes and restart the local server. At this point, you have an app that will route a certain inbound caller to your cell phone, say a quick joke to another specific caller, and hang up on everyone else!

Wrap Up

If this tutorial was your first Programmable Voice application setup, then congratulations on a big step! Twilio’s Programmable Voice API is extremely powerful and can facilitate much more functionality than what’s displayed in this tutorial, like conference calling and IVR mechanisms.

For next steps, head over to the Twilio Voice API docs and look at some other things that can be added to your app!

David Fundakowski is a senior data architect and full stack developer in Kansas City. Reach out through LinkedIn or Twitter if you want to connect!