https://a.storyblok.com/f/270183/1368x665/ea7dbac572/26mar_dev-blog_python-inbound-call.jpg

Handle an Inbound Phone Call with Python

Published on March 19, 2026

Time to read: 13 minutes

In this tutorial, you’ll learn how to use Python FastAPI to develop a voice application that receives inbound phone calls using the Vonage Voice API. Software that receives and routes phone calls helps companies improve customer experience. It enables more personalized and efficient support. This tutorial will guide you through three common customer support call flow scenarios:

  1. Playing a Text-To-Speech Message

  2. Connecting a Caller to an Agent

  3. Playing Hold Music

By the end of this tutorial, you'll understand how to effectively handle inbound calls with Vonage using a Python FastAPI app and ngrok. This guide will empower you to extend your app's capabilities to connect with users in real-time, making your application more interactive and engaging. Whether you're building a customer service solution or a communication tool, mastering these skills will enhance your project's functionality and user experience.

How Vonage Helps You Build Call Flows With Call Control Objects

Vonage voice applications employ Vonage Call Control Objects (NCCO) to construct call flows with NCCO actions. In other words, an NCCO is a JSON array of actions that can control inbound calls. A typical NCCO looks like this:

[
	{"action": "talk",
    	"text": "Hello, one moment please, your call is being forwarded to our agent."},
	{"action": "connect",
    	"from": VONAGE_NUMBER,
    	"endpoint": [{"type": 'phone',
       		       "number": YOUR_SECOND_NUMBER}]}
]

In this particular example, there are two actions: talk and connect. An action usually comes with a set of configurable key-value options:

The talk and connect actions are executed synchronously in the order they are listed in. Some actions can be configured to be asynchronous. This can come in handy when building a call flow in which a caller can select from a menu of options such as selecting a language. In such a scenario, you wouldn’t want the caller to have to wait through the entire menu of languages to proceed with the call. This is something you would want to do asynchronously. To understand how this works, review the asynchronous NCCO actions guide.

In summary, NCCOs provide the building blocks for crafting sophisticated call flows tailored to your specific needs.

A Brief Overview: Webhooks, Web Apps, and Tunneling

In order to complete this tutorial, we will rely on technical concepts and tools outside of Vonage that may be helpful in other areas of software development.

What Is a Webhook?

A webhook is how one web-based application pushes data or event notifications to another such application, enabling automatic communication instead of one application having to constantly poll the other for updates. In general, this means interacting over the web via an API using an HTTP request. In this particular tutorial, we will use a webhook to notify your web application that someone has called your virtual number and to return an NCCO of call flow actions.

What Is a Python FastAPI Web Application?

FastAPI is a high-performance web framework for building HTTP-based service APIs in Python. What makes FastAPI different from other Python web frameworks is its close alignment with Pydantic, a data validation library for Python. This coupling enables FastAPI to validate, serialize, and deserialize data. This provides a more declarative method of specifying the structure and types of data for incoming requests such as HTTP bodies and outgoing responses. FastAPI also automatically generates OpenAPI specs. All of these features combined make it especially suited for modern REST APIs, microservices development, and applications that require real-time functionality.

What Is Tunneling?

In this tutorial, we will create a web application and run it locally on your machine. Because it will be running locally, it cannot be accessed by the public internet, and if your web app cannot be accessed, then Vonage cannot make a request to the webhook. That’s where tunneling comes in. Tunneling exposes local servers to the public internet through temporary or static public URLs. ngrok is a software platform that provides this service.

In summary, we will create a Python FastAPI web app with a webhook and use ngrok to make your local application accessible via tunneling to a public URL so Vonage can actually post to the webhook and handle an inbound call as defined in the code.

Tutorial: Handle an Inbound Phone Call with Python

In this tutorial, we will use FastAPI to define a webhook that the Voice API can make a request to when someone calls your virtual number. We will use ngrok to expose the webhook to the public internet. And we will use Vonage to purchase a virtual number as well as the Voice API to complete the inbound call handling.

If you prefer, you can pull down the code for handling an inbound call from the Vonage Community GitHub.

Prerequisites

In order to complete this tutorial, you will need the following:

Tutorial Setup

