<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: anusha</title>
    <description>The latest articles on DEV Community by anusha (@botoclock).</description>
    <link>https://dev.to/botoclock</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4004284%2Fc7c15289-9b22-479c-b291-b944235c5687.jpg</url>
      <title>DEV Community: anusha</title>
      <link>https://dev.to/botoclock</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/botoclock"/>
    <language>en</language>
    <item>
      <title>I Built a Voice Agent That Switches Languages Mid-Call on Telnyx</title>
      <dc:creator>anusha</dc:creator>
      <pubDate>Wed, 05 Aug 2026 23:02:02 +0000</pubDate>
      <link>https://dev.to/botoclock/i-built-a-voice-agent-that-switches-languages-mid-call-on-telnyx-1g51</link>
      <guid>https://dev.to/botoclock/i-built-a-voice-agent-that-switches-languages-mid-call-on-telnyx-1g51</guid>
      <description>&lt;p&gt;Translation apps interpret between two people speaking different languages. Code-switching is different: one caller switches languages mid-conversation and the agent follows them. No interpreter, no restart, no separate vendors — one Telnyx AI Assistant that detects the spoken language on every turn and replies in kind.&lt;/p&gt;

&lt;p&gt;The Telnyx code example is:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/ai-multilingual-code-switching-agent-python" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/telnyx-code-examples/tree/main/ai-multilingual-code-switching-agent-python&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is a Python Flask app with a simple browser page showing a phone number. No database, no Cloud Storage. Call the number, speak in English, switch to Spanish mid-conversation, and the agent follows you.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Flow
&lt;/h2&gt;

&lt;p&gt;A customer calls a support line. They start in English, switch to Spanish for a sensitive question, then switch back. The agent detects the language from their speech on every turn and replies in that language. No language picker menu, no "press 1 for English." The caller just speaks.&lt;/p&gt;

&lt;p&gt;One phone number, one AI Assistant, five languages — English, Spanish, Portuguese, Hindi, and Mandarin.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Code-Switching Works
&lt;/h2&gt;

&lt;p&gt;The entire behavior lives in the assistant's instructions. There is no application-layer language detection, no routing, no manual STT/LLM/TTS pipeline. The LLM follows plain English instructions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;listen carefully to the caller. detect the language they are speaking on every turn.
reply in the same language the caller is using right now.
if the caller switches language mid-conversation or mid-sentence, switch with them.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The STT (Deepgram nova-3 with &lt;code&gt;language: "auto"&lt;/code&gt;) transcribes in whatever language the caller speaks. The LLM follows the instructions. The TTS (&lt;code&gt;voice ultra katie&lt;/code&gt;) renders the reply in that language. One platform, one API key.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Is Different From Translation
&lt;/h2&gt;

&lt;p&gt;The existing &lt;code&gt;ai-real-time-translation-bridge-python&lt;/code&gt; example connects two callers who speak different languages and translates between them. That is interpretation — two people, two languages, one bridge.&lt;/p&gt;

&lt;p&gt;This example is code-switching — one caller, multiple languages, one agent. The agent is not translating. It is the agent. It replies in whatever language you speak, switches when you switch, and never says "I did not understand" or asks you to pick a language.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Technical Decisions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;deepgram/nova-3&lt;/code&gt; with &lt;code&gt;language: "auto"&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Per the AI Assistants docs: "To enable a multilingual agent, set the transcription model to &lt;code&gt;deepgram/nova-3&lt;/code&gt;." The &lt;code&gt;language: "auto"&lt;/code&gt; setting lets nova-3 auto-detect the spoken language on every turn. Note: &lt;code&gt;language: "multi"&lt;/code&gt; is not valid and returns a 400 error. Use &lt;code&gt;"auto"&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;voice ultra katie&lt;/code&gt; in the instructions
&lt;/h3&gt;

&lt;p&gt;The existing phone assistant examples use &lt;code&gt;voice ultra katie&lt;/code&gt; in the instructions. Ultra supports 36+ languages, so one voice handles all five demo languages without switching voices per language.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;moonshotai/Kimi-K2.6&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;A native Telnyx model — no external API key required.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Flask App Is Minimal
&lt;/h2&gt;

&lt;p&gt;Since the Voice AI Assistant handles the entire conversation, the Flask app only needs to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create/reuse the assistant via &lt;code&gt;provision_assistant.py&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Serve a page showing the inbound phone number&lt;/li&gt;
&lt;li&gt;Log webhook events for observability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is no call-control logic, no application-layer STT, no manual TTS. The conversation runs entirely on Telnyx.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run It
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/team-telnyx/telnyx-code-examples.git
&lt;span class="nb"&gt;cd &lt;/span&gt;telnyx-code-examples/ai-multilingual-code-switching-agent-python
&lt;span class="nb"&gt;cp&lt;/span&gt; .env.example .env
python provision_assistant.py
python app.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open the browser, call the number, speak in any language. Switch mid-conversation and the agent follows.&lt;/p&gt;

&lt;h2&gt;
  
  
  Related Examples
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/ai-real-time-translation-bridge-python" rel="noopener noreferrer"&gt;&lt;code&gt;ai-real-time-translation-bridge-python&lt;/code&gt;&lt;/a&gt; — two-caller interpreter&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/ai-language-learning-phone-tutor-python" rel="noopener noreferrer"&gt;&lt;code&gt;ai-language-learning-phone-tutor-python&lt;/code&gt;&lt;/a&gt; — phone-based language tutor&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/language-learning-flashcards-python" rel="noopener noreferrer"&gt;&lt;code&gt;language-learning-flashcards-python&lt;/code&gt;&lt;/a&gt; — browser-based pronunciation scoring&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>telnyx</category>
      <category>ai</category>
      <category>devrel</category>
    </item>
    <item>
      <title>I Built a Language Tutor That Listens and Scores Your Pronunciation</title>
      <dc:creator>anusha</dc:creator>
      <pubDate>Wed, 05 Aug 2026 22:11:18 +0000</pubDate>
      <link>https://dev.to/botoclock/i-built-a-language-tutor-that-listens-and-scores-your-pronunciation-23f5</link>
      <guid>https://dev.to/botoclock/i-built-a-language-tutor-that-listens-and-scores-your-pronunciation-23f5</guid>
      <description>&lt;p&gt;Language learning apps work fine on a screen, but the hardest part is the thing screens avoid: actually speaking out loud and getting immediate feedback on whether you said it right.&lt;/p&gt;

&lt;p&gt;Most voice-based language tools either play a recording or transcribe your speech. They don't do both in an interactive loop. I wanted to build something that does — something that plays a phrase, listens to you repeat it, and tells you whether you got it right.&lt;/p&gt;

&lt;p&gt;The Telnyx code example is:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/language-learning-flashcards-python" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/telnyx-code-examples/tree/main/language-learning-flashcards-python&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is a Python Flask app with a browser UI. No phone number, no webhook tunnel, no Cloud Storage.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Loop
&lt;/h2&gt;

&lt;p&gt;The app uses all three Telnyx AI primitives in one interactive round-trip:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;TTS speaks&lt;/strong&gt; — an Ultra voice (Camila for Spanish, Valerie for French) plays a flashcard phrase. Audio autoplays.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You repeat&lt;/strong&gt; — click Record, speak the phrase, click Stop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;STT transcribes&lt;/strong&gt; — Whisper transcribes your speech.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inference scores&lt;/strong&gt; — Kimi-K2.6 compares your transcription against the target phrase and returns a score: correct, close, or wrong, plus a one-sentence tip.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;One platform, one API key, no external services.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Is Different From a Translator
&lt;/h2&gt;

&lt;p&gt;The existing &lt;code&gt;ai-content-translator-python&lt;/code&gt; example does STT → translate → TTS. It's one-directional: you speak, the app translates. This example is interactive: the app speaks to you, you speak back, the app evaluates you. The flow is completely different even though it uses the same three Telnyx primitives.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Voices
&lt;/h2&gt;

&lt;p&gt;The app uses native Ultra voices per language, not one English voice with &lt;code&gt;language_boost&lt;/code&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Language&lt;/th&gt;
&lt;th&gt;Voice&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Spanish&lt;/td&gt;
&lt;td&gt;Camila (es, Female)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;French&lt;/td&gt;
&lt;td&gt;Valerie (fr-FR, Female)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;To add more languages, enumerate voices via &lt;code&gt;GET /v2/text-to-speech/voices&lt;/code&gt; and add them to &lt;code&gt;LANGUAGE_VOICE_MAP&lt;/code&gt; in &lt;code&gt;app.py&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the Scoring Works
&lt;/h2&gt;

&lt;p&gt;The Inference call uses a system prompt that tells Kimi to compare the target phrase against the user's spoken text and return JSON with a score and a tip:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"correct"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"feedback"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Great job, your pronunciation matched the target phrase perfectly!"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Kimi-K2.6 is a reasoning model — it takes ~10 seconds to think before scoring, but the result is more nuanced than a simple string comparison. It accounts for minor accent differences (correct), noticeable errors like wrong or missing words (close), and completely wrong speech (wrong).&lt;/p&gt;

&lt;h2&gt;
  
  
  The Decks
&lt;/h2&gt;

&lt;p&gt;The app ships with 4 pre-built flashcard decks:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Deck&lt;/th&gt;
&lt;th&gt;Language&lt;/th&gt;
&lt;th&gt;Cards&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Spanish — Greetings&lt;/td&gt;
&lt;td&gt;Spanish&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Spanish — Numbers&lt;/td&gt;
&lt;td&gt;Spanish&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Spanish — Common phrases&lt;/td&gt;
&lt;td&gt;Spanish&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;French — Greetings&lt;/td&gt;
&lt;td&gt;French&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Add more by editing &lt;code&gt;FLASHCARD_DECKS&lt;/code&gt; in &lt;code&gt;app.py&lt;/code&gt;. Each card has a &lt;code&gt;phrase&lt;/code&gt; (what TTS speaks and what you repeat) and a &lt;code&gt;translation&lt;/code&gt; (shown as a hint).&lt;/p&gt;

