<?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: Sonam</title>
    <description>The latest articles on DEV Community by Sonam (@sonam_50a41a4ced7e6b4f3fa).</description>
    <link>https://dev.to/sonam_50a41a4ced7e6b4f3fa</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%2F4004574%2Ff1f1b787-1d13-4716-b3f7-c8d2d134849c.png</url>
      <title>DEV Community: Sonam</title>
      <link>https://dev.to/sonam_50a41a4ced7e6b4f3fa</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sonam_50a41a4ced7e6b4f3fa"/>
    <language>en</language>
    <item>
      <title>Build an AI Outbound Call Campaign with Python and Telnyx</title>
      <dc:creator>Sonam</dc:creator>
      <pubDate>Wed, 26 Aug 2026 21:58:10 +0000</pubDate>
      <link>https://dev.to/sonam_50a41a4ced7e6b4f3fa/build-an-ai-outbound-call-campaign-with-python-and-telnyx-3hge</link>
      <guid>https://dev.to/sonam_50a41a4ced7e6b4f3fa/build-an-ai-outbound-call-campaign-with-python-and-telnyx-3hge</guid>
      <description>&lt;p&gt;Outbound call campaigns look simple until you need them to behave like real software.&lt;/p&gt;

&lt;p&gt;Calling one number is easy. Calling a list of numbers means you suddenly care about queueing, rate limits, call state, webhook verification, result tracking, and summaries for the team.&lt;/p&gt;

&lt;p&gt;I built a small example for that pattern: an AI Call Campaign Orchestrator using Python, Flask, Telnyx Call Control, the Telnyx Agent SDK, SQLite, and SMS.&lt;/p&gt;

&lt;p&gt;Code: &lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/ai-call-campaign-orchestrator" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/telnyx-code-examples/tree/main/ai-call-campaign-orchestrator&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What the app does
&lt;/h2&gt;

&lt;p&gt;The app exposes a simple API:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;POST /campaign&lt;/code&gt; starts a campaign from a list of phone numbers&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /campaign/&amp;lt;campaign_id&amp;gt;&lt;/code&gt; returns campaign status and call results&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;POST /webhooks/call&lt;/code&gt; receives Telnyx Call Control events&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /health&lt;/code&gt; confirms the service is running&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The interesting part is what happens after you submit the campaign.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;CampaignAgent&lt;/code&gt; queues the calls, applies a rate limit, places outbound calls through Call Control, stores results in SQLite, and sends an SMS summary when the campaign is complete.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST /campaign
  -&amp;gt; queue calls
  -&amp;gt; rate-limit delivery
  -&amp;gt; place calls with Call Control
  -&amp;gt; receive signed webhooks
  -&amp;gt; store results in SQLite
  -&amp;gt; send SMS summary
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Starting a campaign
&lt;/h2&gt;

&lt;p&gt;You send a list of E.164 phone numbers to &lt;code&gt;POST /campaign&lt;/code&gt;:&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/campaign &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;'{
    "phone_numbers": [
      "+15551234567",
      "+15557654321",
      "+15559876543"
    ]
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The API returns a campaign ID:&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="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"campaign_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3f2c1a5e-8b7d-4e6f-9a0c-1d2e3f4a5b6c"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"started"&lt;/span&gt;&lt;span class="w"&gt;
&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;From there, the agent owns the campaign lifecycle.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why rate limiting matters
&lt;/h2&gt;

&lt;p&gt;Outbound communication workflows need pacing.&lt;/p&gt;

&lt;p&gt;If you fire too many calls at once, you can create a bad user experience and run into carrier or operational limits. This sample uses &lt;code&gt;CALL_RATE_LIMIT_PER_MINUTE&lt;/code&gt; so campaign delivery is controlled from configuration instead of buried inside the call loop.&lt;/p&gt;

&lt;p&gt;That makes the campaign behavior easier to tune without rewriting the app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Placing calls with Call Control
&lt;/h2&gt;

&lt;p&gt;For each queued number, the app places an outbound Call Control call with the Telnyx Python SDK.&lt;/p&gt;

&lt;p&gt;The app needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;TELNYX_API_KEY&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TELNYX_CONNECTION_ID&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TELNYX_PHONE_NUMBER&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TELNYX_WEBHOOK_URL&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The webhook URL points back to the app, so call lifecycle events can update campaign state as calls are answered, completed, or fail.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verifying webhooks
&lt;/h2&gt;

&lt;p&gt;The webhook endpoint uses &lt;code&gt;telnyx.webhooks.unwrap()&lt;/code&gt; with the Telnyx Ed25519 public key.&lt;/p&gt;

&lt;p&gt;That matters because webhook handlers should not blindly trust incoming HTTP requests. The app verifies that the event really came from Telnyx before it updates call state.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Telnyx Call Control event
  -&amp;gt; POST /webhooks/call
  -&amp;gt; verify signature
  -&amp;gt; update campaign result
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Tracking status
&lt;/h2&gt;

&lt;p&gt;At any point, you can poll:&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/campaign/&amp;lt;campaign_id&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response includes total calls, completion status, and per-number results.&lt;/p&gt;

&lt;p&gt;That gives you a simple campaign dashboard API without adding another database service just for the sample.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing the loop with SMS
&lt;/h2&gt;

&lt;p&gt;When the campaign finishes, the app sends a summary SMS using Telnyx Messaging.&lt;/p&gt;

&lt;p&gt;You configure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;TELNYX_SMS_FROM&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TELNYX_SMS_TO&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a demo, this is a clean way to prove the workflow completed. In a production version, the same step could also notify Slack, update a CRM, or send a report to another internal system.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I like about this pattern
&lt;/h2&gt;

&lt;p&gt;The app is not trying to be a full campaign product.&lt;/p&gt;

&lt;p&gt;It is showing the backend shape you need for one:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;one API to start the work&lt;/li&gt;
&lt;li&gt;an agent to manage queueing and pacing&lt;/li&gt;
&lt;li&gt;Call Control for the voice workflow&lt;/li&gt;
&lt;li&gt;signed webhooks for lifecycle updates&lt;/li&gt;
&lt;li&gt;SQL for campaign state&lt;/li&gt;
&lt;li&gt;SMS for completion notification&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is the useful part. It gives you the skeleton for a durable communications workflow without starting from a pile of disconnected services.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run it yourself
&lt;/h2&gt;

&lt;p&gt;Clone the repo:&lt;br&gt;
&lt;/p&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-call-campaign-orchestrator
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a virtual environment and install dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Configure your environment:&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="nb"&gt;cp&lt;/span&gt; .env.example .env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then fill in your Telnyx API key, connection ID, phone number, webhook URL, public key, SMS settings, and call rate limit.&lt;/p&gt;

&lt;p&gt;Run 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;Resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code example: &lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/ai-call-campaign-orchestrator" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/telnyx-code-examples/tree/main/ai-call-campaign-orchestrator&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Telnyx AI toolkit: &lt;a href="https://github.com/team-telnyx/ai" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/ai&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Telnyx 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>ai</category>
      <category>python</category>
      <category>voice</category>
      <category>telnyx</category>
    </item>
    <item>
      <title>Build a Live Call Transcription Agent on Telnyx Edge Compute</title>
      <dc:creator>Sonam</dc:creator>
      <pubDate>Tue, 25 Aug 2026 05:38:03 +0000</pubDate>
      <link>https://dev.to/sonam_50a41a4ced7e6b4f3fa/build-a-live-call-transcription-agent-on-telnyx-edge-compute-1ieo</link>
      <guid>https://dev.to/sonam_50a41a4ced7e6b4f3fa/build-a-live-call-transcription-agent-on-telnyx-edge-compute-1ieo</guid>
      <description>&lt;p&gt;Call recordings are useful, but they are usually passive.&lt;/p&gt;

&lt;p&gt;This example turns a live phone call into something your app can use right away.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;edge-call-transcription-agent&lt;/code&gt; example answers an inbound call on Telnyx Edge Compute, starts streaming transcription, stores final transcript segments in an Agent SDK actor, summarizes the call after hangup with Telnyx AI Inference, persists the record, and sends the summary by SMS.&lt;/p&gt;

&lt;p&gt;Code: &lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/edge-call-transcription-agent" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/telnyx-code-examples/tree/main/edge-call-transcription-agent&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What it builds
&lt;/h2&gt;

&lt;p&gt;The app is a TypeScript Edge Compute function with two Agent SDK actors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;TranscribeAgent&lt;/code&gt; - one actor per call&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;TranscriptRegistry&lt;/code&gt; - one shared actor for listing stored transcripts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The call flow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;call.initiated
  -&amp;gt; record call state
  -&amp;gt; answer call

call.answered
  -&amp;gt; speak greeting

call.speak.ended
  -&amp;gt; start inbound transcription

call.transcription
  -&amp;gt; append transcript segment

call.hangup
  -&amp;gt; summarize
  -&amp;gt; store
  -&amp;gt; SMS summary
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Routes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;POST /webhooks/voice&lt;/code&gt; receives Telnyx Call Control events&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /&lt;/code&gt; serves a lightweight dashboard&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /transcripts&lt;/code&gt; lists recent stored transcripts&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /transcripts/:call_control_id&lt;/code&gt; fetches one transcript&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /debug/state?call_control_id=...&lt;/code&gt; shows live actor state&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /health/liveness&lt;/code&gt; and &lt;code&gt;GET /health/readiness&lt;/code&gt; are health checks&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why actors help
&lt;/h2&gt;

&lt;p&gt;Streaming transcription arrives as many events.&lt;/p&gt;