Before we begin building, let’s get everything in order.

Vonage API Account

To complete this tutorial, you will need a Vonage API account. If you don’t have one already, you can sign up today and start building with free credit. Once you have an account, you can find your API Key and API Secret at the top of the Vonage API Dashboard.

This tutorial also uses a virtual phone number. To purchase one, go to Numbers > Buy Numbers and search for one that meets your needs.

Create an account with ngrok and install it

The Voice API must be able to access your webhook so that it can make requests to it, therefore, the endpoint URL must be accessible over the public internet.

In order to do that for this tutorial, we will use ngrok. Check out our ngrok tutorial to learn how to install and use it.

Create a project directory

In a terminal window, create a directory for this project and then navigate into it:

mkdir vonage-inbound-call && cd vonage-inbound-call

Let’s Start Building!

Now that you’ve got everything set up, it’s time to start building.

This tutorial will guide you through three different call flows:

  1. Playing a Text-To-Speech Message

  2. Connecting a Caller to an Agent

  3. Playing Hold Music

Before we get to each scenario, we will first create a Vonage application and prepare our environment for development by completing the following:

  1. Spin up an ngrok tunnel

  2. Create a Voice API application and link your number to it

  3. Create and activate a Python virtual environment

  4. Install dependencies

Step 1: Spin up an ngrok tunnel

The Voice API must be able to access your webhook so that it can make requests to it. In order to do this, the endpoint URL must be exposed to the public internet. This is what ngrok is for.

In a separate terminal window, run:

ngrok http 3000

This command will generate the public URLs your local server will tunnel to on port 3000. Take note of the public URL – it should look something like this:

Forwarding      https://0a6ec0a950eb.ngrok-free.app -> http://localhost:3000

You first need to create a Voice API application. The “application” we are referring to here is a container for the configuration and security information you need for the Voice API. It is not the same as the FastAPI web application we will also be creating.

Create your Voice API application in the developer dashboard by navigating to the Applications window from the left hand menu and clicking the “Create new application” button. This will open the application creation menu. Give your application the name inbound-call.

Under the Capabilities section, toggle the option for Voice, which will reveal a list of text fields. In the text field labeled Answer URL, provide the ngrok public URL amended with the webhook you defined in your FastAPI app. This will look something like: https://96b34a48a639.ngrok-free.app/webhooks/answer.

Click the “Generate new application” button.

A screenshot of the Create an application menu in the Vonage developer dashboard.A screenshot of the Create an application menu in the Vonage developer dashboard.Now that your application has been created, you can link your number to it by clicking on the Link button in the table of available numbers. Your application is now ready to answer inbound calls. The scenarios we will code tell the application what to do when the /answer webhook is called.

Step 3: Create and activate a Python virtual environment 

Python virtual environments allow you to install packages in a location isolated from the rest of your system. This helps prevent system-wide clutter and conflicts with different Python versions and packages. Check out our blog post about virtual environments to learn more.

In your project directory, run the following commands to create a virtual environment and activate a virtual environment:

python3 -m venv venv && source venv/bin/activate

You should now see your terminal prompt prefaced with (venv). To deactivate your virtual environment, run the deactivate command.

Step 4: Install dependencies

In order to install the dependencies we need for this tutorial, run the following:

pip install vonage python-dotenv 'fastapi[standard]'

Now we have everything we need to start coding. 

Scenario 1: Playing a Text-To-Speech Message

In this particular scenario, we will respond to an inbound call to a virtual number by playing a text-to-speech message.

Step 1: Write the code

Create a file called scenario-1.py that includes the following code:

from fastapi import FastAPI, Query
from vonage_voice import Talk

app = FastAPI()

@app.get('/webhooks/answer')
async def answer_call(from_: str = Query(..., alias='from')):
	from_ = '-'.join(from_)
	
return [Talk(text=f'Thank you for calling from {from_}').model_dump(by_alias=True, exclude_none=True)]

This code creates the /webhooks/answer endpoint. When this endpoint receives a request from the Voice API, it creates and iterates through a NCCO list with a Talk action. This is what is responsible for the text-to-speech a caller hears when they dial the number. To learn more about responding to calls with text-to-speech, check out our code samples.