&lt;h2&gt;
  
  
  Run It
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/team-telnyx/telnyx-code-examples.git
&lt;span class="nb"&gt;cd &lt;/span&gt;telnyx-code-examples/language-learning-flashcards-python
&lt;span class="nb"&gt;cp&lt;/span&gt; .env.example .env    &lt;span class="c"&gt;# fill in TELNYX_API_KEY&lt;/span&gt;
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
python app.py           &lt;span class="c"&gt;# starts on http://127.0.0.1:5050&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open the browser, pick a deck, listen, repeat, get scored.&lt;/p&gt;

&lt;h2&gt;
  
  
  One Thing to Watch
&lt;/h2&gt;

&lt;p&gt;Kimi-K2.6 is a reasoning model. It spends tokens thinking before it produces the JSON score. If &lt;code&gt;max_tokens&lt;/code&gt; is too low (I tried 200 at first), it runs out of tokens during reasoning and returns empty content. The fix is &lt;code&gt;max_tokens: 1000&lt;/code&gt; — enough for reasoning + the short JSON response. This is a Kimi-specific behavior; a non-reasoning model like Llama-3.3-70B would work with 200 tokens, but Kimi is the recommended Telnyx model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Related Examples
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/ai-language-learning-phone-tutor-python" rel="noopener noreferrer"&gt;&lt;code&gt;ai-language-learning-phone-tutor-python&lt;/code&gt;&lt;/a&gt; — phone-based language tutor&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/ai-content-translator-python" rel="noopener noreferrer"&gt;&lt;code&gt;ai-content-translator-python&lt;/code&gt;&lt;/a&gt; — STT + translate + TTS pipeline&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/multi-character-narrator-python" rel="noopener noreferrer"&gt;&lt;code&gt;multi-character-narrator-python&lt;/code&gt;&lt;/a&gt; — multi-voice TTS with emotions&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>telnyx</category>
      <category>ai</category>
      <category>devrel</category>
    </item>
    <item>
      <title>I Wanted to Hear Every Telnyx Voice in One Scene, So I Built a Multi-Character Narrator</title>
      <dc:creator>anusha</dc:creator>
      <pubDate>Wed, 05 Aug 2026 00:22:44 +0000</pubDate>
      <link>https://dev.to/botoclock/i-wanted-to-hear-every-telnyx-voice-in-one-scene-so-i-built-a-multi-character-narrator-2ki8</link>
      <guid>https://dev.to/botoclock/i-wanted-to-hear-every-telnyx-voice-in-one-scene-so-i-built-a-multi-character-narrator-2ki8</guid>
      <description>&lt;p&gt;Telnyx ships over 700 Ultra voices across 36 languages with sub-100ms time-to-first-byte. The voices are not the problem. Hearing them is.&lt;/p&gt;

&lt;p&gt;The docs list three. The Voices API returns 4,000+ across every provider. Voice pickers play a fixed sample sentence per voice. None of that tells you how a voice handles emotion, pacing, or character inside a real scene.&lt;/p&gt;

&lt;p&gt;So I built a small app that lets you do exactly that. You write a short scene with a few characters, assign each character a different Telnyx Ultra voice and an SSML emotion, and render the whole thing into one MP3. Every voice speaks in character, in context, in one continuous audio file.&lt;/p&gt;

&lt;p&gt;The Telnyx code example is:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/multi-character-narrator-python" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/telnyx-code-examples/tree/main/multi-character-narrator-python&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Use Case
&lt;/h2&gt;

&lt;p&gt;Voice pickers exist. They play a fixed sample sentence per voice. What they do not do is let you hear a voice inside a real scene — a tense argument, a calm narrator, a panicked character, a reassuring guide — because a single sample sentence does not tell you how a voice handles emotion, pacing, or character.&lt;/p&gt;

&lt;p&gt;This example solves that. You write a short scene with a few characters. Each character gets a different Telnyx Ultra voice. Each character gets an SSML emotion. You hit render. The app fans out parallel TTS calls, stitches the per-line audio in script order, and plays you one continuous MP3 with every voice speaking in character.&lt;/p&gt;

&lt;p&gt;The default scene is the Ides of March from Julius Caesar. Five characters, ten lines, five distinct voices, five different emotions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cassius — determined, plotting the assassination&lt;/li&gt;
&lt;li&gt;Caesar — surprised, realizing the betrayal&lt;/li&gt;
&lt;li&gt;Brutus — apologetic, justifying the act&lt;/li&gt;
&lt;li&gt;Mark Antony — angry, mourning the fallen leader&lt;/li&gt;
&lt;li&gt;Narrator — calm, setting the scene&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One render, one MP3, every voice in context. That is the demo.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Eight Curated Ultra Voices
&lt;/h2&gt;

&lt;p&gt;The app ships with eight pre-built Telnyx Ultra voices curated for the most common use cases. Each one is a real Telnyx voice with a UUID voice ID that works on the REST endpoint.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Voice&lt;/th&gt;
&lt;th&gt;Gender&lt;/th&gt;
&lt;th&gt;Language&lt;/th&gt;
&lt;th&gt;Best Use Case&lt;/th&gt;
&lt;th&gt;Sound Profile&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Asher&lt;/td&gt;
&lt;td&gt;Male&lt;/td&gt;
&lt;td&gt;en&lt;/td&gt;
&lt;td&gt;Voice Assistants &amp;amp; Media&lt;/td&gt;
&lt;td&gt;Smooth, dynamic, podcaster-style tone&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Callie&lt;/td&gt;
&lt;td&gt;Female&lt;/td&gt;
&lt;td&gt;en&lt;/td&gt;
&lt;td&gt;Coaching &amp;amp; Onboarding&lt;/td&gt;
&lt;td&gt;High energy, encouraging, friendly tone&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Clara&lt;/td&gt;
&lt;td&gt;Female&lt;/td&gt;
&lt;td&gt;en-US&lt;/td&gt;
&lt;td&gt;General Purpose IVR/AI&lt;/td&gt;
&lt;td&gt;Clear, standard US accent, versatile pacing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Howard&lt;/td&gt;
&lt;td&gt;Male&lt;/td&gt;
&lt;td&gt;en-US&lt;/td&gt;
&lt;td&gt;Conversational Agents&lt;/td&gt;
&lt;td&gt;Deep, reassuring, highly trustworthy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Allie&lt;/td&gt;
&lt;td&gt;Female&lt;/td&gt;
&lt;td&gt;en-US&lt;/td&gt;
&lt;td&gt;Casual &amp;amp; Interactive AI&lt;/td&gt;
&lt;td&gt;Conversational flow, natural pauses&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Jasper&lt;/td&gt;
&lt;td&gt;Male&lt;/td&gt;
&lt;td&gt;en-GB&lt;/td&gt;
&lt;td&gt;Finance &amp;amp; Healthcare&lt;/td&gt;
&lt;td&gt;Calm, authoritative, precise delivery&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Skyler&lt;/td&gt;
&lt;td&gt;Neutral&lt;/td&gt;
&lt;td&gt;en&lt;/td&gt;
&lt;td&gt;Modern Brand Voice&lt;/td&gt;
&lt;td&gt;Casual, tech-forward, friendly vibe&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Arvin&lt;/td&gt;
&lt;td&gt;Male&lt;/td&gt;
&lt;td&gt;en&lt;/td&gt;
&lt;td&gt;Navigation &amp;amp; Directives&lt;/td&gt;
&lt;td&gt;Steady, clear cadence for detailed guidance&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Pick any of the eight for any character. Click Preview to hear the voice with the selected emotion before rendering the whole scene.&lt;/p&gt;

&lt;h2&gt;
  
  
  Twenty Ultra SSML Emotions
&lt;/h2&gt;

&lt;p&gt;Ultra supports inline SSML emotion tags placed before the text:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;emotion&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"excited"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;Great news — your order shipped early!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The app exposes all twenty Ultra SSML emotions as a per-character dropdown. Primary emotions: angry, excited, content, sad, scared. Additional: happy, enthusiastic, curious, calm, grateful, affectionate, sarcastic, surprised, confident, hesitant, apologetic, determined, frustrated, disappointed.&lt;/p&gt;

&lt;p&gt;Each character in the default Julius Caesar scene is auto-assigned an emotion that fits the role. Cassius is determined. Caesar is surprised. Brutus is apologetic. Mark Antony is angry. The Narrator is calm. Same voice, different emotion, different delivery — all from one inline SSML tag per line.&lt;/p&gt;

&lt;h2&gt;
  
  
  How It Was Made
&lt;/h2&gt;

&lt;p&gt;The app is a single Flask file with an inline browser UI. No phone number, no webhook, no Cloud Storage, no database. One env var: &lt;code&gt;TELNYX_API_KEY&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The pipeline
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST /narrate  (script with speaker labels)
  -&amp;gt; parse script into ordered lines
  -&amp;gt; map speaker -&amp;gt; voice (8 curated Ultra voices, overridable)
  -&amp;gt; map speaker -&amp;gt; emotion (20 Ultra SSML emotions, overridable)
  -&amp;gt; parallel fan-out: one REST TTS call per line
     POST /v2/text-to-speech/speech
       text_type=ssml, output_type=binary_output
       &amp;lt;emotion value="..." /&amp;gt; wrapping when emotion set
  -&amp;gt; stitch per-line MP3 bytes in script order
  -&amp;gt; store in memory (1h TTL)
  -&amp;gt; return project_id + per_line_ttfb_ms + audio_url
  -&amp;gt; GET /audio/&amp;lt;project_id&amp;gt;.mp3 streams the stitched MP3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why REST, not WebSocket
&lt;/h3&gt;

&lt;p&gt;Ultra is REST-only on the public WebSocket. A 403 on &lt;code&gt;wss://api.telnyx.com/v2/text-to-speech/speech&lt;/code&gt; is intentional. The app uses &lt;code&gt;POST /v2/text-to-speech/speech&lt;/code&gt; with &lt;code&gt;output_type: binary_output&lt;/code&gt; so it can measure true time-to-first-byte per line. Base64 mode would hide the real latency.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why UUIDs, not display names
&lt;/h3&gt;

