Voice AI agents are transforming customer service, sales, and support. But before an AI can talk to a caller, you need the voice infrastructure layer β a way to receive inbound phone calls, control the call flow, and respond programmatically. Without this foundation, your AI model has no way to pick up the phone.
Telnyx Call Control gives you this through webhooks. When someone calls your Telnyx number, your application receives events in real time and issues commands back β answer, speak, gather input, transfer, or hang up. This is the foundation every voice AI agent runs on.
Telnyx Call Control is a webhook-driven API for programmable voice. Instead of static IVR menus, your application receives HTTP events for every call state change and responds with commands.
The event loop works like this: Telnyx sends a webhook to your application, your app processes the event, your app issues a Call Control command, and Telnyx sends the next event when that command completes. This cycle repeats for the life of the call.
This event-driven pattern is what makes voice AI possible. Your app can insert AI inference β speech-to-text, LLM processing, text-to-speech β between receiving a caller's input and responding. The Call Control webhook loop gives you a hook into every moment of the conversation.
The call flow has three steps:
A customer calls your Telnyx number. Telnyx sends a call.initiated
webhook to your application with the caller's number, your number, and a call_control_id
you'll use for all subsequent commands on this call.
Your application answers and responds. Your webhook handler calls the answer
action to connect the call, then speak
to play a text-to-speech message to the caller. Each command triggers the next webhook event when it completes β call.answered
confirms the call connected, call.speak.ended
confirms the TTS finished.
The call completes. When the TTS finishes, Telnyx sends a call.speak.ended
event. Your app issues hangup
to end the call. The call.hangup
event confirms cleanup.
One webhook endpoint. A state machine driven by events.
Everything below works with any language or framework. All you need is the ability to receive HTTP requests and make HTTP requests.
When you receive a call.initiated
webhook, answer the call with:
curl -X POST https://api.telnyx.com/v2/calls/{call_control_id}/actions/answer \
-H "Authorization: Bearer YOUR_TELNYX_API_KEY" \
-H "Content-Type: application/json"
The call_control_id
comes from the call.initiated
webhook payload. It's the handle you use to issue every subsequent command on this call.
Once the call is connected, play a text-to-speech message:
curl -X POST https://api.telnyx.com/v2/calls/{call_control_id}/actions/speak \
-H "Authorization: Bearer YOUR_TELNYX_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"payload": "Thank you for calling. Your call is important to us. Goodbye.",
"voice": "female",
"language_code": "en-US"
}'
The payload
field is the text to speak. The voice
field accepts female
or male
. The language_code
field controls the language and accent β en-US
, en-GB
, es-ES
, and others are supported. When playback finishes, Telnyx sends a call.speak.ended
webhook event.
When you're done, terminate the call:
curl -X POST https://api.telnyx.com/v2/calls/{call_control_id}/actions/hangup \
-H "Authorization: Bearer YOUR_TELNYX_API_KEY" \
-H "Content-Type: application/json"
Telnyx sends a call.hangup
event to confirm the call has ended. The hangup_reason
field in the event payload tells you why the call terminated.
Telnyx signs every webhook request with an Ed25519 signature. Your application should verify this signature before processing the event to ensure the request actually came from Telnyx. The Telnyx SDKs handle this automatically β in Python, for example, you call client.webhooks.unwrap()
with the raw request body and headers.
This requires the TELNYX_PUBLIC_KEY
environment variable, which you can find in the Mission Control Portal.
Webhook-driven, not poll-driven. Every call state change arrives as an HTTP event β call.initiated
, call.answered
, call.speak.ended
, call.hangup
. Your app stays in control without polling or long-lived connections.
Full call control vocabulary. Answer, speak, gather DTMF or speech, transfer, conference, record β one API covers the entire call lifecycle. The same webhook loop works whether you're building a simple greeting or a multi-turn AI agent.
Private network infrastructure. Telnyx operates a private global IP network. Voice traffic doesn't traverse the public internet, reducing latency and improving call quality.
No per-minute markup games. Telnyx pricing is straightforward. You pay for what you use without inflated per-minute fees or hidden platform surcharges.
A complete working example β a Python Flask application that implements webhook signature verification, Call Control event handling, text-to-speech, and error handling β is available in the Telnyx code examples repository:
route-phone-calls-to-ai-agent-python on GitHub
The example includes a webhook endpoint (/webhooks/call
), a state machine that answers inbound calls and plays a TTS greeting, and proper error handling for authentication, rate limiting, and API errors. Clone it, add your credentials, and you have a running voice webhook handler.
To get started:
/webhooks/call
endpoint.TELNYX_API_KEY
and TELNYX_PUBLIC_KEY
environment variables, and run the app.Full API documentation is available at developers.telnyx.com.