Warm Phone Call Transfers with Python, Flask and Twilio Voice

Warm Phone Call Transfers with Python, Flask and Twilio Voice

Have you ever had your call disconnect while you were on hold with customer service and waiting for a transfer from one department to another? You probably felt like this when the call dropped:

A warm phone call transfer, where a caller is on the phone with an agent then the agent brings on another agent and introduces them to the customer, shouldn’t be so difficult. There are only a few steps that need to be done properly for a warm phone call transfer to work, as we can visualize in the following diagram.

As the diagram shows, a customer, who in this post we’ll refer to as Trinity, dials into a typical phone number for service. An agent, known as Agent Smith, answers the calls. However, Agent Smith discovers he can’t solve the problem and needs to connect Trinity to Agent Johnson, who’s better equipped to handle the issue. Agent Smith dials in Agent Johnson, introduces Agent Johnson to the customer, then hangs up so Trinity and Agent Johnson can solve the problem and finish the support call.
In this blog post, we’ll use Twilio’s Voice API and a bit of Python code to implement the above scenario and help rid the world of warm transfer failures so that this problem never happens to your callers.

What We’ll Need

Let’s walk through how to perform a warm transfer with Python and a simple web application built with the Flask framework. The following dependencies will be used to create and run the application:

We’ll also need three phones with phone numbers to represent Trinity, Agent Smith and Agent Johnson. If you don’t have multiple phones lying around for testing, you can use Twilio Client to turn three browser tabs into different phones. Use this Twilio Client quickstart project with the deploy to Heroku button to create phones as you need them.

There is also a GitHub repository with the code we’ll be creating in this post. It’s a handy reference in case you make a typo or get stuck somewhere along the way.

Also, if Python isn’t your jam and Java is more your speed, check out my colleague’s post that handles a warm transfer in that language instead.

Now that we know our dependencies, let’s get our coding environment established.

Python Environment Setup

It’s time to get our Python development environment ready for this application. In this section we’ll handle the following steps to make sure you’re ready to run the code:

  1. Check the Python installation
  2. Set up a new virtualenv then activate it
  3. Install a couple of dependencies via pip
  4. Fire up Ngrok to handle localhost tunneling
  5. Obtain a phone number for our customer service line
  6. Export a few critical environment variables for our application to use

Setup Step 1: Check the Python installation

If you’re on Mac OS X or Linux, you’ve already got Python installed on your system. Check the version by going into the terminal and typing python --version. You’ll see the precise version number, something like Python 2.7.6. If you’re on Python 2.6 or greater, you’re good to go.

For Windows, make sure you download and install the Python .exe installation package. Either Python 2 or 3 is fine. My recommendation is to use Python 3.5 unless you have a pressing reason to use an earlier version, because the Python community is now migrating en masse to the Python 3.

Once Python is installed on your system and you can run it with the python command, we’re ready to set up a virtualenv.

Setup Step 2: Set up a new virtualenv and activate it

Virtualenv is a dependency isolation library that makes it much easier to switch between Python versions and various code packages you’re using on different projects. Create a virtualenv in a location you’ll remember using the following command. In my case, I have a folder called Envs under my home directory that keeps my virtualenv organized.

Now we have our virtualenv activated and should see our command prompt show the name of our virutalenv, such as (warmtransfer)$. With the virtualenv in place and activated, we can use pip to install our dependencies.

Setup Step 3: Install dependencies with pip

pip handles Python library installations. With your virtualenv still activated, run the following command to install our Flask and Twilio helper library dependencies.

You can likely install the newest versions of the Flask and Twilio packages but it’s preferable to peg dependencies to a specific version number just in case there are future backwards-incompatible modifications. With those libraries installed, next we need to launch Ngrok to create a localhost tunnel so that our application will be reachable by Twilio.

Setup Step 4: Fire up Ngrok

Download and install the Ngrok application. From the command line, run Ngrok to create a localhost tunnel to the port our application will run on using the following command:

./ngrok http 5000

We should see a screen like the following that shows Ngrok started properly. Take note of the second line that begins with “Forwarding”. That’s the URL we need to set for the BASE_URL environment variable as well as our Twilio webhook.


With Ngrok up and running, we can turn our attention to setting up a phone number for Trinity  to call into for customer service.

Setup Step 5: Obtain a number for our customer service line

Where do we get the phone number to use as a customer service line for our warm phone call transfer example? That phone number comes from our Twilio account.

If you don’t already have a Twilio account, you can sign up for a free trial account. After signing up, upgrade to a paid account as we’ll need its ability to call multiple telephone numbers other than our own verified number.

Obtain a Twilio phone number with calling capabilities that we can configure for our warm call transfer example. Go to the Twilio numbers page which should look like the following screenshot:


Click on the “Buy a Number” button and purchase a phone number with calling capabilities in the location of your choice. After obtaining the phone number, you’ll get to set up voice calling on the numbers configuration page:


As shown above, within the phone number’s screen there is a voice webhook we need to configure. WIthin the Request URL text box, type in the Ngrok Forwarding URL, such as “https://5341e8cd.ngrok.io” plus “/call” because that is the route we are going to write in the next section to respond to the webhook with appropriate TwiML. Make sure to scroll down and click the “Save” button at the bottom of the screen so our changes take effect.