&lt;p&gt;The app needs to remember:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;which call this is&lt;/li&gt;
&lt;li&gt;whether it is answering, transcribing, summarizing, sending, or done&lt;/li&gt;
&lt;li&gt;interim transcript segments&lt;/li&gt;
&lt;li&gt;final transcript text&lt;/li&gt;
&lt;li&gt;start and end timestamps&lt;/li&gt;
&lt;li&gt;summary status&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An actor per call makes that state easy to hold.&lt;/p&gt;

&lt;p&gt;The source maps each &lt;code&gt;call_control_id&lt;/code&gt; to one &lt;code&gt;TranscribeAgent&lt;/code&gt;, so the transcript can build up across webhook events.&lt;/p&gt;

&lt;h2&gt;
  
  
  The AI summary
&lt;/h2&gt;

&lt;p&gt;After hangup, the actor queues a finalize pipeline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;summarize -&amp;gt; store -&amp;gt; notify
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The summary uses Telnyx AI Inference through the Edge Compute binding:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TELNYX&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createCompletion&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;messages&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="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;system&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SUMMARY_SYSTEM_PROMPT&lt;/span&gt; &lt;span class="p"&gt;},&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;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;transcript&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;max_tokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.3&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 default model is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;zai-org/GLM-5.2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The prompt asks for a concise SMS-friendly summary under 320 characters.&lt;/p&gt;

&lt;h2&gt;
  
  
  SMS notification
&lt;/h2&gt;

&lt;p&gt;Once the summary is ready, the app sends it over Telnyx Messaging:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TELNYX&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SMS_FROM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SMS_TO&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;summary&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;Use placeholders in examples and configure real numbers only as private runtime values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SMS_FROM=&amp;lt;TELNYX_SMS_FROM&amp;gt;
SMS_TO=&amp;lt;SUMMARY_RECIPIENT&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Try 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/edge-call-transcription-agent
npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;telnyx-edge secret &lt;span class="nb"&gt;set &lt;/span&gt;TELNYX_API_KEY &amp;lt;YOUR_TELNYX_API_KEY&amp;gt;
telnyx-edge secret &lt;span class="nb"&gt;set &lt;/span&gt;SMS_FROM &amp;lt;TELNYX_SMS_FROM&amp;gt;
telnyx-edge secret &lt;span class="nb"&gt;set &lt;/span&gt;SMS_TO &amp;lt;SUMMARY_RECIPIENT&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;telnyx-edge ship
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Point your Call Control app 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://edge-call-transcription-agent-&amp;lt;id&amp;gt;.telnyxcompute.com/webhooks/voice
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then call the Telnyx number assigned to the Call Control application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Production notes
&lt;/h2&gt;

&lt;p&gt;Before using this with real callers, add:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;webhook signature verification&lt;/li&gt;
&lt;li&gt;auth on transcript and debug endpoints&lt;/li&gt;
&lt;li&gt;transcript redaction&lt;/li&gt;
&lt;li&gt;retention rules&lt;/li&gt;
&lt;li&gt;SMS opt-out and consent handling&lt;/li&gt;
&lt;li&gt;idempotency for Call Control and SMS actions&lt;/li&gt;
&lt;li&gt;monitoring for STT, LLM summary, storage, and SMS delivery&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The useful pattern is simple: live call events, durable state, AI summary, and post-call notification in one edge app.&lt;/p&gt;

&lt;p&gt;Resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code: &lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/edge-call-transcription-agent" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/telnyx-code-examples/tree/main/edge-call-transcription-agent&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Agent SDK docs: &lt;a href="https://developers.telnyx.com/docs/agent-sdk" rel="noopener noreferrer"&gt;https://developers.telnyx.com/docs/agent-sdk&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Edge Compute docs: &lt;a href="https://developers.telnyx.com/docs/edge-compute" rel="noopener noreferrer"&gt;https://developers.telnyx.com/docs/edge-compute&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Call Control API reference: &lt;a href="https://developers.telnyx.com/api-reference/call-control" rel="noopener noreferrer"&gt;https://developers.telnyx.com/api-reference/call-control&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Streaming transcription guide: &lt;a href="https://developers.telnyx.com/docs/voice/programmable-voice/transcription" rel="noopener noreferrer"&gt;https://developers.telnyx.com/docs/voice/programmable-voice/transcription&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 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 AI skills and toolkits: &lt;a href="https://github.com/team-telnyx/ai" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/ai&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>voice</category>
      <category>edge</category>
      <category>telnyx</category>
    </item>
    <item>
      <title>Switch AI Models at Runtime on Telnyx Edge Compute</title>
      <dc:creator>Sonam</dc:creator>
      <pubDate>Thu, 20 Aug 2026 22:13:11 +0000</pubDate>
      <link>https://dev.to/sonam_50a41a4ced7e6b4f3fa/switch-ai-models-at-runtime-on-telnyx-edge-compute-2ncf</link>
      <guid>https://dev.to/sonam_50a41a4ced7e6b4f3fa/switch-ai-models-at-runtime-on-telnyx-edge-compute-2ncf</guid>
      <description>&lt;p&gt;Most AI examples hardcode the model name.&lt;/p&gt;

&lt;p&gt;That is fine until you actually want to compare models.&lt;/p&gt;

&lt;p&gt;If every model change requires a code edit and redeploy, experimenting gets annoying fast. The &lt;code&gt;multi-model-inference-switcher&lt;/code&gt; example turns model choice into runtime configuration instead.&lt;/p&gt;

&lt;p&gt;Code: &lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/multi-model-inference-switcher" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/telnyx-code-examples/tree/main/multi-model-inference-switcher&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What it builds
&lt;/h2&gt;

&lt;p&gt;This is a TypeScript app running on Telnyx Edge Compute with the Agent SDK.&lt;/p&gt;

&lt;p&gt;It gives you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;an admin UI&lt;/li&gt;
&lt;li&gt;a model dropdown&lt;/li&gt;
&lt;li&gt;a chat panel&lt;/li&gt;
&lt;li&gt;durable conversation history&lt;/li&gt;
&lt;li&gt;usage stats by model&lt;/li&gt;
&lt;li&gt;a KV-backed &lt;code&gt;active-model&lt;/code&gt; flag&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The active model is read from Telnyx KV Storage every time &lt;code&gt;/chat&lt;/code&gt; is called. When you switch the model from the UI or API, the next message uses the new model immediately.&lt;/p&gt;

&lt;p&gt;No redeploy.&lt;/p&gt;

&lt;h2&gt;
  
  
  The flow
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET /
  -&amp;gt; admin UI

POST /model
  -&amp;gt; validate model
  -&amp;gt; write active-model to KV

POST /chat
  -&amp;gt; read active-model from KV
  -&amp;gt; SwitcherAgent.process(text, model)
  -&amp;gt; Telnyx AI Inference
  -&amp;gt; return reply + model
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;moonshotai/Kimi-K2.6&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;zai-org/GLM-5.2&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;meta-llama/Llama-3.3-70B-Instruct&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why this is useful
&lt;/h2&gt;

&lt;p&gt;Model choice is product behavior.&lt;/p&gt;

&lt;p&gt;Changing the model can affect:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;latency&lt;/li&gt;
&lt;li&gt;cost&lt;/li&gt;
&lt;li&gt;output quality&lt;/li&gt;
&lt;li&gt;reasoning depth&lt;/li&gt;
&lt;li&gt;tone&lt;/li&gt;
&lt;li&gt;reliability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So it helps to make the active model observable and switchable without mixing that decision into application deploys.&lt;/p&gt;

&lt;h2&gt;
  
  
  API examples
&lt;/h2&gt;

&lt;p&gt;Switch the active model:&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 https://multi-model-inference-switcher-&amp;lt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;.telnyxcompute.com/model &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;'{"model":"zai-org/GLM-5.2"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Send a chat message:&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 https://multi-model-inference-switcher-&amp;lt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;.telnyxcompute.com/chat &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;'{"text":"Explain feature flags for AI models."}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example response:&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="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reply"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Feature flags let you change behavior at runtime..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"model"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"zai-org/GLM-5.2"&lt;/span&gt;&lt;span class="w"&gt;
&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;Inspect history and usage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://multi-model-inference-switcher-&amp;lt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;.telnyxcompute.com/history
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Agent SDK primitives used
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;SwitcherAgent&lt;/code&gt; uses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;durable message history for chat context&lt;/li&gt;
&lt;li&gt;actor state for total requests and model usage&lt;/li&gt;
&lt;li&gt;the Telnyx binding for zero-credential AI Inference&lt;/li&gt;
&lt;li&gt;KV Storage for the global model flag&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The inference call looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TELNYX&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createCompletion&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;max_tokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.7&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 key part is that &lt;code&gt;model&lt;/code&gt; comes from KV, not a hardcoded constant.&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-model-inference-switcher
npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create and seed KV:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;telnyx-edge storage kv create &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="s2"&gt;"switcher-flag"&lt;/span&gt;
telnyx-edge storage kv key put &amp;lt;kv-id&amp;gt; active-model moonshotai/Kimi-K2.6
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set your namespace ID in &lt;code&gt;telnyx.toml&lt;/code&gt;, add your secret, and deploy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;telnyx-edge secrets add TELNYX_API_KEY &amp;lt;YOUR_API_KEY&amp;gt;
telnyx-edge ship
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Production notes
&lt;/h2&gt;

&lt;p&gt;Before exposing this publicly, add:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;auth on the admin UI and &lt;code&gt;/model&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;audit logging for model changes&lt;/li&gt;
&lt;li&gt;a reviewed model allowlist&lt;/li&gt;
&lt;li&gt;fallback behavior if the active model fails&lt;/li&gt;
&lt;li&gt;latency and cost tracking per model&lt;/li&gt;
&lt;li&gt;scoped flags by environment, tenant, or cohort&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The small idea here is powerful: keep your app deployed, but make model selection something you can operate.&lt;/p&gt;