Step 2: Try it out!

Now it’s time to try out your app.

In your terminal window, run:

fastapi dev --port 3000 scenario-1.py

This will spin up a local server with your webhook endpoint on port 3000. Your application is ready to receive calls!

Call the virtual number you linked to the application in the dashboard. If everything is working correctly, you should be greeted with the text-to-speech message you defined in your Python code.

After testing, terminate the server so you can code the next scenario. 

Scenario 2: Connecting a Caller to an Agent

In this scenario, we will include the NCCO action Connect which will connect the caller to an agent after completing the Talk action.

Step 1: Write the code

Create a new file called scenario-2.py and add the following code:

from fastapi import FastAPI, Query
from vonage_voice import Talk, Connect, NccoAction, PhoneEndpoint

VONAGE_VIRTUAL_NUMBER = 'Replace-this-text-with-your-Vonage-virtual-number'
AGENT_NUMBER = 'Replace-this-text-with-your-agent-number'

app = FastAPI()

@app.get('/webhooks/answer')
async def answer_call():

	ncco: list[NccoAction] = [
	Talk(text="Hello, one moment please while your call is being forwarded to our agent."),
	Connect(from_=VONAGE_VIRTUAL_NUMBER,
        	endpoint=[PhoneEndpoint(number=AGENT_NUMBER)]),]
    
	return [action.model_dump(by_alias=True, exclude_none=True) for action in ncco]

This code creates the /webhooks/answer endpoint. When this endpoint receives a request from the Voice API, it creates and iterates through a NCCO list with a Talk action and then a Connect action. The Connect action requires you to provide both your Vonage virtual number and an additional agent number for the variables VONAGE_VIRTUAL_NUMBER and AGENT_NUMBER, respectively. You can read more about connecting calls with the Voice API here.

Step 2: Try it out!

Stop any applications you may have running from any other scenarios in this tutorial and run:

fastapi dev --port 3000 scenario-2.py

To try out this particular scenario, you will need two phone numbers: One to call the virtual number with and another phone number to connect to. Dial your Vonage virtual number and if everything has been implemented correctly, you should be greeted with the text-to-speech message and then your second number (the agent number) should ring.

Scenario 3: Playing Hold Music

In this scenario, we handle an inbound call by placing the caller on hold and playing music until an agent is available. Then the call is transferred to the agent.

This is the call flow we will implement:

  1. When the call is answered, the caller will hear a text-to-speech message

  2. The caller will be placed on hold and music will be streamed to them

  3. After a simulated 30 seconds time delay, the call will be forwarded to a second NCCO

  4. The second NCCO plays a text-to-speech message and connects the caller to an agent

Because this particular scenario utilizes the Voice API to update a call in progress, we need the Vonage application ID and a private key to create a client. This ensures that the user making the update is authorized and has ownership of the call – it would be pretty unsettling if you suddenly heard hold music on a call with a friend! The application ID and private key can be acquired through the Vonage developer dashboard.  

Step 1: Add your application ID and private key to your environment variables

In the developer dashboard Applications menu, click on the application you created for this tutorial and then click “Edit.” Once the edit window opens, click on the button that says, “Generate public and private key.” This will trigger a download of your private key as a file with the extension .key. Keep this file private and do not share it anywhere it could be compromised. Click on the “Save changes” button and also note your Application ID.

A screenshot of the application Edit menu in the Vonage developer dashboard showing where to generate a public and private key.A screenshot of the application Edit menu in the Vonage developer dashboard showing where to generate a public and private key.A screenshot of the inbound-call application in the Vonage developer dashboard showing where you can find the application ID.A screenshot of the inbound-call application in the Vonage developer dashboard showing where you can find the application ID.Move your private key file to the same directory where your Python code is located. In the same directory, create a file called .env and add the following:

APPLICATION_ID=Replace-this-with-your-application-ID
PRIVATE_KEY=Replace-this-with-the-name-of-your-private-key-file

Step 2: Write your code

Create a new file called scenario-3.py and add the following code:

import os
from dotenv import load_dotenv
from threading import Timer
from fastapi import FastAPI, Query
from vonage import Auth, Vonage
from vonage_voice import Talk, Connect, NccoAction, PhoneEndpoint, Stream

