Build an AI Phone Story Hotline – Interactive Storytelling with Telnyx Voice AI Telnyx released an open-source Python app that turns a phone number into an AI-powered interactive storytelling hotline, where callers choose a genre and make choices via keypad or voice to shape a real-time narrated story generated by Llama 3.3 70B. The 104-line app demonstrates creative AI over voice and a reusable pattern for conversational AI applications. Imagine calling a phone number, picking a genre — mystery, sci-fi, fantasy, horror, or romance — and listening to an AI narrate a story that adapts to your choices in real time. Press 1 to open the creaking door. Press 2 to check the window. The story branches, the AI continues, and every call is a new adventure. This is the AI Phone Story Hotline — a 104-line Python app built with Telnyx Call Control and AI Inference. No game engine, no branching script, no pre-written dialogue trees. The AI generates the story as you go, and your phone keypad shapes what happens next. In this walkthrough, you'll build it from scratch. Clone the repo, configure a phone number, and deploy in minutes. What You'll Build A phone number that anyone can call to start an interactive story: Caller dials in — Telnyx answers and greets them with a genre menu Caller picks a genre — press 1–5 for mystery, sci-fi, fantasy, horror, or romance AI generates chapter one — a 3–4 sentence story segment ending with two choices Caller chooses — press 1 or 2, or speak the choice aloud Story continues — AI generates the next chapter based on the choice, keeping full conversation context After 5 chapters — the AI brings the story to a satisfying ending The whole interaction is voice-driven: Text-to-Speech reads each chapter, and the caller responds with DTMF keypresses or speech. The AI model Llama 3.3 70B via Telnyx AI Inference handles the storytelling, and Call Control handles the telephony. Why This Is Interesting Most AI phone demos are business use cases — book a table, qualify a lead, reset a password. This one is different. It's creative AI — the model is writing fiction in real time, branching based on human input, and delivering it over a phone call. The storytelling format short chapters, two choices each keeps the AI responses tight and the call engaging. It also demonstrates a pattern that works for any conversational AI app: webhook-driven state machine + LLM with conversation memory + voice I/O. Once you see how the story hotline works, you can swap the storytelling prompt for any domain — a choose-your-own-adventure onboarding flow, an interactive quiz, a branching training simulation. Prerequisites - Python 3.8+ - A Telnyx account https://portal.telnyx.com with funded balance - A Telnyx API key https://portal.telnyx.com/api-keys - A Telnyx phone number https://portal.telnyx.com/numbers/my-numbers with voice enabled - A Call Control Application https://portal.telnyx.com/call-control/applications configured with your webhook URL ngrok https://ngrok.com for exposing your local server to Telnyx webhooks The Architecture Phone Call │ ▼ Telnyx Call Control webhook events │ ▼ Flask app app.py, 104 lines │ ├──► call.initiated → answer the call ├──► call.answered → TTS greeting + genre menu ├──► call.gather.ended → DTMF/speech input → AI Inference ├──► call.speak.ended → gather next choice → AI Inference └──► call.hangup → cleanup session │ ▼ Telnyx AI Inference Llama 3.3 70B │ ▼ Story chapter TTS back to caller The app is a state machine driven by Telnyx webhook events. Each event triggers the next action — answer, speak, gather, or hang up. The AI Inference call sits in the middle, generating story chapters from the running conversation history. Step 1: Clone and Configure git clone https://github.com/team-telnyx/telnyx-code-examples.git cd telnyx-code-examples/ai-phone-story-hotline-python cp .env.example .env pip install -r requirements.txt Edit .env with your credentials: TELNYX API KEY=KEY0123456789ABCDEF from portal.telnyx.com/api-keys TELNYX PUBLIC KEY= from portal.telnyx.com/api-keys public key STORY NUMBER=+13105551234 your Telnyx phone number AI MODEL=meta-llama/Llama-3.3-70B-Instruct Step 2: Understand the Code The entire app is 104 lines in a single file: app.py . Here are the key pieces. Webhook Signature Verification Every Telnyx webhook is signed with an Ed25519 key. The app verifies the signature before trusting any event: python @app.route "/webhooks/voice", methods= "POST" def handle voice : try: client.webhooks.unwrap request.get data as text=True , headers=dict request.headers except Exception: return jsonify {"error": "invalid signature"} , 401 This prevents spoofed webhook calls from triggering call actions. The State Machine Each call is tracked in an in-memory dict keyed by call control id : active calls = {} The state machine handles five events: Call initiated — store the call state and answer: if event type == "call.initiated" and p.get "direction" == "incoming": active calls ccid = {"state": "genre select", "conversation": , "chapters": 0} client.calls.actions.answer ccid Call answered — greet the caller with the genre menu using Text-to-Speech: elif event type == "call.answered": client.calls.actions.speak ccid, payload="Welcome to Story Hotline Choose your adventure. Press 1 for Mystery, 2 for Sci-Fi, 3 for Fantasy, 4 for Horror, 5 for Romance.", voice="female", language code="en-US" Speak ended — after TTS finishes, gather the caller's input. During genre selection, gather DTMF digits. During the story, gather speech or DTMF: elif event type == "call.speak.ended" and call: if call "state" == "genre select": client.calls.actions.gather ccid, input type="dtmf", timeout secs=10, min digits=1, max digits=1 else: client.calls.actions.gather ccid, input type="speech dtmf", end silence timeout secs=3, timeout secs=20, language code="en-US" The Storytelling Prompt When the caller picks a genre, the app builds the system prompt that guides the AI for the rest of the call: GENRES = {"1": "mystery", "2": "sci-fi", "3": "fantasy", "4": "horror", "5": "romance"} if call "state" == "genre select": genre = GENRES.get digits, "mystery" call "state" = "story" call "conversation" = {"role": "system", "content": f"You are an interactive {genre} storyteller on a phone hotline. " f"Tell a gripping story in short chapters 3-4 sentences each . " f"End each chapter with exactly TWO choices: 'Press 1 to...' or 'Press 2 to...'. " f"Make it vivid and cinematic. After 5 chapters, bring the story to a satisfying ending." } story start = call inference call "conversation" + {"role": "user", "content": "Begin the story."} call "conversation" .append {"role": "assistant", "content": story start} client.calls.actions.speak ccid, payload=story start, voice="female", language code="en-US" The prompt does three things: Format constraint — short chapters 3–4 sentences that fit naturally in a phone call Choice structure — exactly two options ending with "Press 1" or "Press 2" so the gather step can capture DTMF Ending condition — after 5 chapters, wrap up the story AI Inference The call inference helper sends the full conversation history to Telnyx AI Inference and returns the model's response: python def call inference messages, max tokens=250 : resp = requests.post INFERENCE URL, headers={"Authorization": f"Bearer {TELNYX API KEY}", "Content-Type": "application/json"}, json={"model": AI MODEL, "messages": messages, "max tokens": max tokens, "temperature": 0.9, }, timeout=20 resp.raise for status return resp.json "choices" 0 "message" "content" Temperature is set to 0.9 — high enough for creative storytelling, low enough to keep the narrative coherent. The model is Llama 3.3 70B Instruct, available on Telnyx AI Inference with an OpenAI-compatible API. The Conversation Loop Each time the caller makes a choice, the app appends it to the conversation and asks the AI for the next chapter: elif call "state" == "story": choice = digits or speech call "conversation" .append {"role": "user", "content": f"I choose option {choice}"} call "chapters" += 1 if call "chapters" = 5: call "conversation" -1 "content" += ". Bring the story to a dramatic conclusion." continuation = call inference call "conversation" call "conversation" .append {"role": "assistant", "content": continuation} client.calls.actions.speak ccid, payload=continuation, voice="female", language code="en-US" After 5 chapters, the app injects a final instruction to bring the story to an end. The AI sees the full conversation history on every call, so it maintains narrative continuity across all chapters. Step 3: Run the App Start the Flask server: python app.py In a separate terminal, expose your local server with ngrok: ngrok http 5000 Copy the HTTPS URL and configure it in the Telnx Portal https://portal.telnyx.com : - Go to Call Control Applications - Create or edit your application - Set the Webhook URL to https://