&lt;p&gt;Resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code: &lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/multi-model-inference-switcher" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/telnyx-code-examples/tree/main/multi-model-inference-switcher&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Agent SDK docs: &lt;a href="https://developers.telnyx.com/docs/agent-sdk" rel="noopener noreferrer"&gt;https://developers.telnyx.com/docs/agent-sdk&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Edge Compute docs: &lt;a href="https://developers.telnyx.com/docs/edge-compute" rel="noopener noreferrer"&gt;https://developers.telnyx.com/docs/edge-compute&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 AI skills and toolkits: &lt;a href="https://github.com/team-telnyx/ai" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/ai&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>edge</category>
      <category>typescript</category>
      <category>telnyx</category>
    </item>
    <item>
      <title>Build an SMS Triage Bot on Telnyx Edge Compute</title>
      <dc:creator>Sonam</dc:creator>
      <pubDate>Thu, 13 Aug 2026 18:58:07 +0000</pubDate>
      <link>https://dev.to/sonam_50a41a4ced7e6b4f3fa/build-an-sms-triage-bot-on-telnyx-edge-compute-4gin</link>
      <guid>https://dev.to/sonam_50a41a4ced7e6b4f3fa/build-an-sms-triage-bot-on-telnyx-edge-compute-4gin</guid>
      <description>&lt;p&gt;Support SMS inboxes are usually a routing problem before they are an AI problem.&lt;/p&gt;

&lt;p&gt;Someone asks about billing. Someone else needs technical support. A third person wants to talk to sales. The app has to understand the message, pick the right destination, reply to the customer, and remember what happened.&lt;/p&gt;

&lt;p&gt;This TypeScript example does that on Telnyx Edge Compute with the Agent SDK.&lt;/p&gt;

&lt;p&gt;Code: &lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/agent-sms-triage-bot" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/telnyx-code-examples/tree/main/agent-sms-triage-bot&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What it builds
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;agent-sms-triage-bot&lt;/code&gt; receives inbound SMS webhooks, classifies each message into one of four topics, looks up the route for that topic, replies by SMS, and stores triage history in durable actor state.&lt;/p&gt;

&lt;p&gt;The topics are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;billing&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;support&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sales&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;general&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The default route table maps those topics to queue names:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;billing -&amp;gt; billing-queue
support -&amp;gt; support-queue
sales   -&amp;gt; sales-queue
general -&amp;gt; general-queue
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The request flow
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Inbound SMS
  -&amp;gt; POST /webhooks/sms
  -&amp;gt; TriageAgent.triage(from, text)
  -&amp;gt; Telnyx AI Inference classifies topic
  -&amp;gt; durable route table lookup
  -&amp;gt; SMS reply
  -&amp;gt; triage history update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The app uses one &lt;code&gt;TriageAgent&lt;/code&gt; actor per inbound number. That actor stores route rules, recent history, total messages, and topic counts.&lt;/p&gt;

&lt;h2&gt;
  
  
  The main routes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;POST /webhooks/sms&lt;/code&gt; receives Telnyx &lt;code&gt;message.received&lt;/code&gt; events&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;POST /debug/triage&lt;/code&gt; simulates inbound SMS&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;POST /routes&lt;/code&gt; updates the route table&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /routes&lt;/code&gt; lists route rules&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /history&lt;/code&gt; returns recent triage history&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /debug/state&lt;/code&gt; inspects actor state&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /health/liveness&lt;/code&gt; and &lt;code&gt;GET /health/readiness&lt;/code&gt; provide health checks&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Agent SDK piece
&lt;/h2&gt;

&lt;p&gt;The core class is &lt;code&gt;TriageAgent&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It extends the Agent SDK &lt;code&gt;Agent&lt;/code&gt; class and uses durable state for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;route table&lt;/li&gt;
&lt;li&gt;triage history&lt;/li&gt;
&lt;li&gt;total message count&lt;/li&gt;
&lt;li&gt;topic counts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The AI classification call uses the Telnyx binding:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;completion&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TELNYX&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createCompletion&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AI_MODEL&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;moonshotai/Kimi-K2.6&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;messages&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="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;system&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CLASSIFY_SYSTEM_PROMPT&lt;/span&gt; &lt;span class="p"&gt;},&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;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Customer message: "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"`&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;max_tokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.2&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 SMS reply uses the same binding pattern:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TELNYX&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fromNumber&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;phoneNumber&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;replyText&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 means the example does not hardcode an API key in application code. Messaging and inference both go through &lt;code&gt;this.env.TELNYX&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try the debug endpoint
&lt;/h2&gt;

&lt;p&gt;After deploying with &lt;code&gt;telnyx-edge ship&lt;/code&gt;, you can test without sending a real SMS:&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 https://agent-sms-triage-bot-&amp;lt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;.telnyxcompute.com/debug/triage &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;'{"from":"&amp;lt;customer-number&amp;gt;","to":"&amp;lt;triage-number&amp;gt;","text":"Why was I charged twice this month?"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example response:&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="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"triaged"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"from"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;customer-number&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"to"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;triage-number&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Why was I charged twice this month?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"topic"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"billing"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"route"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"billing-queue"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"confidence"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.95&lt;/span&gt;&lt;span class="w"&gt;
&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;Then inspect the actor history:&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="s2"&gt;"https://agent-sms-triage-bot-&amp;lt;id&amp;gt;.telnyxcompute.com/history?number=&amp;lt;triage-number&amp;gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why this pattern is useful
&lt;/h2&gt;

&lt;p&gt;For a small support workflow, this keeps the first version compact:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Telnyx Messaging receives and sends SMS&lt;/li&gt;
&lt;li&gt;Telnyx AI Inference classifies intent&lt;/li&gt;
&lt;li&gt;Edge Compute hosts the webhook&lt;/li&gt;
&lt;li&gt;Agent SDK durable state stores routing memory&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can later replace queue names with real integrations: Slack, Zendesk, Salesforce, email, or an internal queue.&lt;/p&gt;

&lt;h2&gt;
  
  
  Production notes
&lt;/h2&gt;

&lt;p&gt;Before using this with real customer traffic, I would add:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;webhook signature verification&lt;/li&gt;
&lt;li&gt;SMS opt-out and consent handling&lt;/li&gt;
&lt;li&gt;duplicate webhook idempotency&lt;/li&gt;
&lt;li&gt;PII redaction and retention controls&lt;/li&gt;
&lt;li&gt;role-based route updates&lt;/li&gt;
&lt;li&gt;human review for low-confidence classifications&lt;/li&gt;
&lt;li&gt;alerting when outbound SMS fails&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code: &lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/agent-sms-triage-bot" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/telnyx-code-examples/tree/main/agent-sms-triage-bot&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Agent SDK docs: &lt;a href="https://developers.telnyx.com/docs/agent-sdk" rel="noopener noreferrer"&gt;https://developers.telnyx.com/docs/agent-sdk&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Edge Compute docs: &lt;a href="https://developers.telnyx.com/docs/edge-compute" rel="noopener noreferrer"&gt;https://developers.telnyx.com/docs/edge-compute&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 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 AI skills and toolkits: &lt;a href="https://github.com/team-telnyx/ai" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/ai&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>sms</category>
      <category>edge</category>
      <category>telnyx</category>
    </item>
    <item>
      <title>Build an AI Snooze Reminder Agent on Telnyx Edge Compute</title>
      <dc:creator>Sonam</dc:creator>
      <pubDate>Thu, 13 Aug 2026 18:28:40 +0000</pubDate>
      <link>https://dev.to/sonam_50a41a4ced7e6b4f3fa/build-an-ai-snooze-reminder-agent-on-telnyx-edge-compute-7bh</link>
      <guid>https://dev.to/sonam_50a41a4ced7e6b4f3fa/build-an-ai-snooze-reminder-agent-on-telnyx-edge-compute-7bh</guid>
      <description>&lt;p&gt;Most reminder examples stop at "send this message later."&lt;/p&gt;

&lt;p&gt;This one goes a little further.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;scheduled-reminder-agent&lt;/code&gt; example sends an SMS reminder, waits for the user's reply, uses Telnyx AI Inference to classify whether the reply means "snooze" or "acknowledged," and then either marks the reminder done or schedules the next reminder with adaptive timing.&lt;/p&gt;

&lt;p&gt;Code: &lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/scheduled-reminder-agent" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/telnyx-code-examples/tree/main/scheduled-reminder-agent&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;p&gt;The app runs on Telnyx Edge Compute with the Telnyx Agent SDK.&lt;/p&gt;

&lt;p&gt;The flow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST /remind
  -&amp;gt; ReminderAgent.scheduleReminder()
  -&amp;gt; this.schedule(delay, "sendReminder")
  -&amp;gt; Telnyx Messaging sends the SMS
  -&amp;gt; user replies by SMS
  -&amp;gt; /webhooks/sms
  -&amp;gt; ReminderAgent.receiveReply()
  -&amp;gt; Telnyx AI Inference classifies intent
  -&amp;gt; reminder is acknowledged or snoozed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One actor instance owns one recipient's reminder state.&lt;/p&gt;

&lt;p&gt;That means active reminders, snooze count, reply status, and scheduled work live together instead of being split across a webhook server, queue, scheduler, and database.&lt;/p&gt;