&lt;p&gt;Ultra voice IDs are UUIDs in the &lt;code&gt;Telnyx.Ultra.&amp;lt;uuid&amp;gt;&lt;/code&gt; format, not short display names like &lt;code&gt;Telnyx.Ultra.Clara&lt;/code&gt;. Short names return 400 on the REST endpoint. The Voices API at &lt;code&gt;GET /v2/text-to-speech/voices&lt;/code&gt; returns all available voices with their UUIDs — over 700 Ultra voices alone, filterable by &lt;code&gt;provider == "telnyx"&lt;/code&gt; and &lt;code&gt;id | startswith("Telnyx.Ultra.")&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The app ships with eight curated UUIDs so the demo works out of the box, but the dropdown is easy to extend with any voice from the Voices API.&lt;/p&gt;

&lt;h3&gt;
  
  
  Parallel fan-out with per-line error isolation
&lt;/h3&gt;

&lt;p&gt;The app uses &lt;code&gt;ThreadPoolExecutor&lt;/code&gt; to render every line in parallel. If one line fails (e.g. an invalid voice override), the response includes an &lt;code&gt;errors&lt;/code&gt; array and the stitched audio contains only the successful lines in script order. A failed line does not lose the whole render.&lt;/p&gt;

&lt;h3&gt;
  
  
  The browser UI
&lt;/h3&gt;

&lt;p&gt;The UI auto-detects speakers as you type. Each speaker gets a voice dropdown, an emotion dropdown, and a Preview button that renders a short sample line in the selected voice with the selected emotion. The render button fans out the parallel TTS calls, stitches the result, and autoplays the MP3.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run It
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/team-telnyx/telnyx-code-examples.git
&lt;span class="nb"&gt;cd &lt;/span&gt;telnyx-code-examples/multi-character-narrator-python
&lt;span class="nb"&gt;cp&lt;/span&gt; .env.example .env    &lt;span class="c"&gt;# fill in TELNYX_API_KEY&lt;/span&gt;
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
python app.py           &lt;span class="c"&gt;# starts on http://127.0.0.1:5050&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open the browser UI. The default Julius Caesar script is pre-loaded. Pick voices, pick emotions, preview, render, play.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where This Goes Next
&lt;/h2&gt;

&lt;p&gt;The app is a starting point. Add more voices from the 700+ Ultra voices available via the Voices API. Add more languages — Ultra covers 36, and the same script-render-stitch pipeline works for any of them via &lt;code&gt;language_boost&lt;/code&gt;. Add Cloud Storage for persistent, shareable audio URLs. Add more sample scripts — audiobook chapters, podcast intros, e-learning role-plays, game cinematics — any multi-speaker content where you want to hear voices in context.&lt;/p&gt;

&lt;p&gt;The point is the same: hear Telnyx voices in a real scene, not a sample sentence. Everything else follows from that.&lt;/p&gt;

</description>
      <category>telnyx</category>
      <category>ai</category>
      <category>devrel</category>
    </item>
    <item>
      <title>I Built a Website Voice Assistant That Can Actually Control the Page</title>
      <dc:creator>anusha</dc:creator>
      <pubDate>Fri, 31 Jul 2026 17:07:24 +0000</pubDate>
      <link>https://dev.to/botoclock/i-built-a-website-voice-assistant-that-can-actually-control-the-page-2ena</link>
      <guid>https://dev.to/botoclock/i-built-a-website-voice-assistant-that-can-actually-control-the-page-2ena</guid>
      <description>&lt;p&gt;A lot of website AI demos stop at answering questions.&lt;/p&gt;

&lt;p&gt;The assistant can explain where something is, but it does not actually take you there. It can tell you to open settings, but it does not open settings. It can ask you to fill out a form, but it does not touch the form.&lt;/p&gt;

&lt;p&gt;That is the difference I wanted to show with Telnyx AI Assistant client-side tools.&lt;/p&gt;

&lt;p&gt;The Telnyx code example is here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/ai-assistant-client-side-tools-nextjs" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/telnyx-code-examples/tree/main/ai-assistant-client-side-tools-nextjs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The process is clone the sample, configure the same tool names and JSON schemas on your Telnyx AI Assistant, set the public assistant ID in the browser app, and then test the UI actions from the website call button.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Idea
&lt;/h2&gt;

&lt;p&gt;Client-side tools let a Telnyx AI Assistant invoke predefined JavaScript functions in the browser during a voice or chat conversation.&lt;/p&gt;

&lt;p&gt;That sounds small, but it changes the demo.&lt;/p&gt;

&lt;p&gt;Instead of saying:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Go to the AI Assistants page and click Create Assistant.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the assistant can call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;navigate_to_section
open_create_assistant_modal
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The browser updates immediately because those functions are normal React state updates.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;The sample is a fictional SaaS dashboard called PolarForge AI. It is built with Next.js, React, TypeScript, and Tailwind CSS.&lt;/p&gt;

&lt;p&gt;It has a sidebar, several dashboard sections, light and dark themes, an AI Assistants page, a Create Assistant modal, and an activity panel that logs every tool call.&lt;/p&gt;

&lt;p&gt;The assistant can use five tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;set_theme&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;navigate_to_section&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;open_create_assistant_modal&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;get_form_state&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;update_assistant_form&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The interesting one is &lt;code&gt;get_form_state&lt;/code&gt;, because it has to read current React state. It is not returning a hard-coded object. If the modal is open and the form says the assistant name is "Enterprise Concierge", the tool returns that visible state.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Client-Side Instead of Webhook?
&lt;/h2&gt;

&lt;p&gt;Webhook tools are still useful. If the assistant needs to create an account, charge a card, update a CRM record, or call something with privileged credentials, that belongs on the backend.&lt;/p&gt;

&lt;p&gt;But a lot of website actions are local:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;switch to dark mode&lt;/li&gt;
&lt;li&gt;open a modal&lt;/li&gt;
&lt;li&gt;navigate inside a single-page app&lt;/li&gt;
&lt;li&gt;fill a visible form&lt;/li&gt;
&lt;li&gt;read page state&lt;/li&gt;
&lt;li&gt;call APIs the browser is already authenticated to call&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is where client-side tools are a better fit. The assistant does not need a backend webhook just to change React state.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Integration
&lt;/h2&gt;

&lt;p&gt;The app uses &lt;code&gt;@telnyx/ai-agent-lib&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TelnyxAIAgentProvider&lt;/span&gt; &lt;span class="na"&gt;agentId&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;agentId&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ToolRegistrar&lt;/span&gt; &lt;span class="na"&gt;executeTool&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;executeTool&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TelnyxWidget&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;TelnyxAIAgentProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside React, the tools are registered with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;registerClientTool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;navigate_to_section&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;executeTool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;navigate_to_section&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The matching tool definitions still need to exist on the Telnyx AI Assistant. The names and JSON schemas need to match what the browser registers.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Demo Moment
&lt;/h2&gt;

&lt;p&gt;The clean version of the demo is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User: switch to dark mode
Assistant: calls set_theme
Page: changes to dark mode

User: take me to AI Assistants
Assistant: calls navigate_to_section
Page: switches section without reload

User: create an assistant named Enterprise Concierge
Assistant: opens the modal and updates the name field
Page: shows the form update
Activity panel: logs every tool call
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is the proof: the assistant is not just talking about the app. It is controlling the app through approved browser functions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Security Boundary
&lt;/h2&gt;

&lt;p&gt;The browser does not get a Telnyx API key.&lt;/p&gt;

&lt;p&gt;The sample uses public &lt;code&gt;NEXT_PUBLIC_*&lt;/code&gt; configuration for the AI Agent Lib connection. If you need to provision an assistant or call the Telnyx REST API, do that from a secure server-side environment or through the Telnyx Portal.&lt;/p&gt;

&lt;p&gt;The sample also validates every tool call. Unknown sections fail. Unknown form fields fail. Invalid voices or languages fail. Updating the form while the modal is closed fails.&lt;/p&gt;

&lt;p&gt;That is the shape I would keep in a production app: small explicit tools, strict validation, and no privileged secrets in client-side code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Code example: &lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/ai-assistant-client-side-tools-nextjs" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/telnyx-code-examples/tree/main/ai-assistant-client-side-tools-nextjs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Client-side tools docs: &lt;a href="https://developers.telnyx.com/docs/inference/ai-assistants/client-side-tools" rel="noopener noreferrer"&gt;https://developers.telnyx.com/docs/inference/ai-assistants/client-side-tools&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;AI Agent Lib: &lt;a href="https://www.npmjs.com/package/@telnyx/ai-agent-lib" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/@telnyx/ai-agent-lib&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Telnyx release note: &lt;a href="https://telnyx.com/release-notes/client-side-tools-ai-assistants" rel="noopener noreferrer"&gt;https://telnyx.com/release-notes/client-side-tools-ai-assistants&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>telnyx</category>
      <category>ai</category>
      <category>devrel</category>
    </item>
    <item>
      <title>I Built a Voice AI Assistant for Lab Results Notifications</title>
      <dc:creator>anusha</dc:creator>
      <pubDate>Thu, 30 Jul 2026 18:22:31 +0000</pubDate>
      <link>https://dev.to/botoclock/i-built-a-voice-ai-assistant-for-lab-results-notifications-159d</link>
      <guid>https://dev.to/botoclock/i-built-a-voice-ai-assistant-for-lab-results-notifications-159d</guid>
      <description>&lt;p&gt;Lab results are one of those healthcare workflows where the phone call sounds simple, but the product boundaries matter a lot.&lt;/p&gt;

&lt;p&gt;A patient wants to know whether results are back. The clinic needs to verify identity. The system should disclose only what is appropriate. And if the patient needs the full report, the best answer is usually a secure portal link, not reading everything out loud or putting lab values in an SMS.&lt;/p&gt;

&lt;p&gt;So I built the sample as a notification assistant, not a clinical assistant.&lt;/p&gt;

&lt;p&gt;The Telnyx code example is here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/ai-lab-results-notification-voice-python" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/telnyx-code-examples/tree/main/ai-lab-results-notification-voice-python&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It uses Telnyx AI Assistants with a small Python + Flask app for the screen-share dashboard and optional backend extension points.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Flow
&lt;/h2&gt;

&lt;p&gt;The caller dials a Telnyx phone number that is assigned directly to the AI Assistant.&lt;/p&gt;

