{"slug": "build-a-natural-language-ivr-with-telnyx-call-control-and-ai-inference", "title": "Build a Natural Language IVR with Telnyx Call Control and AI Inference", "summary": "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.", "body_md": "Nobody likes phone trees. \"Press 1 for billing, press 2 for support.\" Miss an option? Start over. It is friction at its worst.\n\nThe `voice-ivr-with-agent-backend`\n\nexample replaces that with a natural language conversation. Callers just say what they need, and the app routes them to the right department.\n\nCode: [https://github.com/team-telnyx/telnyx-code-examples/tree/main/voice-ivr-with-agent-backend](https://github.com/team-telnyx/telnyx-code-examples/tree/main/voice-ivr-with-agent-backend)\n\nA Python/Flask app that handles inbound calls with a conversational IVR:\n\n``` php\nInbound Call\n    -> answer with Call Control\n    -> look up menu config from KV\n    -> LLM generates a dynamic greeting\n    -> gather(speech) — caller says what they need\n    -> LLM routes intent to a department\n    -> transfer call\n```\n\nThe app combines four Telnyx primitives:\n\n`answer()`\n\n, `speak()`\n\n, `gather_using_speech()`\n\n, `transfer()`\n\n`telnyx.ai.openai.chat.completions.create()`\n\nfor greetings and intent routing`IVRAgent`\n\nclass 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:\n\n``` php\ndef generate_dynamic_menu_prompt(menu_config: dict) -> str:\n    departments = menu_config.get(\"departments\", [])\n    dept_list = \"\\n\".join(\n        f\"- {d['name']}: {d['description']}\" for d in departments\n    )\n    return (\n        f\"You are an IVR assistant for {menu_config['business_name']}. \"\n        f\"Available departments:\\n{dept_list}\\n\\n\"\n        f\"Greet the caller briefly and ask how you can help. \"\n        f\"Keep it conversational and under 2 sentences.\"\n    )\n```\n\nThe 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.\n\nWhen the caller speaks, the transcription is passed to `route_intent_with_llm`\n\n. The LLM is instructed to respond with only the department name for reliable parsing:\n\n```\ncompletion = telnyx.ai.openai.chat.completions.create(\n    model=\"telnyx-llm\",\n    messages=[\n        {\"role\": \"system\", \"content\": system_prompt},\n        {\"role\": \"user\", \"content\": user_input},\n    ],\n    max_tokens=20,\n    temperature=0.1,\n)\nintent = completion.choices[0].message.content.strip().lower()\n```\n\nIf the LLM fails or returns \"unknown,\" the app falls back to keyword matching. After `max_turns`\n\n(3 by default), the call transfers to a default operator.\n\nThe `gather_using_speech`\n\nprimitive plays a prompt and captures the caller's speech in one call:\n\n```\ntelnyx.Call.gather_using_speech(\n    call_control_id,\n    payload=prompt,\n    voice=\"female-en-US\",\n    language=\"en-US\",\n    max_duration=15,\n)\n```\n\nEvery webhook request is verified with Ed25519 signature verification:\n\n```\ntelnyx.Webhook.construct_event(\n    payload, signature, timestamp, TELNYX_PUBLIC_KEY\n)\n```\n\nThis prevents spoofed requests from triggering call actions.\n\nThe `IVRAgent`\n\nclass manages each call:\n\n`on_connect()`\n\n: fetch menu config, generate LLM greeting, speak, gather`on_gather_ended(speech)`\n\n: route intent via LLM, transfer or retry`turn_count`\n\nand `max_turns`\n\nbefore falling back to a default transfer`PUT /api/menu-config/<phone_number>`\n\n: update KV menu config dynamically`GET /api/menu-config/<phone_number>`\n\n: retrieve current config`GET /api/agents`\n\n: list active IVR agents (debugging)`GET /health`\n\n: health check\n\n```\ngit clone https://github.com/team-telnyx/telnyx-code-examples.git\ncd telnyx-code-examples/voice-ivr-with-agent-backend\npython3 -m venv venv\nsource venv/bin/activate\npip install -r requirements.txt\ncp .env.example .env\n```\n\nFill in your Telnyx API Key, Public Key, Connection ID, and default transfer number. Then:\n\n```\npython app.py\nngrok http 5000\n```\n\nPoint your Call Control Application webhook to `https://<your-ngrok-url>.ngrok-free.app/webhooks/voice`\n\n.\n\nBefore using with real callers, add:\n\nResources:", "url": "https://wpnews.pro/news/build-a-natural-language-ivr-with-telnyx-call-control-and-ai-inference", "canonical_source": "https://dev.to/sonam_50a41a4ced7e6b4f3fa/build-a-natural-language-ivr-with-telnyx-call-control-and-ai-inference-24l5", "published_at": "2026-08-28 21:12:30+00:00", "updated_at": "2026-08-28 21:48:27.546938+00:00", "lang": "en", "topics": ["natural-language-processing", "ai-products", "developer-tools"], "entities": ["Telnyx", "OpenAI"], "alternates": {"html": "https://wpnews.pro/news/build-a-natural-language-ivr-with-telnyx-call-control-and-ai-inference", "markdown": "https://wpnews.pro/news/build-a-natural-language-ivr-with-telnyx-call-control-and-ai-inference.md", "text": "https://wpnews.pro/news/build-a-natural-language-ivr-with-telnyx-call-control-and-ai-inference.txt", "jsonld": "https://wpnews.pro/news/build-a-natural-language-ivr-with-telnyx-call-control-and-ai-inference.jsonld"}}