&lt;h2&gt;
  
  
  Agent SDK primitives used
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;ReminderAgent&lt;/code&gt; extends the Agent SDK &lt;code&gt;Agent&lt;/code&gt; class from &lt;code&gt;@telnyx/edge-runtime&lt;/code&gt;.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;this.schedule()&lt;/code&gt; for durable reminder delivery and reply timeouts&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;this.setState()&lt;/code&gt; and &lt;code&gt;this.getState()&lt;/code&gt; for per-phone-number reminder state&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;this.env.TELNYX.messages.send()&lt;/code&gt; for SMS&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;this.env.TELNYX.ai.openai.chat.createCompletion()&lt;/code&gt; for snooze detection&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;[telnyx]&lt;/code&gt; binding in &lt;code&gt;telnyx.toml&lt;/code&gt; gives the function a pre-authenticated Telnyx client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[telnyx]&lt;/span&gt;
&lt;span class="py"&gt;binding&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"TELNYX"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So the application code does not hardcode an API key.&lt;/p&gt;

&lt;h2&gt;
  
  
  Main routes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;POST /remind&lt;/code&gt; schedules a new SMS reminder&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;POST /webhooks/sms&lt;/code&gt; receives Telnyx inbound SMS replies&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;POST /cancel&lt;/code&gt; cancels a pending reminder&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;POST /debug/remind&lt;/code&gt; schedules a short test reminder&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;POST /debug/reply&lt;/code&gt; simulates an inbound SMS reply&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /debug/state&lt;/code&gt; inspects actor state&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /health/liveness&lt;/code&gt; and &lt;code&gt;GET /health/readiness&lt;/code&gt; are health checks&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;After deploying with &lt;code&gt;telnyx-edge ship&lt;/code&gt;, schedule a reminder:&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 https://scheduled-reminder-agent-&amp;lt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;.telnyxcompute.com/remind &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;'{
    "to":"+15551230000",
    "from":"+15559870000",
    "message":"Time for your meeting",
    "delay_minutes":5
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then simulate a snooze reply:&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 https://scheduled-reminder-agent-&amp;lt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;.telnyxcompute.com/debug/reply &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;'{
    "from":"+15551230000",
    "text":"snooze for 2 hours"
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inspect the state:&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="s2"&gt;"https://scheduled-reminder-agent-&amp;lt;id&amp;gt;.telnyxcompute.com/debug/state?from=+15551230000"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Adaptive snoozing
&lt;/h2&gt;

&lt;p&gt;If the user says something specific like "snooze for 2 hours," the model extracts that delay.&lt;/p&gt;

&lt;p&gt;If the user just says "later" or "not now," the agent uses exponential backoff:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;30 minutes -&amp;gt; 1 hour -&amp;gt; 2 hours -&amp;gt; 4 hours -&amp;gt; 8 hours
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That makes the reminder less repetitive over time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Production notes
&lt;/h2&gt;

&lt;p&gt;Before using this in production, I would add:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;webhook signature verification&lt;/li&gt;
&lt;li&gt;SMS opt-out and consent handling&lt;/li&gt;
&lt;li&gt;timezone-aware scheduling&lt;/li&gt;
&lt;li&gt;idempotency for reminder creation and outbound sends&lt;/li&gt;
&lt;li&gt;retry handling for failed SMS delivery&lt;/li&gt;
&lt;li&gt;max snooze and escalation rules&lt;/li&gt;
&lt;li&gt;PII-safe logs and retention policies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But as a starter pattern, this is a clean way to build scheduled agent workflows on Edge Compute.&lt;/p&gt;

&lt;p&gt;Resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code: &lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/scheduled-reminder-agent" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/telnyx-code-examples/tree/main/scheduled-reminder-agent&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Agent SDK docs: &lt;a href="https://developers.telnyx.com/docs/agent-sdk" rel="noopener noreferrer"&gt;https://developers.telnyx.com/docs/agent-sdk&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Agent SDK quickstart: &lt;a href="https://developers.telnyx.com/docs/agent-sdk/quickstart" rel="noopener noreferrer"&gt;https://developers.telnyx.com/docs/agent-sdk/quickstart&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Edge Compute docs: &lt;a href="https://developers.telnyx.com/docs/edge-compute" rel="noopener noreferrer"&gt;https://developers.telnyx.com/docs/edge-compute&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 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 AI skills and toolkits: &lt;a href="https://github.com/team-telnyx/ai" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/ai&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>sms</category>
      <category>edge</category>
      <category>telnyx</category>
    </item>
    <item>
      <title>Build an SMS Triage Bot on Telnyx Edge Compute</title>
      <dc:creator>Sonam</dc:creator>
      <pubDate>Wed, 12 Aug 2026 01:59:23 +0000</pubDate>
      <link>https://dev.to/sonam_50a41a4ced7e6b4f3fa/build-an-sms-triage-bot-on-telnyx-edge-compute-4g0</link>
      <guid>https://dev.to/sonam_50a41a4ced7e6b4f3fa/build-an-sms-triage-bot-on-telnyx-edge-compute-4g0</guid>
      <description>&lt;p&gt;Support SMS inboxes are usually a routing problem before they are an AI problem.&lt;/p&gt;

&lt;p&gt;Someone asks about billing. Someone else needs technical support. A third person wants to talk to sales. The app has to understand the message, pick the right destination, reply to the customer, and remember what happened.&lt;/p&gt;

&lt;p&gt;This TypeScript example does that on Telnyx Edge Compute with the Agent SDK.&lt;/p&gt;

&lt;p&gt;Code: &lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/agent-sms-triage-bot" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/telnyx-code-examples/tree/main/agent-sms-triage-bot&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What it builds
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;agent-sms-triage-bot&lt;/code&gt; receives inbound SMS webhooks, classifies each message into one of four topics, looks up the route for that topic, replies by SMS, and stores triage history in durable actor state.&lt;/p&gt;

&lt;p&gt;The topics are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;billing&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;support&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sales&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;general&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The default route table maps those topics to queue names:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;billing -&amp;gt; billing-queue
support -&amp;gt; support-queue
sales   -&amp;gt; sales-queue
general -&amp;gt; general-queue
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The request flow
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Inbound SMS
  -&amp;gt; POST /webhooks/sms
  -&amp;gt; TriageAgent.triage(from, text)
  -&amp;gt; Telnyx AI Inference classifies topic
  -&amp;gt; durable route table lookup
  -&amp;gt; SMS reply
  -&amp;gt; triage history update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The app uses one &lt;code&gt;TriageAgent&lt;/code&gt; actor per inbound number. That actor stores route rules, recent history, total messages, and topic counts.&lt;/p&gt;

&lt;h2&gt;
  
  
  The main routes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;POST /webhooks/sms&lt;/code&gt; receives Telnyx &lt;code&gt;message.received&lt;/code&gt; events&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;POST /debug/triage&lt;/code&gt; simulates inbound SMS&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;POST /routes&lt;/code&gt; updates the route table&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /routes&lt;/code&gt; lists route rules&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /history&lt;/code&gt; returns recent triage history&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /debug/state&lt;/code&gt; inspects actor state&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /health/liveness&lt;/code&gt; and &lt;code&gt;GET /health/readiness&lt;/code&gt; provide health checks&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Agent SDK piece
&lt;/h2&gt;

&lt;p&gt;The core class is &lt;code&gt;TriageAgent&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It extends the Agent SDK &lt;code&gt;Agent&lt;/code&gt; class and uses durable state for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;route table&lt;/li&gt;
&lt;li&gt;triage history&lt;/li&gt;
&lt;li&gt;total message count&lt;/li&gt;
&lt;li&gt;topic counts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The AI classification call uses the Telnyx binding:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;completion&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TELNYX&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createCompletion&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AI_MODEL&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;moonshotai/Kimi-K2.6&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;messages&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="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;system&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CLASSIFY_SYSTEM_PROMPT&lt;/span&gt; &lt;span class="p"&gt;},&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;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Customer message: "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"`&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;max_tokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.2&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 SMS reply uses the same binding pattern:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TELNYX&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fromNumber&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;phoneNumber&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;replyText&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 means the example does not hardcode an API key in application code. Messaging and inference both go through &lt;code&gt;this.env.TELNYX&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try the debug endpoint
&lt;/h2&gt;

&lt;p&gt;After deploying with &lt;code&gt;telnyx-edge ship&lt;/code&gt;, you can test without sending a real SMS:&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 https://agent-sms-triage-bot-&amp;lt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;.telnyxcompute.com/debug/triage &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;'{"from":"&amp;lt;customer-number&amp;gt;","to":"&amp;lt;triage-number&amp;gt;","text":"Why was I charged twice this month?"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example response:&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="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"triaged"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"from"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;customer-number&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"to"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;triage-number&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Why was I charged twice this month?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"topic"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"billing"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"route"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"billing-queue"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"confidence"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.95&lt;/span&gt;&lt;span class="w"&gt;
&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;Then inspect the actor history:&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="s2"&gt;"https://agent-sms-triage-bot-&amp;lt;id&amp;gt;.telnyxcompute.com/history?number=&amp;lt;triage-number&amp;gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why this pattern is useful
&lt;/h2&gt;

&lt;p&gt;For a small support workflow, this keeps the first version compact:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Telnyx Messaging receives and sends SMS&lt;/li&gt;
&lt;li&gt;Telnyx AI Inference classifies intent&lt;/li&gt;
&lt;li&gt;Edge Compute hosts the webhook&lt;/li&gt;
&lt;li&gt;Agent SDK durable state stores routing memory&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can later replace queue names with real integrations: Slack, Zendesk, Salesforce, email, or an internal queue.&lt;/p&gt;

&lt;h2&gt;
  
  
  Production notes
&lt;/h2&gt;