&lt;p&gt;The assistant asks for:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Full legal name&lt;/li&gt;
&lt;li&gt;Date of birth&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Only after those match a mock record does it disclose the lab result summary.&lt;/p&gt;

&lt;p&gt;For the normal result path, the assistant says the result is back and within the expected range, then asks whether the patient wants a secure portal link by text.&lt;/p&gt;

&lt;p&gt;If the patient says yes, the assistant uses Telnyx native messaging to send a portal link. The text does not include lab values.&lt;/p&gt;

&lt;p&gt;For abnormal or borderline examples, the assistant gives follow-up language and points the patient toward a nurse callback or clinical staff.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Part I Care About
&lt;/h2&gt;

&lt;p&gt;The AI is not practicing medicine.&lt;/p&gt;

&lt;p&gt;The AI is not interpreting raw lab values.&lt;/p&gt;

&lt;p&gt;The AI is not replacing the provider.&lt;/p&gt;

&lt;p&gt;It is doing the operational part:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;verify identity
give a short provider-approved summary
send the patient to the secure portal
route follow-up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That distinction matters in healthcare AI demos. A good demo should show what the system can do, but also show what it refuses to do.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Use Telnyx AI Assistants Here
&lt;/h2&gt;

&lt;p&gt;The cleanest version of this demo does not need a custom tunnel in the call path.&lt;/p&gt;

&lt;p&gt;The phone number is assigned directly to the Telnyx AI Assistant. The assistant is provisioned from &lt;code&gt;provision_assistant.py&lt;/code&gt;. The SMS link uses the assistant's native &lt;code&gt;send_message&lt;/code&gt; tool.&lt;/p&gt;

&lt;p&gt;That means the live demo path is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Telnyx number
  -&amp;gt; Telnyx AI Assistant
  -&amp;gt; native SMS link
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Flask app is still useful, but mostly for the demo dashboard and optional backend extension points.&lt;/p&gt;

&lt;p&gt;That keeps the call experience simple enough to explain on screen.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Dashboard
&lt;/h2&gt;

&lt;p&gt;I also added a local Flask UI at:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://127.0.0.1:5000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It shows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;call flow&lt;/li&gt;
&lt;li&gt;mock patient test identities&lt;/li&gt;
&lt;li&gt;HIPAA compliance safeguards&lt;/li&gt;
&lt;li&gt;local audit and callback state&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It does not show API keys, real PHI, the raw assigned phone number, or raw transcripts.&lt;/p&gt;

&lt;p&gt;That screen is useful for a video because it gives viewers something concrete to look at while the phone call is happening.&lt;/p&gt;

&lt;h2&gt;
  
  
  HIPAA Compliance Safeguards
&lt;/h2&gt;

&lt;p&gt;This sample is designed around the safeguards expected in a HIPAA-compliant lab results notification workflow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;mock data only&lt;/li&gt;
&lt;li&gt;identity verification before result disclosure&lt;/li&gt;
&lt;li&gt;minimum necessary spoken result summary&lt;/li&gt;
&lt;li&gt;no lab values in SMS&lt;/li&gt;
&lt;li&gt;recording disabled&lt;/li&gt;
&lt;li&gt;assistant data retention disabled&lt;/li&gt;
&lt;li&gt;no diagnosis or treatment advice&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For production, pair these controls with the required BAA, role-based access controls, storage controls, audit logs, retention settings, monitoring, and legal/compliance review.&lt;/p&gt;

&lt;p&gt;That is the deployment posture this demo is built to show: Telnyx AI for a privacy-sensitive healthcare workflow, with PHI kept out of SMS and result access routed through a secure portal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run It Locally
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/team-telnyx/telnyx-code-examples.git
&lt;span class="nb"&gt;cd &lt;/span&gt;telnyx-code-examples/ai-lab-results-notification-voice-python
python3 &lt;span class="nt"&gt;-m&lt;/span&gt; venv .venv
&lt;span class="nb"&gt;source&lt;/span&gt; .venv/bin/activate
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;span class="nb"&gt;cp&lt;/span&gt; .env.example .env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;TELNYX_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;KEY...
&lt;span class="nv"&gt;TELNYX_PHONE_NUMBER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;+18005551234
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start the dashboard:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python app.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Provision the assistant:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python provision_assistant.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then assign a Telnyx number directly to the assistant in the Portal and call it.&lt;/p&gt;

&lt;p&gt;For the happy path, use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;name: maya rivera
date of birth: november 22 1984
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then ask for the secure portal link by text.&lt;/p&gt;

&lt;h2&gt;
  
  
  Production Notes
&lt;/h2&gt;

&lt;p&gt;For a production version, I would move patient verification and result lookup out of the prompt and into a backend connected to the real patient portal or EHR workflow.&lt;/p&gt;

&lt;p&gt;I would also keep the assistant prompt conservative:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;verify identity
disclose minimum necessary
do not diagnose
do not interpret beyond provider note
do not put lab values in sms
route clinical questions to staff
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is the shape I like for this kind of workflow. AI handles a narrow phone interaction, while the secure patient portal remains the place where patients access the full report.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Code example: &lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/ai-lab-results-notification-voice-python" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/telnyx-code-examples/tree/main/ai-lab-results-notification-voice-python&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Telnyx AI Assistant quickstart: &lt;a href="https://developers.telnyx.com/docs/inference/ai-assistants/no-code-voice-assistant" rel="noopener noreferrer"&gt;https://developers.telnyx.com/docs/inference/ai-assistants/no-code-voice-assistant&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;AI Assistant interruption settings: &lt;a href="https://developers.telnyx.com/docs/inference/ai-assistants/interruption-settings" rel="noopener noreferrer"&gt;https://developers.telnyx.com/docs/inference/ai-assistants/interruption-settings&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Architecting for HIPAA on Telnyx: &lt;a href="https://telnyx.com/resources/architecting-hipaa-telnyx" rel="noopener noreferrer"&gt;https://telnyx.com/resources/architecting-hipaa-telnyx&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Telnyx Developer Docs: &lt;a href="https://developers.telnyx.com" rel="noopener noreferrer"&gt;https://developers.telnyx.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>telnyx</category>
      <category>ai</category>
      <category>devrel</category>
    </item>
    <item>
      <title>I Built a Voice AI Payment Collection Demo That Keeps Card Details Out of the Assistant</title>
      <dc:creator>anusha</dc:creator>
      <pubDate>Wed, 29 Jul 2026 23:55:26 +0000</pubDate>
      <link>https://dev.to/botoclock/i-built-a-voice-ai-payment-collection-demo-that-keeps-card-details-out-of-the-assistant-41km</link>
      <guid>https://dev.to/botoclock/i-built-a-voice-ai-payment-collection-demo-that-keeps-card-details-out-of-the-assistant-41km</guid>
      <description>&lt;p&gt;Voice AI payment collection is tricky because the most impressive part of the demo is also the part you should not let the model handle.&lt;/p&gt;

&lt;p&gt;An assistant can verify a caller, explain an account balance, answer billing questions, and negotiate a payment plan. But once the caller is ready to enter card details, the assistant should step aside.&lt;/p&gt;

&lt;p&gt;I built a Telnyx code example that does exactly that.&lt;/p&gt;

&lt;p&gt;The code example:&lt;br&gt;
&lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/ai-pci-protected-payment-collection-python" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/telnyx-code-examples/tree/main/ai-pci-protected-payment-collection-python&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  The Flow
&lt;/h2&gt;

&lt;p&gt;A caller dials a Telnyx number for a billing line. Telnyx sends the inbound call event to a Flask webhook. The app answers with Voice API and starts a Telnyx AI Assistant.&lt;/p&gt;

&lt;p&gt;The assistant explains what the line can do: account status, billing questions, payment plans, and secure keypad payment collection. Then it verifies the caller by full name and date of birth.&lt;/p&gt;

&lt;p&gt;After verification, the assistant can disclose the account balance and help the caller choose a payment plan. If the caller agrees to pay, the assistant calls a backend webhook tool called &lt;code&gt;start_secure_payment&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That tool starts Telnyx Pay over Voice. From that point on, the caller enters card details with the phone keypad. The assistant does not ask for the numbers, does not hear the numbers, and does not receive the keypad digits.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why This Pattern Matters
&lt;/h2&gt;

&lt;p&gt;The pattern is simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AI handles the conversation&lt;/li&gt;
&lt;li&gt;Pay over Voice handles card entry&lt;/li&gt;
&lt;li&gt;The app records only sanitized payment status&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That split is the point of the sample.&lt;/p&gt;

&lt;p&gt;If you let a general voice agent collect card numbers, you immediately create a logging, recording, transcript, and storage problem. Even if the assistant is "just helping," the raw payment data can end up in places it does not belong.&lt;/p&gt;

&lt;p&gt;Pay over Voice moves that sensitive step into a purpose-built IVR flow. Telnyx prompts for card number, expiration date, billing ZIP, and security code. The caller enters those values by keypad, and Telnyx sends payment progress/completion events back to the app.&lt;/p&gt;

&lt;p&gt;The local dashboard in the sample is intentionally boring in the right way. It shows high-level proof that the secure payment flow started and completed. It does not show raw PAN, CVV, expiration date, billing ZIP, or raw DTMF.&lt;/p&gt;
&lt;h2&gt;
  
  
  What The Demo Shows
&lt;/h2&gt;

&lt;p&gt;The sample includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Flask webhook handling for inbound Voice API events&lt;/li&gt;
&lt;li&gt;AI Assistant startup with &lt;code&gt;ai_assistant_start&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Assistant webhook tools&lt;/li&gt;
&lt;li&gt;Pay Connector provisioning&lt;/li&gt;
&lt;li&gt;Pay over Voice start command&lt;/li&gt;
&lt;li&gt;Mock payment processor endpoint&lt;/li&gt;
&lt;li&gt;Sanitized event dashboard&lt;/li&gt;
&lt;li&gt;Test-mode payment flow&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The demo customer is Jordan Lee, with a past-due balance of &lt;code&gt;$342.50&lt;/code&gt;. A good demo path is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;caller: jordan lee
caller: march fifteenth nineteen ninety
caller: can i do forty dollars a week?
caller: yes, start the secure payment
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then Pay over Voice takes over. For test mode, enter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;4111111111111111
0827
94111
123
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Run It Locally
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/team-telnyx/telnyx-code-examples.git
&lt;span class="nb"&gt;cd &lt;/span&gt;telnyx-code-examples/ai-pci-protected-payment-collection-python
&lt;span class="nb"&gt;cp&lt;/span&gt; .env.example .env
python3 &lt;span class="nt"&gt;-m&lt;/span&gt; venv .venv
&lt;span class="nb"&gt;source&lt;/span&gt; .venv/bin/activate
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
ngrok http 5000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set your public URL and API key in &lt;code&gt;.env&lt;/code&gt;, then provision the assistant:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python provision_assistant.py
python app.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Point your Voice API application webhook to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://&amp;lt;ngrok-id&amp;gt;.ngrok-free.app/webhooks/voice
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Call your Telnyx number and walk through the billing flow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where To Take It Next
&lt;/h2&gt;

