cd /news/natural-language-processing/build-a-natural-language-ivr-with-te… · home topics natural-language-processing article
[ARTICLE · art-114693] src=dev.to ↗ pub= topic=natural-language-processing verified=true sentiment=· neutral

Build a Natural Language IVR with Telnyx Call Control and AI Inference

Telnyx has published a code example demonstrating a natural language IVR system that replaces traditional phone trees with conversational AI. The Python/Flask app uses Telnyx Call Control and AI inference to generate dynamic greetings and route callers to the correct department based on speech input, with fallback mechanisms and webhook signature verification.

read2 min views1 publishedAug 28, 2026

Nobody likes phone trees. "Press 1 for billing, press 2 for support." Miss an option? Start over. It is friction at its worst.

The voice-ivr-with-agent-backend

example replaces that with a natural language conversation. Callers just say what they need, and the app routes them to the right department.

Code: https://github.com/team-telnyx/telnyx-code-examples/tree/main/voice-ivr-with-agent-backend

A Python/Flask app that handles inbound calls with a conversational IVR:

Inbound Call
    -> answer with Call Control
    -> look up menu config from KV
    -> LLM generates a dynamic greeting
    -> gather(speech) — caller says what they need
    -> LLM routes intent to a department
    -> transfer call

The app combines four Telnyx primitives:

answer()

, speak()

, gather_using_speech()

, transfer()

telnyx.ai.openai.chat.completions.create()

for greetings and intent routingIVRAgent

class that tracks call state, turn count, and retry logicInstead of a hardcoded "Press 1 for billing," the app generates a conversational greeting from the KV config:

def generate_dynamic_menu_prompt(menu_config: dict) -> str:
    departments = menu_config.get("departments", [])
    dept_list = "\n".join(
        f"- {d['name']}: {d['description']}" for d in departments
    )
    return (
        f"You are an IVR assistant for {menu_config['business_name']}. "
        f"Available departments:\n{dept_list}\n\n"
        f"Greet the caller briefly and ask how you can help. "
        f"Keep it conversational and under 2 sentences."
    )

The LLM generates the greeting through the OpenAI-compatible Telnyx Inference binding. If it fails, the app falls back to a static greeting from the KV config.

When the caller speaks, the transcription is passed to route_intent_with_llm

. The LLM is instructed to respond with only the department name for reliable parsing:

completion = telnyx.ai.openai.chat.completions.create(
    model="telnyx-llm",
    messages=[
        {"role": "system", "content": system_prompt},
        {"role": "user", "content": user_input},
    ],
    max_tokens=20,
    temperature=0.1,
)
intent = completion.choices[0].message.content.strip().lower()

If the LLM fails or returns "unknown," the app falls back to keyword matching. After max_turns

(3 by default), the call transfers to a default operator.

The gather_using_speech

primitive plays a prompt and captures the caller's speech in one call:

telnyx.Call.gather_using_speech(
    call_control_id,
    payload=prompt,
    voice="female-en-US",
    language="en-US",
    max_duration=15,
)

Every webhook request is verified with Ed25519 signature verification:

telnyx.Webhook.construct_event(
    payload, signature, timestamp, TELNYX_PUBLIC_KEY
)

This prevents spoofed requests from triggering call actions.

The IVRAgent

class manages each call:

on_connect()

: fetch menu config, generate LLM greeting, speak, gatheron_gather_ended(speech)

: route intent via LLM, transfer or retryturn_count

and max_turns

before falling back to a default transferPUT /api/menu-config/<phone_number>

: update KV menu config dynamicallyGET /api/menu-config/<phone_number>

: retrieve current configGET /api/agents

: list active IVR agents (debugging)GET /health

: health check

git clone https://github.com/team-telnyx/telnyx-code-examples.git
cd telnyx-code-examples/voice-ivr-with-agent-backend
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
cp .env.example .env

Fill in your Telnyx API Key, Public Key, Connection ID, and default transfer number. Then:

python app.py
ngrok http 5000

Point your Call Control Application webhook to https://<your-ngrok-url>.ngrok-free.app/webhooks/voice

.

Before using with real callers, add:

Resources:

── more in #natural-language-processing 4 stories · sorted by recency
── more on @telnyx 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/build-a-natural-lang…] indexed:0 read:2min 2026-08-28 ·