&lt;p&gt;Before using this with real customer traffic, I would add:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;webhook signature verification&lt;/li&gt;
&lt;li&gt;SMS opt-out and consent handling&lt;/li&gt;
&lt;li&gt;duplicate webhook idempotency&lt;/li&gt;
&lt;li&gt;PII redaction and retention controls&lt;/li&gt;
&lt;li&gt;role-based route updates&lt;/li&gt;
&lt;li&gt;human review for low-confidence classifications&lt;/li&gt;
&lt;li&gt;alerting when outbound SMS fails&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code: &lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/agent-sms-triage-bot" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/telnyx-code-examples/tree/main/agent-sms-triage-bot&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Agent SDK docs: &lt;a href="https://developers.telnyx.com/docs/agent-sdk" rel="noopener noreferrer"&gt;https://developers.telnyx.com/docs/agent-sdk&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Edge Compute docs: &lt;a href="https://developers.telnyx.com/docs/edge-compute" rel="noopener noreferrer"&gt;https://developers.telnyx.com/docs/edge-compute&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 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 AI skills and toolkits: &lt;a href="https://github.com/team-telnyx/ai" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/ai&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>sms</category>
      <category>edge</category>
      <category>telnyx</category>
    </item>
    <item>
      <title>Build a Voice Agent That Holds a Live Call on Telnyx Edge Compute</title>
      <dc:creator>Sonam</dc:creator>
      <pubDate>Wed, 12 Aug 2026 00:35:01 +0000</pubDate>
      <link>https://dev.to/sonam_50a41a4ced7e6b4f3fa/build-a-voice-agent-that-holds-a-live-call-on-telnyx-edge-compute-33im</link>
      <guid>https://dev.to/sonam_50a41a4ced7e6b4f3fa/build-a-voice-agent-that-holds-a-live-call-on-telnyx-edge-compute-33im</guid>
      <description>&lt;p&gt;Getting an AI agent to answer a phone call is only the beginning.&lt;/p&gt;

&lt;p&gt;The harder part is keeping the call going.&lt;/p&gt;

&lt;p&gt;This example builds a voice agent on Telnyx Edge Compute that answers an inbound call, speaks a greeting, listens with streaming transcription, sends the caller's speech to Telnyx AI Inference, speaks the reply with TTS, and then goes back to listening.&lt;/p&gt;

&lt;p&gt;Code: &lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/edge-voice-agent-holds-call" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/telnyx-code-examples/tree/main/edge-voice-agent-holds-call&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The call loop
&lt;/h2&gt;

&lt;p&gt;The core flow is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;call.initiated
  -&amp;gt; answer
call.answered
  -&amp;gt; speak greeting
call.speak.ended
  -&amp;gt; start transcription
call.transcription final
  -&amp;gt; stop transcription
  -&amp;gt; run LLM turn
  -&amp;gt; speak reply
call.speak.ended
  -&amp;gt; listen again
call.hangup
  -&amp;gt; persist final call state
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each live call gets a &lt;code&gt;VoiceAgent&lt;/code&gt; actor keyed by &lt;code&gt;call_control_id&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That actor stores:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;call phase&lt;/li&gt;
&lt;li&gt;caller and callee&lt;/li&gt;
&lt;li&gt;turn count&lt;/li&gt;
&lt;li&gt;last transcript&lt;/li&gt;
&lt;li&gt;last reply&lt;/li&gt;
&lt;li&gt;durable message history&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The main routes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;POST /webhooks/voice&lt;/code&gt; receives Call Control webhooks&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /debug/call?call_control_id=&amp;lt;call_id&amp;gt;&lt;/code&gt; inspects actor state&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;POST /debug/respond&lt;/code&gt; tests an LLM turn without a live call&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /health/liveness&lt;/code&gt; and &lt;code&gt;GET /health/readiness&lt;/code&gt; support health checks&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Agent SDK piece
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;VoiceAgent&lt;/code&gt; extends the Agent SDK &lt;code&gt;Agent&lt;/code&gt; class from &lt;code&gt;@telnyx/edge-runtime&lt;/code&gt;.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;this.messages.add()&lt;/code&gt; to store caller and assistant turns&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;this.messages.toOpenAI()&lt;/code&gt; to format history for the model&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;this.setState()&lt;/code&gt; and &lt;code&gt;this.getState()&lt;/code&gt; for durable call state&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;this.env.TELNYX.ai.openai.chat.createCompletion()&lt;/code&gt; for zero-credential inference through the &lt;code&gt;[telnyx]&lt;/code&gt; binding&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The model call looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;completion&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TELNYX&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createCompletion&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;:&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;system&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SYSTEM_PROMPT&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;max_tokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.5&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 webhook handler speaks the returned text through Call Control TTS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why streaming transcription?
&lt;/h2&gt;

&lt;p&gt;The sample uses Call Control streaming transcription because the app owns the conversation loop.&lt;/p&gt;

&lt;p&gt;The caller speaks. Telnyx sends a final transcript. The app stops transcription, runs the LLM turn, speaks the reply, and starts transcription again after TTS ends.&lt;/p&gt;

&lt;p&gt;That gives you room to add your own logic:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;human handoff&lt;/li&gt;
&lt;li&gt;CRM lookup&lt;/li&gt;
&lt;li&gt;order status tools&lt;/li&gt;
&lt;li&gt;turn limits&lt;/li&gt;
&lt;li&gt;cost tracking&lt;/li&gt;
&lt;li&gt;transcript summaries&lt;/li&gt;
&lt;li&gt;safety rules&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Deploy
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install
&lt;/span&gt;telnyx-edge ship
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set the Call Control API key as an Edge secret:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;telnyx-edge secret &lt;span class="nb"&gt;set &lt;/span&gt;TELNYX_API_KEY your_telnyx_api_key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Point your 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://edge-voice-agent-holds-call-&amp;lt;id&amp;gt;.telnyxcompute.com/webhooks/voice
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then call the Telnyx number assigned to that Call Control application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Production notes
&lt;/h2&gt;

&lt;p&gt;Before shipping this pattern for real callers, I would add:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;webhook signature verification&lt;/li&gt;
&lt;li&gt;idempotency for Call Control actions&lt;/li&gt;
&lt;li&gt;max turn count and call-duration limits&lt;/li&gt;
&lt;li&gt;human transfer&lt;/li&gt;
&lt;li&gt;transcript redaction&lt;/li&gt;
&lt;li&gt;latency monitoring across STT, LLM, and TTS&lt;/li&gt;
&lt;li&gt;stronger barge-in handling&lt;/li&gt;
&lt;li&gt;observability around call phases&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But even as a starter, this is a useful reference for building a voice agent that can actually stay on the call.&lt;/p&gt;

&lt;p&gt;Resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code: &lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/edge-voice-agent-holds-call" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/telnyx-code-examples/tree/main/edge-voice-agent-holds-call&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Agent SDK docs: &lt;a href="https://developers.telnyx.com/docs/agent-sdk" rel="noopener noreferrer"&gt;https://developers.telnyx.com/docs/agent-sdk&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Edge Compute docs: &lt;a href="https://developers.telnyx.com/docs/edge-compute" rel="noopener noreferrer"&gt;https://developers.telnyx.com/docs/edge-compute&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Call Control API reference: &lt;a href="https://developers.telnyx.com/api-reference/call-control" rel="noopener noreferrer"&gt;https://developers.telnyx.com/api-reference/call-control&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Streaming transcription guide: &lt;a href="https://developers.telnyx.com/docs/voice/programmable-voice/transcription" rel="noopener noreferrer"&gt;https://developers.telnyx.com/docs/voice/programmable-voice/transcription&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 AI skills and toolkits: &lt;a href="https://github.com/team-telnyx/ai" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/ai&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>voice</category>
      <category>edge</category>
      <category>telnyx</category>
    </item>
    <item>
      <title>Build an SMS Support Agent with Follow-Up on Telnyx Edge Compute</title>
      <dc:creator>Sonam</dc:creator>
      <pubDate>Tue, 11 Aug 2026 21:51:10 +0000</pubDate>
      <link>https://dev.to/sonam_50a41a4ced7e6b4f3fa/build-an-sms-support-agent-with-follow-up-on-telnyx-edge-compute-2aei</link>
      <guid>https://dev.to/sonam_50a41a4ced7e6b4f3fa/build-an-sms-support-agent-with-follow-up-on-telnyx-edge-compute-2aei</guid>
      <description>&lt;p&gt;A lot of AI support demos stop after one reply.&lt;/p&gt;

&lt;p&gt;Customer asks a question. Model answers. Demo done.&lt;/p&gt;

&lt;p&gt;Real support usually needs a little more than that. You need message history, background processing, a way to send the reply, and sometimes a follow-up if the customer goes quiet.&lt;/p&gt;

&lt;p&gt;This example builds that flow as an SMS support agent on Telnyx Edge Compute.&lt;/p&gt;

&lt;p&gt;Code: &lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/sms-support-agent-with-followup" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/telnyx-code-examples/tree/main/sms-support-agent-with-followup&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;p&gt;The app receives inbound SMS messages, routes each sender to a durable Agent SDK actor, calls Telnyx AI Inference, sends a reply through Telnyx Messaging, and schedules a 24-hour follow-up.&lt;/p&gt;