&lt;p&gt;For a production implementation, replace the mock payment processor with your actual processor, review PCI scope with your compliance team, verify webhook signatures, and store only the sanitized payment outcome your business process needs.&lt;/p&gt;

&lt;p&gt;The assistant should keep the same boundary: explain, verify, plan, and hand off. It should never ask for card details out loud.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Code: &lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/ai-pci-protected-payment-collection-python" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/telnyx-code-examples/tree/main/ai-pci-protected-payment-collection-python&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Pay over Voice: &lt;a href="https://developers.telnyx.com/docs/voice/programmable-voice/pay" rel="noopener noreferrer"&gt;https://developers.telnyx.com/docs/voice/programmable-voice/pay&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Voice API commands: &lt;a href="https://developers.telnyx.com/docs/voice/programmable-voice/voice-api-commands-and-resources" rel="noopener noreferrer"&gt;https://developers.telnyx.com/docs/voice/programmable-voice/voice-api-commands-and-resources&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Start AI Assistant: &lt;a href="https://developers.telnyx.com/api-reference/call-commands/start-ai-assistant" rel="noopener noreferrer"&gt;https://developers.telnyx.com/api-reference/call-commands/start-ai-assistant&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Telnyx Portal: &lt;a href="https://portal.telnyx.com" rel="noopener noreferrer"&gt;https://portal.telnyx.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>telnyx</category>
      <category>ai</category>
      <category>devrel</category>
    </item>
    <item>
      <title>I Built a Voice AI Agent for Pre-Visit Insurance Clearance</title>
      <dc:creator>anusha</dc:creator>
      <pubDate>Wed, 22 Jul 2026 22:51:50 +0000</pubDate>
      <link>https://dev.to/botoclock/i-built-a-voice-ai-agent-for-pre-visit-insurance-clearance-57fh</link>
      <guid>https://dev.to/botoclock/i-built-a-voice-ai-agent-for-pre-visit-insurance-clearance-57fh</guid>
      <description>&lt;p&gt;Pre-visit insurance clearance is one of those workflows that sounds simple until you see how many phone calls it creates.&lt;/p&gt;

&lt;p&gt;A patient calls before an appointment. They need to know whether a procedure, test, or medication requires prior authorization. Staff need to identify the patient, understand what they are asking for, collect payer and provider context, decide how urgent it is, and route the request to the right billing queue.&lt;/p&gt;

&lt;p&gt;I built a Telnyx code example that turns that call into a structured intake ticket.&lt;/p&gt;

&lt;p&gt;The code example:&lt;br&gt;
&lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/ai-pre-visit-clearance-voice-agent-python" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/telnyx-code-examples/tree/main/ai-pre-visit-clearance-voice-agent-python&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  The Flow
&lt;/h2&gt;

&lt;p&gt;A patient calls a Telnyx number. Telnyx sends the inbound call event to a Flask webhook. The app answers with Call Control, then starts &lt;code&gt;gather_using_ai&lt;/code&gt; to collect the patient's spoken pre-clearance request as structured fields.&lt;/p&gt;

&lt;p&gt;When Telnyx finishes collecting those fields, the app receives &lt;code&gt;call.ai_gather.ended&lt;/code&gt;. From there, the app classifies the request with AI Inference, confirms the details with the patient, creates a staff-facing ticket, sends the patient an SMS confirmation, and optionally alerts billing staff in Slack.&lt;/p&gt;

&lt;p&gt;The agent is non-clinical. It doesn't give medical advice, approve or deny coverage, or diagnose. It collects administrative data and routes it — which is the bottleneck in a lot of healthcare revenue-cycle work.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why This Pattern Matters
&lt;/h2&gt;

&lt;p&gt;The main Telnyx primitive here is Gather Using AI.&lt;/p&gt;

&lt;p&gt;The app asks Telnyx to gather the fields it needs directly inside the call. The webhook response gives the backend something structured enough to use in a workflow.&lt;/p&gt;

&lt;p&gt;That makes the rest of the app cleaner:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Call Control handles the inbound call lifecycle&lt;/li&gt;
&lt;li&gt;Gather Using AI collects the spoken intake details&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;call.ai_gather.ended&lt;/code&gt; advances the workflow&lt;/li&gt;
&lt;li&gt;AI Inference classifies urgency and type&lt;/li&gt;
&lt;li&gt;Messaging sends the patient a confirmation&lt;/li&gt;
&lt;li&gt;Slack gives billing staff a queue signal&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The implementation patterns it covers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DOB fallback when caller ID is unknown&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;command_id&lt;/code&gt; on Call Control commands&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;send_silence_when_idle&lt;/code&gt; when answering&lt;/li&gt;
&lt;li&gt;structured JSON classification for procedure, urgency, and request type&lt;/li&gt;
&lt;li&gt;keyword-based urgency override for phrases like "ASAP" or "can't wait"&lt;/li&gt;
&lt;li&gt;confirmation before ticket creation&lt;/li&gt;
&lt;li&gt;partial ticket creation if the caller hangs up after providing useful context&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Run It Locally
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/team-telnyx/telnyx-code-examples.git
&lt;span class="nb"&gt;cd &lt;/span&gt;telnyx-code-examples/ai-pre-visit-clearance-voice-agent-python
&lt;span class="nb"&gt;cp&lt;/span&gt; .env.example .env
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
python app.py
ngrok http 5000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Point your Telnyx Call Control Application webhook to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://&amp;lt;your-ngrok-domain&amp;gt;/webhooks/voice
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Seed a patient:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:5000/patients &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"patient_id":"P001","name":"Jordan Lee","phone":"+15551112233","dob":"03/15/1990","insurance":"Blue Cross","provider":"Dr. Smith"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Call the number and say:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;I need clearance for an MRI on my lower back.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The app collects the request, classifies it, confirms the details, creates a ticket, and sends an SMS confirmation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where To Take It Next
&lt;/h2&gt;

&lt;p&gt;Replace in-memory storage with your EHR, PMS, or billing system integration. Wire ticket completion into real prior auth submission APIs. Add privacy review and HIPAA-ready operational controls before production use. Queue Slack and SMS side effects so webhook responses stay fast.&lt;/p&gt;

&lt;p&gt;The key boundary should stay the same: the agent collects and routes. Staff and payer systems make the actual coverage decisions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Code: &lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/ai-pre-visit-clearance-voice-agent-python" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/telnyx-code-examples/tree/main/ai-pre-visit-clearance-voice-agent-python&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Gather Using AI: &lt;a href="https://developers.telnyx.com/api-reference/call-commands/gather-using-ai" rel="noopener noreferrer"&gt;https://developers.telnyx.com/api-reference/call-commands/gather-using-ai&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;call.ai_gather.ended&lt;/code&gt;: &lt;a href="https://developers.telnyx.com/api-reference/callbacks/call-ai-gather-ended" rel="noopener noreferrer"&gt;https://developers.telnyx.com/api-reference/callbacks/call-ai-gather-ended&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Voice API commands: &lt;a href="https://developers.telnyx.com/docs/voice/programmable-voice/voice-api-commands-and-resources" rel="noopener noreferrer"&gt;https://developers.telnyx.com/docs/voice/programmable-voice/voice-api-commands-and-resources&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;AI Inference docs: &lt;a href="https://developers.telnyx.com/docs/inference" rel="noopener noreferrer"&gt;https://developers.telnyx.com/docs/inference&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Messaging docs: &lt;a href="https://developers.telnyx.com/docs/messaging" rel="noopener noreferrer"&gt;https://developers.telnyx.com/docs/messaging&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Telnyx Portal: &lt;a href="https://portal.telnyx.com" rel="noopener noreferrer"&gt;https://portal.telnyx.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>telnyx</category>
      <category>ai</category>
      <category>devrel</category>
    </item>
    <item>
      <title>I Built a Voice AI Assistant for Prescription Refill Intake</title>
      <dc:creator>anusha</dc:creator>
      <pubDate>Tue, 21 Jul 2026 23:35:10 +0000</pubDate>
      <link>https://dev.to/botoclock/i-built-a-voice-ai-assistant-for-prescription-refill-intake-1cj6</link>
      <guid>https://dev.to/botoclock/i-built-a-voice-ai-assistant-for-prescription-refill-intake-1cj6</guid>
      <description>&lt;p&gt;Prescription refill calls are one of those workflows that sound simple until you look at the operational details.&lt;/p&gt;

&lt;p&gt;The caller needs to explain which medication they are calling about. The pharmacy or clinic may need the preferred pharmacy, the callback number, how soon the refill is needed, and whether the request needs manual review.&lt;/p&gt;

&lt;p&gt;But there is also a hard boundary: the AI should not approve a prescription, deny a refill, change dosage instructions, diagnose anything, or make clinical decisions.&lt;/p&gt;

&lt;p&gt;So I built the workflow as an intake assistant, not a decision-maker.&lt;/p&gt;

&lt;p&gt;The Telnyx code example is here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/ai-prescription-refill-intake-voice-assistant-python" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/telnyx-code-examples/tree/main/ai-prescription-refill-intake-voice-assistant-python&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It uses Telnyx AI Assistants with a small Python + Flask backend.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Flow
&lt;/h2&gt;

&lt;p&gt;The caller dials a Telnyx phone number. Telnyx sends a &lt;code&gt;call.initiated&lt;/code&gt; webhook to the Flask app.&lt;/p&gt;