With our Python development environment established, Ngrok started and a Twilio phone number in hand, we just need to use that information to set environment variables our application will need.

Setup Step 6: Set environment variables

How to set environment variables depends on your operating system. Here are some handy guides if you’re on Windows, Mac OS X or Ubuntu Linux that’ll help with the specific steps.

Note that if you don’t set these environment variables now there are spots within the application code to set them. However, in Python it’s a good practice to use environment variables instead of hardcoding these sensitive values.

There are six variables that need to be set:

  • TWILIO_ACCOUNT_SID – found on your Twilio account dashboard
  • TWILIO_AUTH_TOKEN – also found on your Twilio account dashboard
  • CUSTOMER_SERVICE_NUMBER – a number you’ve purchased on Twilio
  • AGENT1_NUMBER – the phone number of the agent who will first answer the call. For testing purposes this is most likely your own phone number, specified in the +12025551234 format
  • AGENT2_NUMBER – the phone number of the agent who will be transferred in, in the +12025551234 format
  • BASE_URL – the forwarding URL given by Ngrok when it starts up, for example “https://5341e8cd.ngrok.io”

On Mac OS X and Linux, you can set environment variables with the export shell command. For example, you’d run export six times to set the following variables:

Now that our environment is ready to go, let’s write some code to handle inbound customer service phone calls.

Calling Agent Smith

Our environment is established and we have a Twilio phone number, so it’s time to write some Python code to handle an incoming phone call.


We’ll start out by importing our Flask and Twilio dependencies that we installed earlier. We also need a bit of boilerplate to initialize the Flask application and the Twilio helper library. This code will run the application in Flask’s debug mode in case we run into any issues while building our application.

Next our application needs to obtain the environment variables we exported to our shell. The os module is used to get the environment variables for customer service number, the agent phone numbers and the base URL that Ngrok gives us when we fire it up.

We could run our Flask web application at this point but without any routes it doesn’t have any pages to load. Create a couple routes now, named inbound_call and conference_line, as shown below.

The above code in inbound_call handles an inbound call by generating and returning a TwiML response when Twilio performs an HTTP POST request to our application. We also use our instantiated TwilioRestClient to dial an outbound phone call to Agent Smith, our first agent who will take the customer’s call.

The second function, conference_line, creates a TwiML response that looks like the following XML. We don’t need to hand code the TwiML because the Python helper library generates this when we call the Response, dial and conference functions.

Our application is now a simple conference call application. Let’s give the initial version of our warm call transfer application a spin to make sure everything works so far. Run the Flask app from the command line with the following command.

python app.py

With the application running and exposed to Twilio using Ngrok, we can test the application by calling the customer service Twilio number.

Now we’re connected to our initial customer service agent. However, let’s assume the agent can’t answer the question and needs to connect the caller to another person who knows what they’re talking about.

Bringing in Agent Johnson

We need to slightly modify our application to allow for the warm transfer. The gather function in our response will generate a TwiML response for us that instructs Twilio how to handle the phone call.

In the modified inbound_call route we create TwiML for a conference line and then dial our first agent into the conference call. A conference is actually the easiest way to handle adding and removing people from a call. Our customer calling in doesn’t even need to know it’s a conference call, it’s seamless to her as Agent Smith and Agent Johnson add and remove themselves from the line.

The conference_line route creates the TwiML to put Agent Smith in the conference call. The hangupOnStar=True parameter allows Agent Smith to put the call on hold by pressing the * key on the dial pad. Then Agent Smith gets back into the call by pressing any number key on the dial pad, which causes the gather TwiML to add the agent back to the conference with our customer.

We’re ready to test out our application one more time to see it all put together. Here is a review of the entire app.py file we wrote together throughout this post.

With the above code written and running, call into our customer service phone number and the app will use the Twilio Voice API to call Agent Smith. When Agent Smith picks up, press * plus a number on the keypad to “hang up” which hits the /add-agent route and calls out to Agent Johnson.

Agent Smith will be placed back into the call. Agent Smith then transfers to Agent Johnson, they’re both on the line, then Agent Smith drops off while Agent Johnson continues on with Trinity.

Warm Transfer Complete

Now that’s how a warm call transfer should be done!

In this post, we solved the problem of poorly handled call transfers. Now when your customers call in, Agent Smith will be ready to hand off to Agent Johnson when the need arises.


If you’re looking for more tutorials and Python code to do additional voice communications to your application, check out one of my favorite tutorials named Click to Call that uses Python and Flask to allow visitors on the web to easily call your support or sales lines on the phone.

That’s all for now! If you run into any issues throughout the tutorial feel free to drop a comment below or contact me via:

  • Email: makai@twilio.com
  • GitHub: Follow makaimc for repository updates
  • Twitter: @mattmakai
  • http://twitter.com/robertwelbourn Robert Welbourn

    Matt, what happens to all the conference objects that get created during these calls? Do they eventually get deleted?