&lt;p&gt;The flow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Inbound SMS
  -&amp;gt; /webhooks/sms
  -&amp;gt; SupportAgent.receive()
  -&amp;gt; this.messages.add()
  -&amp;gt; this.queue("process")
  -&amp;gt; Telnyx AI Inference
  -&amp;gt; Telnyx Messaging reply
  -&amp;gt; this.schedule(24h, "followup")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important part is that the webhook does not need to wait on the LLM. It stores the message and queues the work, so the webhook can acknowledge quickly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Agent SDK primitives used
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;SupportAgent&lt;/code&gt; extends the Agent SDK &lt;code&gt;Agent&lt;/code&gt; class from &lt;code&gt;@telnyx/edge-runtime&lt;/code&gt;.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;this.messages.add()&lt;/code&gt; for durable conversation history&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;this.messages.toOpenAI()&lt;/code&gt; to format history for the model&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;this.queue("process")&lt;/code&gt; for background AI processing&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;this.schedule(86400, "followup")&lt;/code&gt; for the next-day check-in&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;this.setState()&lt;/code&gt; and &lt;code&gt;this.getState()&lt;/code&gt; for sender/recipient state&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;this.env.TELNYX&lt;/code&gt; for zero-credential Telnyx API access&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;[telnyx]&lt;/code&gt; binding in &lt;code&gt;telnyx.toml&lt;/code&gt; handles API auth:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[telnyx]&lt;/span&gt;
&lt;span class="py"&gt;binding&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"TELNYX"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the agent can call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TELNYX&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createCompletion&lt;/span&gt;&lt;span class="p"&gt;(...)&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TELNYX&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(...)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No API key is hardcoded in the application code.&lt;/p&gt;

&lt;h2&gt;
  
  
  The main routes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;POST /webhooks/sms&lt;/code&gt; receives Telnyx &lt;code&gt;message.received&lt;/code&gt; webhooks&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;POST /debug/message&lt;/code&gt; simulates an inbound SMS for testing&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /debug/state&lt;/code&gt; inspects an actor state for a sender&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /health/liveness&lt;/code&gt; and &lt;code&gt;GET /health/readiness&lt;/code&gt; are health checks&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try the debug route
&lt;/h2&gt;

&lt;p&gt;After deploying, you can simulate an inbound SMS:&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 https://sms-support-agent-&amp;lt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;.telnyxcompute.com/debug/message &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;'{
    "from":"+15551230000",
    "to":"+15559870000",
    "text":"How do I send an SMS?"
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Response:&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="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"queued"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"from"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"+15551230000"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"to"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"+15559870000"&lt;/span&gt;&lt;span class="w"&gt;
&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;For real SMS, set your Telnyx Messaging Profile 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://sms-support-agent-&amp;lt;id&amp;gt;.telnyxcompute.com/webhooks/sms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then text the Telnyx number.&lt;/p&gt;

&lt;h2&gt;
  
  
  The follow-up logic
&lt;/h2&gt;

&lt;p&gt;The follow-up task runs 24 hours later.&lt;/p&gt;

&lt;p&gt;It checks the last message in the durable conversation history. If the last message is still from the assistant, the customer has not replied since the AI answer, so the agent sends:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Did that solve your problem? Reply yes or no, or ask for a human.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the customer already replied, it skips the nudge.&lt;/p&gt;

&lt;p&gt;That is the piece I like. The agent is not just a stateless webhook. It has memory and scheduled work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Production notes
&lt;/h2&gt;

&lt;p&gt;Before shipping this pattern in a real support workflow, I would add:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;webhook signature verification&lt;/li&gt;
&lt;li&gt;opt-out handling for SMS&lt;/li&gt;
&lt;li&gt;idempotent outbound sends&lt;/li&gt;
&lt;li&gt;rate limiting per phone number&lt;/li&gt;
&lt;li&gt;human handoff rules&lt;/li&gt;
&lt;li&gt;message retention policy&lt;/li&gt;
&lt;li&gt;monitoring for queued and scheduled tasks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But as a starter pattern, this is a clean way to build an AI support agent that can answer, remember, and follow up.&lt;/p&gt;

&lt;p&gt;Resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code: &lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/sms-support-agent-with-followup" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/telnyx-code-examples/tree/main/sms-support-agent-with-followup&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Agent SDK docs: &lt;a href="https://developers.telnyx.com/docs/agent-sdk" rel="noopener noreferrer"&gt;https://developers.telnyx.com/docs/agent-sdk&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Agent SDK quickstart: &lt;a href="https://developers.telnyx.com/docs/agent-sdk/quickstart" rel="noopener noreferrer"&gt;https://developers.telnyx.com/docs/agent-sdk/quickstart&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Edge Compute docs: &lt;a href="https://developers.telnyx.com/docs/edge-compute" rel="noopener noreferrer"&gt;https://developers.telnyx.com/docs/edge-compute&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 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 AI skills and toolkits: &lt;a href="https://github.com/team-telnyx/ai" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/ai&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>sms</category>
      <category>edge</category>
      <category>telnyx</category>
    </item>
    <item>
      <title>A/B Test AI Prompts at the Edge with Telnyx Stateful Actors</title>
      <dc:creator>Sonam</dc:creator>
      <pubDate>Tue, 04 Aug 2026 23:40:32 +0000</pubDate>
      <link>https://dev.to/sonam_50a41a4ced7e6b4f3fa/ab-test-ai-prompts-at-the-edge-with-telnyx-stateful-actors-5a3</link>
      <guid>https://dev.to/sonam_50a41a4ced7e6b4f3fa/ab-test-ai-prompts-at-the-edge-with-telnyx-stateful-actors-5a3</guid>
      <description>&lt;p&gt;Changing a prompt is easy. Knowing whether the new prompt is actually better is the hard part.&lt;/p&gt;

&lt;p&gt;This example builds a small prompt A/B testing API on Telnyx Edge Compute. You send one task and two prompt variants. The app runs both variants through Telnyx AI Inference, stores the experiment in a Stateful Actor, and lets users vote on which result they prefer.&lt;/p&gt;

&lt;p&gt;Code: &lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/edge-prompt-ab-tester" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/telnyx-code-examples/tree/main/edge-prompt-ab-tester&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;p&gt;The app gives you a tiny evaluation loop:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;create an experiment&lt;/li&gt;
&lt;li&gt;compare two model responses&lt;/li&gt;
&lt;li&gt;vote for variant A or B&lt;/li&gt;
&lt;li&gt;close the experiment&lt;/li&gt;
&lt;li&gt;check aggregate stats&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Routes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;POST /experiments&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;POST /experiments/&amp;lt;id&amp;gt;/vote&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;POST /experiments/&amp;lt;id&amp;gt;/close&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GET /experiments&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GET /experiments/&amp;lt;id&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GET /stats&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GET /health/liveness&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GET /health/readiness&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Create a prompt experiment
&lt;/h2&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 https://edge-prompt-ab-tester-&amp;lt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;.telnyxcompute.com/experiments &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;'{
    "task": "Write a one-sentence tagline for an edge compute platform.",
    "variant_a": "You are a concise marketing copywriter. Return just the tagline.",
    "variant_b": "You are a technical explainer. Return just the tagline, focus on latency."
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Response:&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="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"exp-msf712rs-0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"task"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Write a one-sentence tagline for an edge compute platform."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"variant_a"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"prompt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"You are a concise marketing copywriter. Return just the tagline."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"response"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Real-time compute, right where your data lives."&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"variant_b"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"prompt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"You are a technical explainer. Return just the tagline, focus on latency."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"response"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Execute code milliseconds from your users to eliminate network latency."&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"votes_a"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"votes_b"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"open"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"created_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-08-04T21:51:55Z"&lt;/span&gt;&lt;span class="w"&gt;
&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;h2&gt;
  
  
  Vote on a result
&lt;/h2&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 https://edge-prompt-ab-tester-&amp;lt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;.telnyxcompute.com/experiments/exp-msf712rs-0/vote &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;'{"variant":"a"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Close the experiment:&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 https://edge-prompt-ab-tester-&amp;lt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;.telnyxcompute.com/experiments/exp-msf712rs-0/close
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check stats:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://edge-prompt-ab-tester-&amp;lt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;.telnyxcompute.com/stats
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example:&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="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"total_experiments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"open_experiments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"closed_experiments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"total_votes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"leader"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"variant_a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"leader_votes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="w"&gt;
&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;h2&gt;
  
  
  The AI call
&lt;/h2&gt;

&lt;p&gt;The app uses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST /v2/ai/chat/completions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Default model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;zai-org/GLM-5.2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each prompt variant becomes the system message. The task becomes the user message. Both variants run against the same input, which makes the comparison easier to reason about.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Stateful Actors?
&lt;/h2&gt;

&lt;p&gt;Prompt tests need a little bit of memory.&lt;/p&gt;

&lt;p&gt;This example uses an &lt;code&gt;ABTester&lt;/code&gt; Stateful Actor to store:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;experiments&lt;/li&gt;
&lt;li&gt;prompt variants&lt;/li&gt;
&lt;li&gt;model responses&lt;/li&gt;
&lt;li&gt;vote counts&lt;/li&gt;
&lt;li&gt;open or closed status&lt;/li&gt;
&lt;li&gt;aggregate stats&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That means the API can remember experiment state without adding a separate database just for the demo.&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/edge-prompt-ab-tester
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Store your API key as an Edge Compute secret:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;telnyx-edge auth api-key &lt;span class="nb"&gt;set&lt;/span&gt; &amp;lt;YOUR_API_KEY&amp;gt;
telnyx-edge secrets add TELNYX_API_KEY &lt;span class="s2"&gt;"KEY0123..."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install and deploy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install
&lt;/span&gt;telnyx-edge ship
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then use the deployed URL to create experiments and collect votes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Production notes
&lt;/h2&gt;