&lt;p&gt;The app does two things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Answers the call&lt;/li&gt;
&lt;li&gt;Starts the configured Telnyx AI Assistant with &lt;code&gt;ai_assistant_start&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After that, the assistant owns the live conversation. It gives an emergency reminder, then asks one question at a time.&lt;/p&gt;

&lt;p&gt;The assistant has three backend tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;create_refill_request&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;flag_manual_review&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;queue_callback&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When the assistant has enough information, it calls &lt;code&gt;create_refill_request&lt;/code&gt;. If the request mentions a controlled substance, dosage change, side effects, urgent access issue, or similar follow-up trigger, it calls &lt;code&gt;flag_manual_review&lt;/code&gt;. If the caller wants a callback, it calls &lt;code&gt;queue_callback&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Part I Care About
&lt;/h2&gt;

&lt;p&gt;The AI is not the system of record.&lt;/p&gt;

&lt;p&gt;The AI is not approving the refill.&lt;/p&gt;

&lt;p&gt;The AI is collecting enough information for staff to do the next step.&lt;/p&gt;

&lt;p&gt;That distinction matters a lot in healthcare voice AI. A useful assistant should reduce the intake burden without pretending to replace a pharmacist, clinician, or compliance process.&lt;/p&gt;

&lt;p&gt;The backend is where the workflow is enforced. It stores the request, masks caller identifiers, checks the assistant tool secret, and exposes a review endpoint for staff.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Use Telnyx AI Assistants Here
&lt;/h2&gt;

&lt;p&gt;You could build this as a manual voice state machine with gather and speak commands. That works, but the code gets focused on conversation plumbing.&lt;/p&gt;

&lt;p&gt;With Telnyx AI Assistants, the shape is cleaner:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Call Control answers the phone
AI Assistant handles the conversation
assistant tools call the Flask backend
backend stores refill workflow state
staff reviews the request
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The assistant prompt is also provisioned from code in &lt;code&gt;provision_assistant.py&lt;/code&gt;, which means someone can recreate the assistant from the repo instead of manually copying settings from a portal screenshot.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run It Locally
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/team-telnyx/telnyx-code-examples.git
&lt;span class="nb"&gt;cd &lt;/span&gt;telnyx-code-examples/ai-prescription-refill-intake-voice-assistant-python
python3 &lt;span class="nt"&gt;-m&lt;/span&gt; venv .venv
&lt;span class="nb"&gt;source&lt;/span&gt; .venv/bin/activate
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;span class="nb"&gt;cp&lt;/span&gt; .env.example .env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set the required environment variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;TELNYX_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;KEY...
&lt;span class="nv"&gt;PUBLIC_BASE_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://your-ngrok-url.ngrok-free.app
&lt;span class="nv"&gt;TOOL_SECRET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;replace_with_a_random_shared_secret
&lt;span class="nv"&gt;TELNYX_PHONE_NUMBER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;+18005551234
&lt;span class="nv"&gt;TELNYX_PUBLIC_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your_telnyx_public_key_here
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start the app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python app.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expose it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ngrok http 5000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Provision the assistant:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python provision_assistant.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then set your Call Control Application webhook URL to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://&amp;lt;your-ngrok-domain&amp;gt;/webhooks/voice
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Call the Telnyx number and ask for a prescription refill.&lt;/p&gt;

&lt;h2&gt;
  
  
  What To Watch In The Code
&lt;/h2&gt;

&lt;p&gt;The main app is &lt;code&gt;app.py&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The inbound webhook answers the call and starts the assistant:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;TELNYX_API_BASE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/calls/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;call_control_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/actions/ai_assistant_start&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;telnyx_headers&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;assistant_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;TELNYX_ASSISTANT_ID&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The assistant setup is in &lt;code&gt;provision_assistant.py&lt;/code&gt;. That file creates or updates the assistant, sets the model, adds the prompt, configures dynamic variables, and registers the webhook tools.&lt;/p&gt;

&lt;p&gt;The tool endpoints are intentionally narrow. They accept structured data and return workflow state. That makes the assistant useful without letting it become the approval layer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Production Notes
&lt;/h2&gt;

&lt;p&gt;The sample keeps state in memory because it is designed to be easy to run locally.&lt;/p&gt;

&lt;p&gt;For production, I would add encrypted database storage, staff authentication, audit logging, retention policies, monitoring, stricter PHI controls, and a full compliance review.&lt;/p&gt;

&lt;p&gt;I would also keep the assistant instructions conservative:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;collect intake
route to staff
do not approve or deny
do not change medication instructions
do not diagnose
send emergency callers to 9-1-1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is the useful shape for this kind of healthcare AI workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Code example: &lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/ai-prescription-refill-intake-voice-assistant-python" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/telnyx-code-examples/tree/main/ai-prescription-refill-intake-voice-assistant-python&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Telnyx AI Assistant quickstart: &lt;a href="https://developers.telnyx.com/docs/inference/ai-assistants/no-code-voice-assistant" rel="noopener noreferrer"&gt;https://developers.telnyx.com/docs/inference/ai-assistants/no-code-voice-assistant&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Attach an AI Assistant to a Call: &lt;a href="https://developers.telnyx.com/docs/voice/programmable-voice/ai-assistant-start" rel="noopener noreferrer"&gt;https://developers.telnyx.com/docs/voice/programmable-voice/ai-assistant-start&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;AI Assistant dynamic variables: &lt;a href="https://developers.telnyx.com/docs/inference/ai-assistants/dynamic-variables" rel="noopener noreferrer"&gt;https://developers.telnyx.com/docs/inference/ai-assistants/dynamic-variables&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Telnyx Developer Docs: &lt;a href="https://developers.telnyx.com" rel="noopener noreferrer"&gt;https://developers.telnyx.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>telnyx</category>
      <category>ai</category>
      <category>devrel</category>
    </item>
    <item>
      <title>I Built a Voice AI Assistant That Can Bring a Human Into the Same Call</title>
      <dc:creator>anusha</dc:creator>
      <pubDate>Tue, 21 Jul 2026 18:56:45 +0000</pubDate>
      <link>https://dev.to/botoclock/i-built-a-voice-ai-assistant-that-can-bring-a-human-into-the-same-call-5b9i</link>
      <guid>https://dev.to/botoclock/i-built-a-voice-ai-assistant-that-can-bring-a-human-into-the-same-call-5b9i</guid>
      <description>&lt;p&gt;Most voice AI demos stop at the assistant. A caller talks to an AI, the AI answers, maybe it creates a ticket, and the call ends.&lt;/p&gt;

&lt;p&gt;But a lot of real workflows need a human in the loop. A support assistant might need an integrations engineer. A healthcare assistant might need a care team member. A financial services assistant might need a licensed advisor. A sales assistant might need the right account executive.&lt;/p&gt;

&lt;p&gt;The interesting part is doing that without making the caller hang up, call a different number, or repeat the same context to another person.&lt;/p&gt;

&lt;p&gt;The Telnyx code example is here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/ai-assistant-multiparticipant-calling-nodejs" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/telnyx-code-examples/tree/main/ai-assistant-multiparticipant-calling-nodejs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It shows a Node.js + Express backend that lets a Telnyx AI Assistant add another participant to the same live AI conversation.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Flow
&lt;/h2&gt;

&lt;p&gt;The caller dials a Telnyx number. Telnyx sends a &lt;code&gt;call.initiated&lt;/code&gt; webhook to the app. The app answers the call with an inline AI Assistant configuration.&lt;/p&gt;

&lt;p&gt;The assistant has a few tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;classify_issue&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;record_specialist_consent&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dial_specialist&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The first two are there so the assistant does not immediately dial someone. It should understand the issue and ask permission before adding another participant.&lt;/p&gt;

&lt;p&gt;The third tool is the bridge to the backend. When the assistant calls &lt;code&gt;dial_specialist&lt;/code&gt;, the backend creates a second call leg with &lt;code&gt;POST /v2/calls&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then, when the specialist answers, Telnyx sends a &lt;code&gt;call.answered&lt;/code&gt; webhook for that second leg. At that point, the backend calls &lt;code&gt;ai_assistant_join&lt;/code&gt; and passes two important values:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the existing AI &lt;code&gt;conversation_id&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;the specialist's &lt;code&gt;call_control_id&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is the moment the call becomes multiparticipant.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Part I Care About
&lt;/h2&gt;

&lt;p&gt;The AI is not just transferring the caller. It is coordinating the call.&lt;/p&gt;

&lt;p&gt;The caller stays in the original conversation. The assistant already has the issue context. When the specialist joins, the assistant can summarize what is happening and then get out of the way unless it needs to clarify, create a ticket, or close the loop.&lt;/p&gt;

&lt;p&gt;That makes the experience feel much closer to a real support handoff:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;caller: I am seeing webhook delivery failures
ai: I can bring in an integrations specialist on this same call. Does that work?
caller: yes
backend: dials specialist
specialist: hi, this is Anusha from integrations
ai: Anusha, the caller is seeing intermittent 401 responses on webhook deliveries...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why Use a Backend Tool Instead of Only Portal Setup?
&lt;/h2&gt;

&lt;p&gt;The Telnyx Portal supports assistant tools, and the docs cover multi-participant calls. This example focuses on the backend-driven version because developers often need control over:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;which participant to dial&lt;/li&gt;
&lt;li&gt;when consent was recorded&lt;/li&gt;
&lt;li&gt;how the second call leg is tracked&lt;/li&gt;
&lt;li&gt;what metadata is stored&lt;/li&gt;
&lt;li&gt;what happens if the participant does not answer&lt;/li&gt;
&lt;li&gt;how the handoff is audited&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The backend tool makes that logic explicit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run It Locally
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/team-telnyx/telnyx-code-examples.git
&lt;span class="nb"&gt;cd &lt;/span&gt;telnyx-code-examples/ai-assistant-multiparticipant-calling-nodejs
&lt;span class="nb"&gt;cp&lt;/span&gt; .env.example .env
npm &lt;span class="nb"&gt;install
&lt;/span&gt;npm &lt;span class="nb"&gt;test
&lt;/span&gt;npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expose it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ngrok http 8787
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Point your Programmable Voice / Voice API application webhook to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://&amp;lt;your-ngrok-domain&amp;gt;/webhooks/voice
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then call your Telnyx number and ask for a support case that needs a specialist.&lt;/p&gt;