load_dotenv()

APPLICATION_ID = os.getenv("APPLICATION_ID")
PRIVATE_KEY = os.getenv("PRIVATE_KEY")

VONAGE_VIRTUAL_NUMBER = '**********' # Replace with your Vonage virtual number
AGENT_NUMBER = '**********' # Replace with your "agent" number

AUDIO_URL = "https://download.samplelib.com/mp3/sample-12s.mp3"

app = FastAPI()

def transfer_call(call_id):
    	print("Transferring the call ... ")
   	 
    	ncco: list[NccoAction] = [
           	Talk(text="Hello, one moment please while your call is being forwarded to our agent."),
           	Connect(from_=VONAGE_VIRTUAL_NUMBER,
                   	endpoint=[PhoneEndpoint(number=AGENT_NUMBER)]),]
   	 
    	client = Vonage(Auth(application_id=APPLICATION_ID, private_key=PRIVATE_KEY,))    
   	 
    	client.voice.transfer_call_ncco(uuid=call_id, ncco=ncco)

@app.get('/webhooks/answer')
async def answer_call(call_id: str = Query(..., alias='uuid')):
    	print(f"The UUID for this call is:====> {call_id}")

    	Timer(30, transfer_call, [call_id]).start()
   	 
    	ncco: list[NccoAction] = [
    	Talk(text="Hello, our agents are assisting other customers. Please hold and we will connect you as soon as possible."),
    	Stream(streamUrl=[AUDIO_URL],loop=0),]
   	 
    	return [action.model_dump(by_alias=True, exclude_none=True) for action in ncco]

There is a lot going on in this code so let’s break it down.

First you will need to update the following variables:

  • VONAGE_VIRTUAL_NUMBER: Replace the placeholder text with your Vonage virtual number

  • AGENT_NUMBER: Replace the placeholder text with the “agent” phone number the caller will be transferred to

In this code, we define the /webhooks/answer endpoint. When this endpoint receives a request from the Voice API, it creates and iterates through a NCCO list with a Talk action and then a Stream action to play the hold music. 

The function transfer_call takes care of creating a Vonage client and then transferring the call using its unique identifier number.

Since we’re simulating what it would be like to be put on hold, the code uses a Timer object to call the transfer_call function after 30 seconds. 

Step 3: Try it out!

Now that you understand the code, let’s get it running. Make sure you don’t have any other applications running and then run:

fastapi dev --port 3000 scenario-3.py

To try out this particular scenario, you will need two phone numbers: One to call the virtual number with and another phone number to connect to. Dial your Vonage virtual number and if everything has been implemented correctly, you should be greeted with the text-to-speech message followed by hold music. After 30 seconds, the call will be transferred to the agent number you provided.

In Summary

In this tutorial, you learned how to handle inbound phone calls using the Vonage Voice API and a Python FastAPI application. You explored how Vonage Call Control Objects (NCCOs) define call flows and how webhooks enable your application to respond to incoming calls in real time. Through three practical scenarios, you implemented common customer support workflows: playing a text-to-speech message, connecting a caller to an agent, and placing a caller on hold with music before transferring the call.

Additionally, you configured a Voice API application, exposed a local FastAPI server using ngrok, and utilized the Voice API to update a call in progress. These foundational elements can significantly enhance customer interactions, improve response times, and streamline operations for businesses. For instance, you can build a conference call system, construct custom call flows, or track the performance of campaigns.

For those interested in further expanding your knowledge, consider exploring related articles on Python Voice Applications or API Integration to uncover more advanced techniques. Implementing these concepts can open opportunities for personalized customer experiences and automation that can drive business growth and efficiency.

Further Reading and Resources Referenced

Have a question or want to share what you're building?

Stay connected and keep up with the latest developer news, tips, and events.

Share:

https://a.storyblok.com/f/270183/400x400/2c4345217d/liz-acosta.jpeg
Liz AcostaDeveloper Advocate

Liz Acosta is a Developer Advocate at Vonage. While her career path from film student to marketer to engineer to Developer Advocate might seem unconventional, it’s pretty typical for Developer Relations! Liz loves pizza, plants, pugs, and Python.