&lt;p&gt;Before using this pattern for real product decisions, I would add:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;authentication&lt;/li&gt;
&lt;li&gt;evaluator identity&lt;/li&gt;
&lt;li&gt;duplicate vote prevention&lt;/li&gt;
&lt;li&gt;randomized or blinded variants&lt;/li&gt;
&lt;li&gt;fixed evaluation sets&lt;/li&gt;
&lt;li&gt;scoring rubrics&lt;/li&gt;
&lt;li&gt;prompt version history&lt;/li&gt;
&lt;li&gt;privacy controls for sensitive tasks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But even in this small form, it is useful. Prompt changes should not be judged only by vibes. They should leave a trail of examples, votes, and outcomes.&lt;/p&gt;

&lt;p&gt;Resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code: &lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/edge-prompt-ab-tester" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/telnyx-code-examples/tree/main/edge-prompt-ab-tester&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Telnyx Edge Compute docs: &lt;a href="https://developers.telnyx.com/docs/edge-compute" rel="noopener noreferrer"&gt;https://developers.telnyx.com/docs/edge-compute&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 AI skills and toolkits: &lt;a href="https://github.com/team-telnyx/ai" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/ai&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>ai</category>
      <category>edge</category>
      <category>typescript</category>
      <category>telnyx</category>
    </item>
    <item>
      <title>Build an Edge Crop Advisory API with Telnyx AI Inference</title>
      <dc:creator>Sonam</dc:creator>
      <pubDate>Tue, 04 Aug 2026 20:50:37 +0000</pubDate>
      <link>https://dev.to/sonam_50a41a4ced7e6b4f3fa/build-an-edge-crop-advisory-api-with-telnyx-ai-inference-15lo</link>
      <guid>https://dev.to/sonam_50a41a4ced7e6b4f3fa/build-an-edge-crop-advisory-api-with-telnyx-ai-inference-15lo</guid>
      <description>&lt;p&gt;I built this as the next step after the Edge URL Summarizer example. The URL summarizer showed a simple pattern: run an AI workflow at the edge, fetch external content when needed, summarize it, and keep useful state close to the request. This crop advisory example takes that same pattern into a more real-world workflow.&lt;/p&gt;

&lt;p&gt;Instead of only summarizing a page, the app accepts a farmer’s field note or an agriculture advisory URL, classifies the crop issue, returns structured JSON, stores the advisory in a Stateful Actor, tracks issue/severity stats, and flags critical cases for escalation.&lt;/p&gt;

&lt;p&gt;So the goal is not “AI gives farming advice” in a vacuum. The goal is to show how an edge-hosted AI app can turn messy field input into something operational: triage, history, stats, and a clear handoff path when a human expert needs to step in.&lt;br&gt;
Code: &lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/edge-agri-crop-advisory" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/telnyx-code-examples/tree/main/edge-agri-crop-advisory&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;p&gt;You send the app a crop issue description:&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 https://edge-agri-crop-advisory-&amp;lt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;.telnyxcompute.com/advisory &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;'{"description":"Tomato leaves have holes all over them. I can see green caterpillars on the underside of the leaves."}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It returns a structured advisory:&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="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"adv-msf1zxyc-0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"farmer_description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Tomato leaves have holes..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"crop_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tomato"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"issue_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pest"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"severity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"medium"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"confidence"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.76&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"recommendation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Inspect the underside of leaves and remove visible caterpillars. Consider an appropriate biological or labeled treatment if damage continues."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"escalate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"generated_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-08-04T19:31:04Z"&lt;/span&gt;&lt;span class="w"&gt;
&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;If the case is critical, it gets flagged:&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="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"severity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"critical"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"escalate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"escalated_to"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"agronomist-on-call"&lt;/span&gt;&lt;span class="w"&gt;
&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;h2&gt;
  
  
  Routes
&lt;/h2&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;POST /advisory&lt;/code&gt; - create an advisory&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /advisories&lt;/code&gt; - list recent advisories&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /advisories/&amp;lt;id&amp;gt;&lt;/code&gt; - get one advisory&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /stats&lt;/code&gt; - see issue/severity/escalation stats&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /health/liveness&lt;/code&gt; - liveness check&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /health/readiness&lt;/code&gt; - readiness check&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;POST /advisory&lt;/code&gt; accepts either:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;description&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;url&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you pass a URL, the app fetches the page, strips HTML, and uses that text as input.&lt;/p&gt;

&lt;h2&gt;
  
  
  The AI call
&lt;/h2&gt;

&lt;p&gt;The app calls:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST /v2/ai/chat/completions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The current code path uses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;zai-org/GLM-5.2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The prompt asks the model to return JSON only with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;crop_type&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;issue_type&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;severity&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;confidence&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;recommendation&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Issue types are constrained to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;disease | pest | nutrient | water | weather | unknown
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Severity is constrained to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;low | medium | high | critical
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That structure makes the response easier to store, display, route, and review.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Stateful Actor part
&lt;/h2&gt;

&lt;p&gt;The app uses a &lt;code&gt;CropAdvisory&lt;/code&gt; Stateful Actor.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;advisories by ID&lt;/li&gt;
&lt;li&gt;total advisory count&lt;/li&gt;
&lt;li&gt;counts by issue type&lt;/li&gt;
&lt;li&gt;counts by severity&lt;/li&gt;
&lt;li&gt;escalation count&lt;/li&gt;
&lt;li&gt;recent crop types&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So after a few requests, you can inspect the accumulated stats:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://edge-agri-crop-advisory-&amp;lt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;.telnyxcompute.com/stats
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Run it
&lt;/h2&gt;

&lt;p&gt;Clone the repo:&lt;br&gt;
&lt;/p&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/edge-agri-crop-advisory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set your Telnyx API key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;telnyx-edge auth api-key &lt;span class="nb"&gt;set&lt;/span&gt; &amp;lt;YOUR_API_KEY&amp;gt;
telnyx-edge secrets add TELNYX_API_KEY &lt;span class="s2"&gt;"KEY0123..."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install and deploy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install
&lt;/span&gt;telnyx-edge ship
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Health check:&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;-sS&lt;/span&gt; &lt;span class="nt"&gt;--retry&lt;/span&gt; 30 &lt;span class="nt"&gt;--retry-delay&lt;/span&gt; 5 &lt;span class="se"&gt;\&lt;/span&gt;
  https://edge-agri-crop-advisory-&amp;lt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;.telnyxcompute.com/health/liveness
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create an advisory:&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 https://edge-agri-crop-advisory-&amp;lt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;.telnyxcompute.com/advisory &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;'{"description":"My corn has yellow streaks on bottom leaves with dark brown spots. About 30% of plants affected."}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Production notes
&lt;/h2&gt;

&lt;p&gt;This is a triage example, not a replacement for an agronomist.&lt;/p&gt;

&lt;p&gt;Before using this pattern in production, I would add:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;authentication&lt;/li&gt;
&lt;li&gt;farmer/account identity&lt;/li&gt;
&lt;li&gt;region and crop stage&lt;/li&gt;
&lt;li&gt;photo input&lt;/li&gt;
&lt;li&gt;weather data&lt;/li&gt;
&lt;li&gt;human review for high and critical cases&lt;/li&gt;
&lt;li&gt;localized treatment guidance&lt;/li&gt;
&lt;li&gt;safety warnings&lt;/li&gt;
&lt;li&gt;SMS or webhook escalation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Still, the pattern is useful: edge function for intake, AI model for structured classification, Stateful Actor for advisory history and stats.&lt;/p&gt;

&lt;p&gt;Resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code: &lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/edge-agri-crop-advisory" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/telnyx-code-examples/tree/main/edge-agri-crop-advisory&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Stateful Actors Quick Start: &lt;a href="https://developers.telnyx.com/docs/edge-compute/stateful-actors/quick-start" rel="noopener noreferrer"&gt;https://developers.telnyx.com/docs/edge-compute/stateful-actors/quick-start&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;Chat Completions API: &lt;a href="https://developers.telnyx.com/api/inference/chat-completions" rel="noopener noreferrer"&gt;https://developers.telnyx.com/api/inference/chat-completions&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Telnyx AI skills and toolkits: &lt;a href="https://github.com/team-telnyx/ai" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/ai&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>edge</category>
      <category>typescript</category>
      <category>telnyx</category>
    </item>
    <item>
      <title>Build a URL Summarizer at the Edge with Telnyx AI Inference</title>
      <dc:creator>Sonam</dc:creator>
      <pubDate>Tue, 04 Aug 2026 19:06:39 +0000</pubDate>
      <link>https://dev.to/sonam_50a41a4ced7e6b4f3fa/build-a-url-summarizer-at-the-edge-with-telnyx-ai-inference-41e</link>
      <guid>https://dev.to/sonam_50a41a4ced7e6b4f3fa/build-a-url-summarizer-at-the-edge-with-telnyx-ai-inference-41e</guid>
      <description>&lt;p&gt;I have been playing with a pattern that feels useful for small AI tools:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;accept a request&lt;/li&gt;
&lt;li&gt;call an AI model&lt;/li&gt;
&lt;li&gt;cache the result close to where the request runs&lt;/li&gt;
&lt;li&gt;avoid building extra infrastructure until you actually need it&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This example applies that pattern to URL summarization using Telnyx Edge Compute Stateful Actors.&lt;/p&gt;

&lt;p&gt;Code: &lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/edge-url-summarizer" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/telnyx-code-examples/tree/main/edge-url-summarizer&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What it builds
&lt;/h2&gt;

&lt;p&gt;The app is a Node.js / TypeScript Edge Compute example.&lt;/p&gt;