&lt;h2&gt;
  
  
  What To Watch In The Code
&lt;/h2&gt;

&lt;p&gt;The main file is &lt;code&gt;server.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The assistant instructions and tool definitions are inline, so someone can recreate the demo from the folder without copying a prebuilt Portal assistant. The &lt;code&gt;dial_specialist&lt;/code&gt; endpoint handles the tool call. The &lt;code&gt;call.answered&lt;/code&gt; handler joins the specialist into the conversation.&lt;/p&gt;

&lt;p&gt;The key call is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`https://api.telnyx.com/v2/calls/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/actions/ai_assistant_join`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;telnyxHeaders&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;conversation_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;conversationId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;participant&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;specialistCallControlId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;support specialist&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;on_hangup&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;continue_conversation&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is the handoff.&lt;/p&gt;

&lt;h2&gt;
  
  
  Production Notes
&lt;/h2&gt;

&lt;p&gt;The code sample keeps state in memory because it is meant to be easy to run. For production, I would store session state in Redis or Postgres, verify webhook signatures, add retries around outbound dialing, log consent, and add assistant behavior for when humans are talking directly to each other.&lt;/p&gt;

&lt;p&gt;The pattern is still the same:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AI handles the front door
AI calls a backend tool
backend dials the next participant
Telnyx joins them into the same AI conversation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Code example: &lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/ai-assistant-multiparticipant-calling-nodejs" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/telnyx-code-examples/tree/main/ai-assistant-multiparticipant-calling-nodejs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Multi-participant calls docs: &lt;a href="https://developers.telnyx.com/docs/inference/ai-assistants/multi-participant-calls" rel="noopener noreferrer"&gt;https://developers.telnyx.com/docs/inference/ai-assistants/multi-participant-calls&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Join AI Assistant Conversation API: &lt;a href="https://developers.telnyx.com/api-reference/call-commands/join-ai-assistant-conversation" rel="noopener noreferrer"&gt;https://developers.telnyx.com/api-reference/call-commands/join-ai-assistant-conversation&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Telnyx Developer Docs: &lt;a href="https://developers.telnyx.com" rel="noopener noreferrer"&gt;https://developers.telnyx.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>telnyx</category>
      <category>ai</category>
      <category>devrel</category>
    </item>
    <item>
      <title>I Built a One-Curl Audio Translator with Telnyx AI Inference</title>
      <dc:creator>anusha</dc:creator>
      <pubDate>Tue, 14 Jul 2026 22:06:14 +0000</pubDate>
      <link>https://dev.to/botoclock/i-built-a-one-curl-audio-translator-with-telnyx-ai-inference-39f9</link>
      <guid>https://dev.to/botoclock/i-built-a-one-curl-audio-translator-with-telnyx-ai-inference-39f9</guid>
      <description>&lt;p&gt;Localizing a podcast, translating a recorded meeting, or dubbing a lecture used to require three different services stitched together. The Telnyx AI Inference API exposes STT, chat completions, and TTS on the same private backbone, which means a single Flask app can run the whole pipeline.&lt;/p&gt;

&lt;p&gt;The Telnyx code example is:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/ai-content-translator-python" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/telnyx-code-examples/tree/main/ai-content-translator-python&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is a Python Flask app you can clone and run in a few minutes. No phone number, no webhook tunnel, no background job runner.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Flow
&lt;/h2&gt;

&lt;p&gt;You POST an audio file plus a target language. The app calls Telnyx STT to transcribe the audio in the source language, calls AI Inference to translate the transcript with a TTS-friendly system prompt, and calls Telnyx TTS to render the translated text into audio. Long transcripts are chunked at sentence boundaries so the TTS model never gets an input larger than it supports, and the chunks are concatenated into one mp3.&lt;/p&gt;

&lt;p&gt;The response includes the job id, both transcripts, and a URL you can curl to download the dubbed audio.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I Like This Example
&lt;/h2&gt;

&lt;p&gt;It is small enough to demo live, but it covers the parts that often get cut from an audio translation demo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;File upload with multipart form data and a writable temp file&lt;/li&gt;
&lt;li&gt;Three Telnyx AI endpoints chained together with per-stage error handling&lt;/li&gt;
&lt;li&gt;Long-transcript chunking at sentence boundaries&lt;/li&gt;
&lt;li&gt;Response shape handling for TTS endpoints that can return raw audio, JSON with base64, or JSON with a fetch URL&lt;/li&gt;
&lt;li&gt;Temp file cleanup so the upload does not leak disk space&lt;/li&gt;
&lt;li&gt;Auto language detection (or pinned language) depending on whether you trust STT's detection&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The design choice I care most about is per-stage error handling. If the TTS call fails on chunk 3 of 8, the app returns &lt;code&gt;200 partial&lt;/code&gt; with the transcripts still intact, rather than throwing away the STT and translation work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run It Locally
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/team-telnyx/telnyx-code-examples.git
&lt;span class="nb"&gt;cd &lt;/span&gt;telnyx-code-examples/ai-content-translator-python
&lt;span class="nb"&gt;cp&lt;/span&gt; .env.example .env
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
python app.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Translate a Spanish clip into English:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:5000/translate &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-F&lt;/span&gt; &lt;span class="nv"&gt;audio&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;@spanish-sample.mp3 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-F&lt;/span&gt; &lt;span class="nb"&gt;source&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;es &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-F&lt;/span&gt; &lt;span class="nv"&gt;target&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;en
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Download the dubbed audio:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-OJ&lt;/span&gt; http://localhost:5000/translate/&amp;lt;job_id&amp;gt;/audio
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read the full transcripts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://localhost:5000/translate/&amp;lt;job_id&amp;gt; | python3 &lt;span class="nt"&gt;-m&lt;/span&gt; json.tool
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What To Demo
&lt;/h2&gt;

&lt;p&gt;I would keep the demo short:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Show the &lt;code&gt;/languages&lt;/code&gt; endpoint to call out the supported set.&lt;/li&gt;
&lt;li&gt;POST a Spanish audio file with &lt;code&gt;target=en&lt;/code&gt; and walk through the response shape (job_id, transcript previews, audio_url).&lt;/li&gt;
&lt;li&gt;Play the downloaded dubbed mp3.&lt;/li&gt;
&lt;li&gt;Show &lt;code&gt;GET /translate/&amp;lt;job_id&amp;gt;&lt;/code&gt; for the full transcripts.&lt;/li&gt;
&lt;li&gt;(Optional) POST with &lt;code&gt;source=auto&lt;/code&gt; to show language detection.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That gives viewers the full loop without getting lost in implementation details.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where To Take It Next
&lt;/h2&gt;

&lt;p&gt;The demo keeps translation jobs in memory for one hour. Production would replace the in-memory &lt;code&gt;jobs&lt;/code&gt; dict with object storage (S3, GCS) for the audio blobs and a database (Postgres) for the metadata, run STT / translate / TTS in a queue, return &lt;code&gt;202 Accepted&lt;/code&gt; for long jobs, add API key auth, and cap upload size and audio length up front.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Code example: &lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/ai-content-translator-python" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/telnyx-code-examples/tree/main/ai-content-translator-python&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Telnyx AI Inference docs: &lt;a href="https://developers.telnyx.com/docs/inference" rel="noopener noreferrer"&gt;https://developers.telnyx.com/docs/inference&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Telnyx Developer Docs: &lt;a href="https://developers.telnyx.com" rel="noopener noreferrer"&gt;https://developers.telnyx.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Telnyx Portal: &lt;a href="https://portal.telnyx.com" rel="noopener noreferrer"&gt;https://portal.telnyx.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>telnyx</category>
      <category>ai</category>
      <category>devrel</category>
    </item>
    <item>
      <title>I Built an AI Subscription Cancel-Save Agent That Does Not Manipulate You</title>
      <dc:creator>anusha</dc:creator>
      <pubDate>Tue, 14 Jul 2026 22:03:54 +0000</pubDate>
      <link>https://dev.to/botoclock/i-built-an-ai-subscription-cancel-save-agent-that-does-not-manipulate-you-3gbd</link>
      <guid>https://dev.to/botoclock/i-built-an-ai-subscription-cancel-save-agent-that-does-not-manipulate-you-3gbd</guid>
      <description>&lt;p&gt;Cancel-save flows are easy to over-engineer into manipulative dark patterns, and easy to under-engineer into "your subscription has been cancelled, goodbye" experiences that lose revenue. A good cancel-save agent does three things: classifies the reason, offers one relevant save option, and respects a direct cancellation request.&lt;/p&gt;

&lt;p&gt;The Telnyx code example is:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/ai-subscription-cancel-save-retention-agent-python" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/telnyx-code-examples/tree/main/ai-subscription-cancel-save-retention-agent-python&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is a Python Flask app that combines Telnyx Voice, AI Inference, and Messaging into a small demo you can clone and run in a few minutes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Flow
&lt;/h2&gt;

&lt;p&gt;A customer calls in saying they want to cancel. The Flask app receives the webhook, verifies the customer by caller ID, and asks why they want to cancel. AI Inference returns the reason (&lt;code&gt;too_expensive&lt;/code&gt;, &lt;code&gt;not_using&lt;/code&gt;, &lt;code&gt;missing_feature&lt;/code&gt;, &lt;code&gt;support_issue&lt;/code&gt;, &lt;code&gt;competitor_switch&lt;/code&gt;, &lt;code&gt;temporary_pause&lt;/code&gt;, &lt;code&gt;other&lt;/code&gt;) and the sentiment. Hardcoded urgent phrases (&lt;code&gt;lawyer&lt;/code&gt;, &lt;code&gt;sue&lt;/code&gt;, &lt;code&gt;fraud&lt;/code&gt;, &lt;code&gt;chargeback&lt;/code&gt;) and direct cancel phrases (&lt;code&gt;cancel now&lt;/code&gt;, &lt;code&gt;please cancel my subscription&lt;/code&gt;) short-circuit before any offer.&lt;/p&gt;