&lt;p&gt;You send it a URL:&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 https://edge-url-summarizer-&amp;lt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;.telnyxcompute.com/summarize &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;'{"url":"https://telnyx.com/blog"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the first request, it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;fetches the page&lt;/li&gt;
&lt;li&gt;extracts text from the HTML&lt;/li&gt;
&lt;li&gt;sends the text to Telnyx AI Inference&lt;/li&gt;
&lt;li&gt;asks for exactly three bullet points&lt;/li&gt;
&lt;li&gt;stores the result in Stateful Actor storage&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On repeat requests for the same URL, it returns the cached summary.&lt;/p&gt;

&lt;h2&gt;
  
  
  API routes
&lt;/h2&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;POST /summarize&lt;/code&gt; - summarize a URL&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /summarize/cached?url=...&lt;/code&gt; - read a cached summary&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;POST /summarize/refresh&lt;/code&gt; - invalidate a cached URL&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /stats&lt;/code&gt; - view cache hit/miss stats&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /cached&lt;/code&gt; - list cached URLs&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /health/liveness&lt;/code&gt; - liveness check&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /health/readiness&lt;/code&gt; - readiness check&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Example response
&lt;/h2&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="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://example.com/article"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Example Article"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"bullets"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"The article explains the main problem."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"The implementation uses an edge function and AI model."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"Cached results make repeat requests faster."&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"word_count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1234&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"generated_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-08-04T12:00:00Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"cached"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
&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;Call the same URL again and the response comes back with:&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="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"cached"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&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;h2&gt;
  
  
  The Telnyx AI call
&lt;/h2&gt;

&lt;p&gt;The app calls:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST /v2/ai/chat/completions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The current sample uses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;zai-org/GLM-5.2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model is prompted to return JSON only:&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="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"bullets"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"point 1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"point 2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"point 3"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&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;That keeps the app response easy to consume from a dashboard, workflow, or internal tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Stateful Actor part
&lt;/h2&gt;

&lt;p&gt;The cache lives in a Telnyx Stateful Actor.&lt;/p&gt;

&lt;p&gt;The actor stores:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;summaries by URL&lt;/li&gt;
&lt;li&gt;cache hits&lt;/li&gt;
&lt;li&gt;cache misses&lt;/li&gt;
&lt;li&gt;total requests&lt;/li&gt;
&lt;li&gt;unique URL count&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That means the app can remember previous summaries without adding Redis or a database just for the demo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run it
&lt;/h2&gt;

&lt;p&gt;Clone the repo:&lt;br&gt;
&lt;/p&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/edge-url-summarizer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set your Telnyx API key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;telnyx-edge auth api-key &lt;span class="nb"&gt;set&lt;/span&gt; &amp;lt;YOUR_API_KEY&amp;gt;
telnyx-edge secrets add TELNYX_API_KEY &lt;span class="s2"&gt;"KEY0123..."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install and deploy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install
&lt;/span&gt;telnyx-edge ship
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Test the deployment:&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;-sS&lt;/span&gt; &lt;span class="nt"&gt;--retry&lt;/span&gt; 30 &lt;span class="nt"&gt;--retry-delay&lt;/span&gt; 5 &lt;span class="se"&gt;\&lt;/span&gt;
  https://edge-url-summarizer-&amp;lt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;.telnyxcompute.com/health/liveness
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Summarize a page:&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 https://edge-url-summarizer-&amp;lt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;.telnyxcompute.com/summarize &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;'{"url":"https://telnyx.com/blog"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check cache stats:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://edge-url-summarizer-&amp;lt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;.telnyxcompute.com/stats
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What I would add before production
&lt;/h2&gt;

&lt;p&gt;For a production version, I would add:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;authentication&lt;/li&gt;
&lt;li&gt;rate limiting&lt;/li&gt;
&lt;li&gt;URL allowlists or SSRF protections&lt;/li&gt;
&lt;li&gt;URL normalization&lt;/li&gt;
&lt;li&gt;TTL-based cache expiration&lt;/li&gt;
&lt;li&gt;better HTML extraction&lt;/li&gt;
&lt;li&gt;retries and observability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But as a runnable example, this is a clean way to see Edge Compute, Stateful Actors, and Telnyx AI Inference working together.&lt;/p&gt;

&lt;p&gt;Resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code: &lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/edge-url-summarizer" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/telnyx-code-examples/tree/main/edge-url-summarizer&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Stateful Actors Quick Start: &lt;a href="https://developers.telnyx.com/docs/edge-compute/stateful-actors/quick-start" rel="noopener noreferrer"&gt;https://developers.telnyx.com/docs/edge-compute/stateful-actors/quick-start&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;Chat Completions API: &lt;a href="https://developers.telnyx.com/api/inference/chat-completions" rel="noopener noreferrer"&gt;https://developers.telnyx.com/api/inference/chat-completions&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Telnyx AI skills and toolkits: &lt;a href="https://github.com/team-telnyx/ai" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/ai&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>edge</category>
      <category>typescript</category>
      <category>telnyx</category>
    </item>
    <item>
      <title>Route Voicemails with Python and Telnyx AI</title>
      <dc:creator>Sonam</dc:creator>
      <pubDate>Thu, 30 Jul 2026 18:18:05 +0000</pubDate>
      <link>https://dev.to/sonam_50a41a4ced7e6b4f3fa/route-voicemails-with-python-and-telnyx-ai-39n1</link>
      <guid>https://dev.to/sonam_50a41a4ced7e6b4f3fa/route-voicemails-with-python-and-telnyx-ai-39n1</guid>
      <description>&lt;p&gt;I built a small Flask example that turns voicemail into a routing workflow.&lt;/p&gt;

&lt;p&gt;Code:&lt;/p&gt;

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

&lt;p&gt;The app accepts either:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a voicemail transcript&lt;/li&gt;
&lt;li&gt;an uploaded voicemail audio file&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For audio, it transcribes the voicemail first. Then it uses Telnyx AI Inference to classify the message and decide where it should go.&lt;/p&gt;

&lt;h2&gt;
  
  
  Categories
&lt;/h2&gt;

&lt;p&gt;The app classifies voicemails into:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;urgent&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;billing&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;support&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sales&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;spam&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;routine&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each category maps to a route:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;urgent  -&amp;gt; Slack alert
billing -&amp;gt; email
support -&amp;gt; ticket queue
sales   -&amp;gt; CRM lead
spam    -&amp;gt; blocklist + archive
routine -&amp;gt; daily digest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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/voicemail-smart-router-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;Configure &lt;code&gt;.env&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TELNYX_API_KEY=your_telnyx_api_key
AI_MODEL=zai-org/GLM-5.2
FALLBACK_MODEL=meta-llama/Llama-3.3-70B-Instruct
HOST=127.0.0.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Optional Slack webhook for urgent messages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SLACK_WEBHOOK=https://hooks.slack.com/...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Classify a transcript
&lt;/h2&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/voicemails/transcript &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;'{
    "transcript": "This is an emergency. Our production system is down and we need help immediately.",
    "caller_number": "+17177247292"
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example response:&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="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"category"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"urgent"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"confidence"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"high"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reason"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The caller reports a production system outage requiring immediate attention."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"suggested_action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Escalate immediately to the on-call engineering team."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"route"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"slack"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"routed_to"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#oncall-alerts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"routing_status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"delivered"&lt;/span&gt;&lt;span class="w"&gt;
&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;h2&gt;
  
  
  Process voicemail audio
&lt;/h2&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/voicemails/process &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-F&lt;/span&gt; &lt;span class="s2"&gt;"file=@voicemail.wav"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-F&lt;/span&gt; &lt;span class="s2"&gt;"caller_number=+17177247292"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For audio, the app calls:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST /v2/ai/audio/transcriptions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;distil-whisper/distil-large-v2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then it calls:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST /v2/ai/chat/completions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to classify the transcript.&lt;/p&gt;

&lt;h2&gt;
  
  
  Routes included
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;POST /voicemails/transcript&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;POST /voicemails/process&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GET /voicemails&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GET /voicemails/&amp;lt;id&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GET /routes&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GET /health&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Production ideas
&lt;/h2&gt;

&lt;p&gt;This sample uses in-memory storage and a simple routing map.&lt;/p&gt;

&lt;p&gt;For production, I would add:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;auth&lt;/li&gt;
&lt;li&gt;durable storage&lt;/li&gt;
&lt;li&gt;webhook ingestion from real voicemail events&lt;/li&gt;
&lt;li&gt;retries for failed delivery&lt;/li&gt;
&lt;li&gt;real email, ticket, and CRM integrations&lt;/li&gt;
&lt;li&gt;review queues for low-confidence messages&lt;/li&gt;
&lt;li&gt;audit logs&lt;/li&gt;
&lt;li&gt;configurable routing rules&lt;/li&gt;
&lt;li&gt;PII handling before storing transcripts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The useful pattern is not just classification. It is classification plus action. The model turns a voicemail into structured intent, and the application routes it to the right workflow.&lt;/p&gt;

&lt;p&gt;Resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code: &lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/voicemail-smart-router-python" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/telnyx-code-examples/tree/main/voicemail-smart-router-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;Audio Transcriptions API: &lt;a href="https://developers.telnyx.com/api/inference/create-transcription" rel="noopener noreferrer"&gt;https://developers.telnyx.com/api/inference/create-transcription&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Chat Completions API: &lt;a href="https://developers.telnyx.com/api/inference/chat-completions" rel="noopener noreferrer"&gt;https://developers.telnyx.com/api/inference/chat-completions&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Telnyx AI skills and toolkits: &lt;a href="https://github.com/team-telnyx/ai" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/ai&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>python</category>
      <category>ai</category>
      <category>flask</category>
      <category>automation</category>
    </item>
  </channel>
</rss>