&lt;p&gt;Otherwise, the agent offers one save option from the OFFER_POLICY mapping. A "yes" applies the save or pauses the subscription. A "no" or "cancel" ends the call with a graceful cancellation. An ambiguous yes/no gets exactly one clarifying prompt — the agent never loops.&lt;/p&gt;

&lt;p&gt;The case is recorded with the outcome (&lt;code&gt;saved&lt;/code&gt;, &lt;code&gt;cancelled&lt;/code&gt;, &lt;code&gt;paused&lt;/code&gt;, &lt;code&gt;transferred&lt;/code&gt;, &lt;code&gt;needs_followup&lt;/code&gt;), and the customer record is updated. Hangups before resolution are recorded as &lt;code&gt;needs_followup&lt;/code&gt; so reps can call back.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I Like This Example
&lt;/h2&gt;

&lt;p&gt;It is small enough to demo live, but it covers the parts that often get cut from a "Hello World" voice AI demo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Single-prompt AI Inference classification that returns structured JSON&lt;/li&gt;
&lt;li&gt;An offer policy that maps reason to one offer and one default outcome&lt;/li&gt;
&lt;li&gt;Hardcoded overrides for urgent phrases and direct cancel so the agent does not rely on the LLM to get safety right&lt;/li&gt;
&lt;li&gt;One clarifying prompt on ambiguous yes/no so the conversation does not loop&lt;/li&gt;
&lt;li&gt;Idempotent webhook handling so retries do not double-log cases&lt;/li&gt;
&lt;li&gt;Hangup tracking so open cases never disappear&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The design choice I care most about is non-manipulation. The agent does not push back on a direct cancellation, and it offers one save option — not five — when the customer is open to one. That maps to how good customer-success teams actually operate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run It Locally
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/team-telnyx/telnyx-code-examples.git
&lt;span class="nb"&gt;cd &lt;/span&gt;telnyx-code-examples/ai-subscription-cancel-save-retention-agent-python
&lt;span class="nb"&gt;cp&lt;/span&gt; .env.example .env
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
python app.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expose the webhook:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ngrok http 5000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the Telnyx Portal, set the Voice API app webhook URL to &lt;code&gt;https://&amp;lt;ngrok-id&amp;gt;.ngrok-free.app/webhooks/voice&lt;/code&gt;. Seed a customer with &lt;code&gt;POST /customers&lt;/code&gt;, then call the Telnyx number from the customer's phone.&lt;/p&gt;

&lt;h2&gt;
  
  
  What To Demo
&lt;/h2&gt;

&lt;p&gt;I would keep the demo short and show three flows back-to-back:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Customer says "I want to cancel, it's too expensive" then accepts the 25% off offer. Case ends &lt;code&gt;saved&lt;/code&gt;, customer status &lt;code&gt;active&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Customer says "I want to cancel, it's too expensive" then declines. Case ends &lt;code&gt;cancelled&lt;/code&gt;, customer status &lt;code&gt;cancelled&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Customer says "I want a lawyer this is fraud." Case ends &lt;code&gt;transferred&lt;/code&gt;, call moves to the human escalation number.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That gives viewers the full loop without getting lost in implementation details.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where To Take It Next
&lt;/h2&gt;

&lt;p&gt;The demo uses in-memory state, which is fine for learning. Production would wire the customer and case stores into your billing system (Stripe, Recurly, Chargebee) so a &lt;code&gt;saved&lt;/code&gt; outcome actually applies the discount and a &lt;code&gt;paused&lt;/code&gt; outcome defers the next invoice. Add consent recording (&lt;code&gt;record_channels: "dual"&lt;/code&gt;) for compliance audits, and replace the caller-ID match with a one-time code flow for real verification.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Code example: &lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/ai-subscription-cancel-save-retention-agent-python" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/telnyx-code-examples/tree/main/ai-subscription-cancel-save-retention-agent-python&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Telnyx Voice docs: &lt;a href="https://developers.telnyx.com/docs/voice/call-control" rel="noopener noreferrer"&gt;https://developers.telnyx.com/docs/voice/call-control&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Telnyx AI Inference docs: &lt;a href="https://developers.telnyx.com/docs/inference" rel="noopener noreferrer"&gt;https://developers.telnyx.com/docs/inference&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Telnyx Messaging docs: &lt;a href="https://developers.telnyx.com/docs/messaging" rel="noopener noreferrer"&gt;https://developers.telnyx.com/docs/messaging&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Telnyx Portal: &lt;a href="https://portal.telnyx.com" rel="noopener noreferrer"&gt;https://portal.telnyx.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>telnyx</category>
      <category>ai</category>
      <category>devrel</category>
    </item>
    <item>
      <title>I Built a Hotel Concierge Line with Telnyx Voice, SMS, and Slack</title>
      <dc:creator>anusha</dc:creator>
      <pubDate>Tue, 14 Jul 2026 21:45:33 +0000</pubDate>
      <link>https://dev.to/botoclock/i-built-a-hotel-concierge-line-with-telnyx-voice-sms-and-slack-50g4</link>
      <guid>https://dev.to/botoclock/i-built-a-hotel-concierge-line-with-telnyx-voice-sms-and-slack-50g4</guid>
      <description>&lt;p&gt;Hotel guests do not care whether the front desk is staffed. They want to text or call one number and have their room service, housekeeping, concierge, or maintenance request handled. A concierge line is a good test case for voice AI because the workflow is familiar and the volume is real.&lt;/p&gt;

&lt;p&gt;The Telnyx code example is:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/hotel-guest-services-python" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/telnyx-code-examples/tree/main/hotel-guest-services-python&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is a Python Flask app that combines Telnyx Voice, AI Inference, Messaging, and Slack into a small demo you can clone and run in a few minutes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Flow
&lt;/h2&gt;

&lt;p&gt;A guest calls or texts the Telnyx number. The Flask app receives the webhook, looks up the room by caller ID, and either greets the guest by name or asks for the room number. Each request is sent to Telnyx AI Inference, which classifies it into &lt;code&gt;room_service&lt;/code&gt;, &lt;code&gt;housekeeping&lt;/code&gt;, &lt;code&gt;concierge&lt;/code&gt;, or &lt;code&gt;maintenance&lt;/code&gt;. Urgent phrases (&lt;code&gt;fire&lt;/code&gt;, &lt;code&gt;flood&lt;/code&gt;, &lt;code&gt;leak&lt;/code&gt;, &lt;code&gt;locked out&lt;/code&gt;, &lt;code&gt;gas&lt;/code&gt;, &lt;code&gt;medical&lt;/code&gt;) override the LLM and route the request as &lt;code&gt;urgent&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The request is appended to a log, the guest gets an SMS confirmation, and staff get a Slack alert with the right emoji for the department. When staff mark the request complete, the guest gets another SMS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I Like This Example
&lt;/h2&gt;

&lt;p&gt;It is small enough to demo live, but it covers the parts that often get cut from a "Hello World" voice AI demo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Caller-ID-based room lookup, with a regex-based fallback for unknown callers&lt;/li&gt;
&lt;li&gt;A constrained JSON system prompt so the LLM returns structured output you can trust&lt;/li&gt;
&lt;li&gt;An urgent-phrase override that does not depend on the LLM getting it right&lt;/li&gt;
&lt;li&gt;Slack alerts that match the department&lt;/li&gt;
&lt;li&gt;Idempotent webhook handling so retries do not double-log requests&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That makes it useful as a starting template for any single-vertical phone service where one phone number handles a small set of structured requests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run It Locally
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/team-telnyx/telnyx-code-examples.git
&lt;span class="nb"&gt;cd &lt;/span&gt;telnyx-code-examples/hotel-guest-services-python
&lt;span class="nb"&gt;cp&lt;/span&gt; .env.example .env
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
python app.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expose the webhook:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ngrok http 5000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the Telnyx Portal, set the Voice API app webhook URL to &lt;code&gt;https://&amp;lt;ngrok-id&amp;gt;.ngrok-free.app/webhooks/voice&lt;/code&gt; and the Messaging Profile inbound webhook URL to &lt;code&gt;https://&amp;lt;ngrok-id&amp;gt;.ngrok-free.app/webhooks/sms&lt;/code&gt;. Call or text the Telnyx number to test.&lt;/p&gt;

&lt;h2&gt;
  
  
  What To Demo
&lt;/h2&gt;

&lt;p&gt;I would keep the demo short:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Call from a phone matching room 205. Say "Can I order a club sandwich and a sparkling water please?"&lt;/li&gt;
&lt;li&gt;Show the open request in &lt;code&gt;curl http://localhost:5000/requests&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Show the Slack alert in the staff channel.&lt;/li&gt;
&lt;li&gt;Mark it complete with &lt;code&gt;curl -X POST http://localhost:5000/requests/0/complete&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Show the fulfilment SMS on the guest phone.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Then call from an unknown number, say "Room 205 please", and make a second request to show the unknown-caller path.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where To Take It Next
&lt;/h2&gt;

&lt;p&gt;The demo uses in-memory state, which is fine for learning. Production would replace &lt;code&gt;ROOMS&lt;/code&gt; and &lt;code&gt;service_requests&lt;/code&gt; with your PMS database (Opera, Mews, Cloudbeds), persist call state and event IDs to Redis, queue Slack and SMS side effects, add call recording for QA, and map urgent requests to a real paging system instead of Slack.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Code example: &lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/hotel-guest-services-python" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/telnyx-code-examples/tree/main/hotel-guest-services-python&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Telnyx Voice docs: &lt;a href="https://developers.telnyx.com/docs/voice/call-control" rel="noopener noreferrer"&gt;https://developers.telnyx.com/docs/voice/call-control&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Telnyx AI Inference docs: &lt;a href="https://developers.telnyx.com/docs/inference" rel="noopener noreferrer"&gt;https://developers.telnyx.com/docs/inference&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Telnyx Messaging docs: &lt;a href="https://developers.telnyx.com/docs/messaging" rel="noopener noreferrer"&gt;https://developers.telnyx.com/docs/messaging&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Telnyx Portal: &lt;a href="https://portal.telnyx.com" rel="noopener noreferrer"&gt;https://portal.telnyx.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>telnyx</category>
      <category>ai</category>
      <category>devrel</category>
    </item>
  </channel>
</rss>
