<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: anusha</title>
    <description>The latest articles on DEV Community by anusha (@botoclock).</description>
    <link>https://dev.to/botoclock</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4004284%2Fc7c15289-9b22-479c-b291-b944235c5687.jpg</url>
      <title>DEV Community: anusha</title>
      <link>https://dev.to/botoclock</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/botoclock"/>
    <language>en</language>
    <item>
      <title>I Built a Long SMS Demo That Keeps the Message Together</title>
      <dc:creator>anusha</dc:creator>
      <pubDate>Fri, 30 Oct 2026 22:11:07 +0000</pubDate>
      <link>https://dev.to/botoclock/i-built-a-long-sms-demo-that-keeps-the-message-together-59kp</link>
      <guid>https://dev.to/botoclock/i-built-a-long-sms-demo-that-keeps-the-message-together-59kp</guid>
      <description>&lt;p&gt;An appointment reminder looks simple until you write the complete version.&lt;/p&gt;

&lt;p&gt;The time, address, check-in instructions, preparation checklist, accessibility details, and rescheduling instructions quickly become longer than one SMS segment. The obvious fallback is to send several separate texts. That works mechanically, but it makes the notification feel fragmented and gives the application several independent deliveries to reason about.&lt;/p&gt;

&lt;p&gt;I built a small &lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/long-sms-chunking-and-tracking-nodejs" rel="noopener noreferrer"&gt;Node.js Telnyx example&lt;/a&gt; to explore the other option.&lt;/p&gt;

&lt;p&gt;I recorded a short demo of the flow here: &lt;a href="https://youtu.be/6R8UYNNKABI" rel="noopener noreferrer"&gt;https://youtu.be/6R8UYNNKABI&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The application sends one logical long message. Telnyx handles the carrier-level chunking, and the application keeps a parent record that can collect the statuses of the child message segments.&lt;/p&gt;

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

&lt;p&gt;The server is an Express application using the Telnyx Node.js SDK. Its main send path is intentionally small:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;validate the destination number&lt;/li&gt;
&lt;li&gt;require a message longer than 1,530 characters for the long-message demo&lt;/li&gt;
&lt;li&gt;call the Telnyx Messages API once&lt;/li&gt;
&lt;li&gt;store the returned parent message ID&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The webhook path handles the delivery side. It verifies the signed request, reads the child message ID and parent message ID, and updates the delivery record.&lt;/p&gt;

&lt;p&gt;That parent-child relationship is the part I would reuse in a real application. An appointment system, logistics platform, or incident-notification service can keep one workflow ID while still retaining the underlying messaging events.&lt;/p&gt;

&lt;h2&gt;
  
  
  The demo has two honest modes
&lt;/h2&gt;

&lt;p&gt;The long-message mode demonstrates automatic chunking. The phone may render the resulting segments as one combined message, depending on the carrier and device.&lt;/p&gt;

&lt;p&gt;The recording uses a separate sequence mode. It sends four medium-length appointment messages so each paragraph appears as a distinct, readable bubble. Those are four independent SMS sends, not one carrier-chunked message.&lt;/p&gt;

&lt;p&gt;Keeping those modes separate makes the example easier to understand. One shows infrastructure behavior; the other shows a practical notification design that may be better for a human recipient.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where I would use it
&lt;/h2&gt;

&lt;p&gt;This pattern fits appointment reminders, delivery instructions, outage notices, customer-support updates, billing notifications, and field-service dispatches. The decision is simple: if the content is one indivisible notification, use the long-message path; if each update should stand on its own, send a sequence.&lt;/p&gt;

&lt;p&gt;The complete code and API reference are in the &lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/long-sms-chunking-and-tracking-nodejs" rel="noopener noreferrer"&gt;Telnyx code examples repository&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>telnyx</category>
      <category>ai</category>
      <category>devrel</category>
    </item>
    <item>
      <title>I Built a Bot That Attends Meetings I Can't Make, and I Control It by Text</title>
      <dc:creator>anusha</dc:creator>
      <pubDate>Fri, 30 Oct 2026 19:03:04 +0000</pubDate>
      <link>https://dev.to/botoclock/i-built-a-bot-that-attends-meetings-i-cant-make-and-i-control-it-by-text-152n</link>
      <guid>https://dev.to/botoclock/i-built-a-bot-that-attends-meetings-i-cant-make-and-i-control-it-by-text-152n</guid>
      <description>&lt;p&gt;Every calendar has a collision in it somewhere. Two meetings at the same time, a dentist appointment that ran long, a train with no signal for the first ten minutes of a standup.&lt;/p&gt;

&lt;p&gt;The usual fix is an apology message and a request for notes afterward. I wanted something better: text a link, and a bot goes in my place.&lt;/p&gt;

&lt;p&gt;I built a Node.js Telnyx code example that does exactly that. You send a meeting link to a Telnyx number. A bot joins the meeting, posts a note in the chat explaining who it is, takes notes, and texts you a TL;DR when the meeting ends.&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/join-meeting-by-sms-nodejs" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/telnyx-code-examples/tree/main/join-meeting-by-sms-nodejs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The video walkthrough:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://youtu.be/cQMwlgGuU5E" rel="noopener noreferrer"&gt;https://youtu.be/cQMwlgGuU5E&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;The interaction is a single text:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;I can't join this meeting: https://meet.google.com/abc-defg-hij.
Join as my bot, and send me a TL;DR when it ends.
~~~

A moment later a reply arrives:

~~~plaintext
On it. Your bot is joining now. I will text you when it is in
and send a TL;DR after the meeting.
~~~

In the meeting, a new participant appears with the bot's name and posts a chat message telling everyone it is taking notes on someone's behalf. When the meeting ends, a second text arrives with the summary.

The phone is the interface because it is the one thing you have when you cannot get to a laptop.

## What The Example Uses

The example runs as a small Express server and uses two Telnyx products with one API key:

- Telnyx Messaging to receive the inbound text and send the replies
- Telnyx Meeting Bot to join the call, capture the transcript, and generate a summary
- Telnyx webhooks with Ed25519 signatures to authenticate inbound messages

There is no separate transcription service and no separate summarization step in the code. The session is created with `summarize_on_end: true`, and Telnyx produces a summary artifact when the meeting is over.

## Keeping It Safe

A bot you can summon by text is also a bot someone else might try to summon, so the example has two guards.

First, the webhook verifies the Telnyx signature over the raw request body. A request that does not carry a valid signature gets a `401` and nothing else happens.

Second, the handler only acts on texts from one configured number, the owner's mobile. Messages from anyone else are ignored.

~~~js
if (from !== OWNER_PHONE_NUMBER) {
  return;
}
~~~

That is enough for a personal tool. It is not an access-control system, and a team version would need real per-user rules.

## Sending The Bot In

The core of the example is one API call:

~~~js
await client.meetingSessions.create({
  meeting_url: meetingUrl,
  bot_name: BOT_NAME,
  chat_on_enter: CHAT_ON_ENTER,
  summarize_on_end: true,
  idempotency_key: `join-meeting-by-sms:${messageId}`,
});
~~~

The `idempotency_key` solves a quiet problem. Telnyx retries webhooks that do not get a quick success response, and a retry must not put a second bot in the meeting. Because the key comes from the inbound message ID, a replayed message resolves to the session that already exists.

The `chat_on_enter` message matters for a human reason. A silent unknown participant is unsettling. A bot that introduces itself is a much better guest.

## Telling You What Is Happening

Joining a meeting is not instant. A host may need to admit the bot from a waiting room, and a host may decline. The server polls the session every few seconds and texts you on each change:

~~~json
waiting_for_admission -&amp;gt; a host needs to admit your bot
active                -&amp;gt; your bot is in and taking notes
admission_denied      -&amp;gt; the host did not admit your bot
failed                -&amp;gt; your bot could not join
~~~

Each status is texted once, so you are not flooded while waiting.

## Getting The TL;DR

When the session ends, the server asks for the session's artifacts and picks out the one with type `summary`. Summary generation is asynchronous, so the server waits for it to complete, up to ninety seconds, then texts it to you.

If the summary is not available, you get a clear fallback message. A silent failure would be worse than an honest one.

## Optional Extras

Two environment variables make the bot feel more like a participant:

- `ASSISTANT_ID` attaches a Telnyx AI Assistant so the bot can speak and answer questions in the meeting
- `CAMERA_IMAGE_URL` sets a static image as the bot's camera tile

## Running The Example

~~~bash
git clone https://github.com/team-telnyx/telnyx-code-examples.git
cd telnyx-code-examples/join-meeting-by-sms-nodejs
cp .env.example .env
npm install
npm start
~~~

Add your API key, public key, Telnyx number, and your own mobile number to `.env`. Expose the server with a tunnel, point your Messaging Profile's inbound webhook at `/webhooks/sms`, and text your number a link.

This puts a real bot in a real meeting and sends real SMS, so try it on a meeting you control first.

## What I Would Change For Production

This is a pattern, not a product. Before using it for real I would:

- Store watched session IDs in a database so a restart does not drop the status texts
- Always disclose the bot in chat, because people deserve to know when a meeting is being summarized
- Register the sending number for US SMS with 10DLC or toll-free verification
- Replace the single owner number with per-user rules
- Plan for meeting platforms where chat is not supported

## Frequently Asked Questions

### Which meeting platforms work?

The example recognizes Google Meet, Zoom, Microsoft Teams, and Webex links.

### Can the bot talk?

Yes, if you attach a Telnyx AI Assistant with `ASSISTANT_ID`.

### What if the host never admits the bot?

You get a text saying the bot is at the waiting room, and another if the host denies admission.

### Can someone else text the bot into a meeting?

No. Only the configured owner number is accepted, and webhooks without a valid Telnyx signature are rejected.

## First comment

Demo video: https://youtu.be/cQMwlgGuU5E

Code: https://github.com/team-telnyx/telnyx-code-examples/tree/main/join-meeting-by-sms-nodejs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>telnyx</category>
      <category>ai</category>
      <category>devrel</category>
    </item>
    <item>
      <title>I Built a Voice Fraud Step-Up Workflow with Telnyx</title>
      <dc:creator>anusha</dc:creator>
      <pubDate>Thu, 29 Oct 2026 20:47:54 +0000</pubDate>
      <link>https://dev.to/botoclock/i-built-a-voice-fraud-step-up-workflow-with-telnyx-ikn</link>
      <guid>https://dev.to/botoclock/i-built-a-voice-fraud-step-up-workflow-with-telnyx-ikn</guid>
      <description>&lt;p&gt;A caller can know the right account details, tell a convincing story, and sound exactly like the person they are pretending to be.&lt;/p&gt;

&lt;p&gt;That makes voice a useful interaction channel, but a weak standalone identity factor for sensitive actions.&lt;/p&gt;

&lt;p&gt;I built a TypeScript Telnyx code example that treats voice authenticity as one risk signal in a larger fraud workflow. The example protects a high-risk support request, such as a wire transfer, and chooses between proceeding, requiring a flashcall step-up, or routing the request to manual review.&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/voice-fraud-step-up-agent" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/telnyx-code-examples/tree/main/voice-fraud-step-up-agent&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The video walkthrough:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://youtu.be/DQ3RLClemqM" rel="noopener noreferrer"&gt;https://youtu.be/DQ3RLClemqM&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Imagine a customer calls a support line and asks to send $2,500 to an external account.&lt;/p&gt;

&lt;p&gt;A simple voice workflow might ask the caller a question, accept a positive answer, and continue. That is not enough for a high-risk action. A fraudster can use a cloned voice, and a human-sounding voice still does not prove account ownership.&lt;/p&gt;

&lt;p&gt;The safer workflow starts by opening a risk gate:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;support request
  -&amp;gt; blocked pending
  -&amp;gt; voice-authenticity check
  -&amp;gt; policy decision
  -&amp;gt; proceed, step up, or human review
~~~

The gate is intentionally separate from the final business action. It determines what verification should happen next. It does not claim that the caller has been fully authenticated.

That separation also makes the demo easier to reason about. The caller experiences a normal phone interaction, while the application keeps the security decision in structured state. Support teams can see whether the request passed the voice screen, required a challenge, or needs a person to review it.

## What The Example Uses

The sample runs on Telnyx Edge Compute and uses:

- Telnyx Call Control to place the outbound call
- Telnyx Deepfake Detection to classify the remote-party audio
- Telnyx webhooks to deliver the result
- Embedded SQL storage for the per-account audit ledger
- Telnyx Verify flashcall as an optional step-up
- Call Control speech for deterministic prompts and verdicts

The live path does not start an autonomous AI Assistant after detection. That choice is deliberate. The policy result should come from code and stored state, not from a model improvising a message to the caller.

## Opening The Risk Gate

The actor receives a sensitive action such as a wire transfer request and records the initial state:

~~~plaintext
blocked_pending — request opened
~~~

That state is intentional. The request does not begin in an approved state while the phone call is still waiting for a result.

## Enabling Deepfake Detection

The outbound Call Control request includes Deepfake Detection:

~~~ts
deepfake_detection: {
  enabled: true,
  timeout: 30,
}
~~~

The request also carries client state containing the account ID. When the webhook arrives, the Edge function decodes that state and routes the result to the correct account actor.

The call continues while the detection service evaluates the remote-party audio. The caller hears a short verification prompt through Call Control speech:

~~~plaintext
This call includes a voice-authenticity screen, not identity verification.
Please state your name and confirm whether you requested the two thousand five
hundred dollar wire transfer on this account.
~~~

The wording is intentionally explicit. The caller is told that the call is checking voice authenticity, not proving account ownership.

## Applying The Result

Telnyx sends a result such as:

~~~plaintext
real
fake
silence_timeout
~~~

The policy combines that result with recent ledger history.

A clean real result returns:

~~~plaintext
proceed
~~~

That means the voice-authenticity policy passed and the workflow can continue to an independent identity factor.

A fake result returns a flashcall step-up when the Verify profile is configured. A recent risky ledger event can also force the step-up even if the latest audio appears human.

A silence_timeout or provider error returns manual review. The workflow fails closed when the signal is missing or inconclusive.

The resulting ledger is easy to inspect:

~~~plaintext
blocked_pending — request opened
proceed         — human-sounding voice result and clean recent ledger
~~~

The word proceed is not the same as identity verified. It means the policy has allowed the next stage.

## Why The Ledger Matters

A fraud decision should be explainable after the call ends.

The actor stores request and outcome rows in embedded SQL. That provides a durable audit trail for questions such as:

- Which account opened the request?
- Which call produced the result?
- Was the result real, fake, or inconclusive?
- Was there a recent risky event?
- Did the workflow proceed, step up, or go to review?

This also gives the application memory across restarts. A later request can see that the account had a recent risky event and require another factor instead of evaluating the newest call in isolation.

## Deterministic Call Messages

The sample originally used an assistant to deliver the post-detection result. The live path now uses Call Control speech instead.

That is a small but important reliability decision. The code chooses the exact message from the policy outcome:

~~~plaintext
proceed
  -&amp;gt; the voice passed the synthetic-voice screen

step_up_flashcall
  -&amp;gt; additional verification is required

manual_review
  -&amp;gt; a specialist will review the request
~~~

The caller hears the same outcome that appears in the ledger. There is no second autonomous decision layer between the policy and the message.

## Running The Example

Start with local validation:

~~~bash
git clone https://github.com/team-telnyx/telnyx-code-examples.git
cd telnyx-code-examples/voice-fraud-step-up-agent
npm install
cp .env.example .env
npm run typecheck
npm run smoke
~~~

The smoke test exercises the policy locally and does not place a call or send a verification message.

For a live run, configure the Edge secrets and target a phone you control:

~~~bash
npm run demo:live
~~~

The local monitor prints the Call Control payload with the target number masked. The API key remains in the deployed Edge function.

## Production Boundaries

This sample is a policy pattern, not a complete identity system.

A production implementation should:

- Add an independent identity factor before completing a sensitive action
- Limit repeated attempts and step-up retries
- Retain ledger data according to the organization’s security policy
- Avoid putting secrets or full phone numbers in logs
- Route uncertain detection results to trained reviewers
- Add account-level velocity and transaction-risk signals
- Keep the voice-authenticity result separate from authorization

The most important boundary is simple: a human-sounding voice is not proof of account ownership.

## Frequently Asked Questions

### Does a real Deepfake Detection result verify the caller?

No. It means the audio appeared human to the detector. The application still needs an independent identity factor.

### What happens when the result is fake?

The policy can trigger a Telnyx Verify flashcall step-up when a Verify profile is configured.

### What happens when detection times out?

The sample fails closed to manual review instead of allowing the sensitive action.

### Why use a durable Edge actor?

The actor gives each account a place to store its pending action, recent risk history, and audit ledger so the next request can use that context.

### Does the live call use an AI Assistant?

No. The live path uses deterministic Call Control speech for the prompt and final verdict.

## Resources

- Code example: https://github.com/team-telnyx/telnyx-code-examples/tree/main/voice-fraud-step-up-agent
- Video walkthrough: https://youtu.be/DQ3RLClemqM
- Deepfake Detection: https://telnyx.com/products/deepfake-detection
- Verify API: https://telnyx.com/products/verify-api
- Stateful Edge Functions: https://telnyx.com/resources/stateful-edge-functions
- Embedded SQL for Edge Functions: https://telnyx.com/resources/serverless-sql-database-edge-functions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>telnyx</category>
      <category>ai</category>
      <category>devrel</category>
    </item>
    <item>
      <title>The AI Recruiter That Never Forgets a Candidate</title>
      <dc:creator>anusha</dc:creator>
      <pubDate>Fri, 16 Oct 2026 21:47:14 +0000</pubDate>
      <link>https://dev.to/botoclock/the-ai-recruiter-that-never-forgets-a-candidate-4b4n</link>
      <guid>https://dev.to/botoclock/the-ai-recruiter-that-never-forgets-a-candidate-4b4n</guid>
      <description>&lt;p&gt;The call dropped. Mid-interview.&lt;/p&gt;

&lt;p&gt;A candidate had just answered the second question of a five-question phone screen. Then the connection died. On most call workflows, that's a restart: the next call starts at question one, the candidate repeats themselves, and the notes from call one are gone.&lt;/p&gt;

&lt;p&gt;That moment is why I built this.&lt;/p&gt;

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

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

&lt;h2&gt;
  
  
  The Screen That Remembers
&lt;/h2&gt;

&lt;p&gt;This is a candidate screening call agent. You press one button, and Telnyx calls the candidate. An AI Assistant runs the interview — five questions, one at a time. As the candidate finishes each answer, it gets scored against a role rubric and written into a durable record.&lt;/p&gt;

&lt;p&gt;The interesting part is where that record lives.&lt;/p&gt;

&lt;p&gt;Most call workflows keep their state inside the call. Call ends, context ends. This sample does the opposite: every candidate gets their own durable actor — a small piece of code with its own memory that stays alive on Telnyx Edge Compute. One per phone number. It holds the candidate's profile, their answer ledger, their scores, how many times the call was retried, and the full event history.&lt;/p&gt;

&lt;p&gt;Calls become just one way to talk to that actor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why That Matters More Than It Sounds
&lt;/h2&gt;

&lt;p&gt;When the candidate's history lives in a durable actor instead of a call, the resume-after-drop isn't a feature you build. It falls out of the architecture.&lt;/p&gt;

&lt;p&gt;Here's the mechanism. Every outbound call carries its own little instruction sheet — Telnyx calls these dynamic variables. Our dial payload sends two things: the five questions to ask, and the list of questions already answered. The assistant reads that and skips straight to the next unanswered question.&lt;/p&gt;

&lt;p&gt;There's no resume flow in the code. There's no session state in the call. I just call the candidate again, and because their actor still exists, the call continues where it left off. When I demo this, it's the moment people lean in: hang up mid-call, press the same button, and the AI says "continuing where we left off" and asks question three.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the Assistant Actually Is
&lt;/h2&gt;

&lt;p&gt;Telnyx hosts the entire conversation — speech-to-text, inference, text-to-speech, turn-taking, voicemail detection. I configured it once: an Ultra voice with expressive mode, patient turn-taking so it never interrupts a candidate mid-sentence, and premium voicemail detection. If nobody picks up, the assistant leaves a short callback message and stops. It doesn't talk at a voicemail greeting for five minutes.&lt;/p&gt;

&lt;p&gt;The outbound call goes through TeXML — Telnyx's voice layer that places and manages the call itself. The call carries the assistant's ID and those dynamic variables, and the assistant does the rest.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Scoring Is Honest On Purpose
&lt;/h2&gt;

&lt;p&gt;Every answer gets scored. The rubric has five questions, each with ideal signals — things a strong answer would contain. The scorer combines signal matches with substance: an answer needs real content to pass, a vague one routes to human review, an empty one fails.&lt;/p&gt;

&lt;p&gt;That's deliberate. An AI phone screen that passes everything is useless — the recruiter can't trust it. In production, the same answers route through Telnyx Decision Models for AI-graded scoring with the same output shape. The rule-based scorer is the honest floor you can audit.&lt;/p&gt;

&lt;p&gt;The recruiter sees a live transcript and a scorecard as the call happens. Average score, per-question verdicts, and a recommended next step: advance, review, or human review.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd Build Next
&lt;/h2&gt;

&lt;p&gt;Three things, and the architecture is already shaped for them.&lt;/p&gt;

&lt;p&gt;First, text-back. Since the actor is the candidate's identity, an inbound SMS can land on the same actor. The candidate confirms their interview by text, and the system already knows who they are and what was said.&lt;/p&gt;

&lt;p&gt;Second, the ATS hook. Opening a screen is already an API route. When a candidate applies, the ATS fires it — the call happens with zero recruiter effort.&lt;/p&gt;

&lt;p&gt;Third, calibrating the scoring. The rubric signals are the demo's honesty floor; Decision Models make the grading adaptive without changing the recruiter's output.&lt;/p&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/candidate-screening-call-agent
npm &lt;span class="nb"&gt;install
cp&lt;/span&gt; .env.example .env   &lt;span class="c"&gt;# four values: API key, TeXML app, caller id, your phone&lt;/span&gt;
npm run dashboard
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Press one button. Telnyx calls you. You are the candidate. Answer like one.&lt;/p&gt;

&lt;p&gt;The full walkthrough video: &lt;a href="https://youtu.be/a03KfWduNWA" rel="noopener noreferrer"&gt;https://youtu.be/a03KfWduNWA&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bigger Pattern
&lt;/h2&gt;

&lt;p&gt;The hiring screen is the demo, but the shape generalizes: an outbound call, a structured conversation, and a durable per-person actor that remembers everything between calls.&lt;/p&gt;

&lt;p&gt;Patient intake. Lead qualification. Insurance verification. Appointment confirmation. Anywhere a call might drop, a person might need to be called twice, and their history should follow them — that's where this architecture belongs.&lt;/p&gt;

&lt;p&gt;The phone is the channel people actually answer. Durable state is what makes it feel human.&lt;/p&gt;

</description>
      <category>telnyx</category>
      <category>ai</category>
      <category>devrel</category>
    </item>
    <item>
      <title>I Built a Durable Insurance Verification Caller with Telnyx Edge</title>
      <dc:creator>anusha</dc:creator>
      <pubDate>Fri, 09 Oct 2026 23:31:07 +0000</pubDate>
      <link>https://dev.to/botoclock/i-built-a-durable-insurance-verification-caller-with-telnyx-edge-187j</link>
      <guid>https://dev.to/botoclock/i-built-a-durable-insurance-verification-caller-with-telnyx-edge-187j</guid>
      <description>&lt;p&gt;Insurance verification is one of those workflows that looks simple from the outside and messy from the inside.&lt;/p&gt;

&lt;p&gt;A patient is scheduled. The clinic needs to know whether the patient's coverage is active. Someone calls the carrier, waits through the IVR, enters a member ID, listens to a spoken eligibility result, and turns that result into a front desk note.&lt;/p&gt;

&lt;p&gt;That process is repetitive, but it is not trivial. Phone calls fail. IVRs loop. Voicemail happens. Spoken answers are inconsistent. And if the result is missed, the cost can land on the clinic or the patient.&lt;/p&gt;

&lt;p&gt;I built a Telnyx code example that models the workflow as a durable voice actor.&lt;/p&gt;

&lt;p&gt;The code example:&lt;br&gt;
&lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/insurance-verification-caller" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/telnyx-code-examples/tree/main/insurance-verification-caller&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  The Flow
&lt;/h2&gt;

&lt;p&gt;A front desk system opens a verification job with a member ID, plan, and provider.&lt;/p&gt;

&lt;p&gt;From there, a Telnyx Edge actor owns the job. It dials the carrier IVR, sends DTMF digits to move through the menu, captures the spoken eligibility response, asks Jev Decision Models to score the transcript, and sends a concise SMS verdict to the front desk.&lt;/p&gt;

&lt;p&gt;The actor also handles the unglamorous parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;voicemail&lt;/li&gt;
&lt;li&gt;dropped calls&lt;/li&gt;
&lt;li&gt;long holds&lt;/li&gt;
&lt;li&gt;retry backoff&lt;/li&gt;
&lt;li&gt;attempt logging&lt;/li&gt;
&lt;li&gt;duplicate SMS prevention&lt;/li&gt;
&lt;li&gt;pod restarts in the middle of the workflow&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is the reason I like this sample. It treats the phone call as a stateful job, not a fragile one-off request.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why Telnyx Fits This Pattern
&lt;/h2&gt;

&lt;p&gt;The example uses Telnyx for the communication primitives:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Voice and Call Control to place the outbound call&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;send_dtmf&lt;/code&gt; to drive the carrier IVR&lt;/li&gt;
&lt;li&gt;transcription to capture the spoken answer&lt;/li&gt;
&lt;li&gt;Jev Decision Models to structure the carrier response&lt;/li&gt;
&lt;li&gt;Messaging to notify the front desk&lt;/li&gt;
&lt;li&gt;Telnyx Edge Agent SDK for durable actors, scheduled retries, and state&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The important move is putting those pieces inside an actor. The actor is the verification job. It knows whether it has already tried once, whether it has a transcript, what verdict came back, and whether the SMS has already been sent.&lt;/p&gt;

&lt;p&gt;That means the app can survive normal telecom messiness without losing the thread.&lt;/p&gt;
&lt;h2&gt;
  
  
  What the Decision Model Does
&lt;/h2&gt;

&lt;p&gt;Carrier IVR language is not guaranteed to be tidy. One system might say:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The member is active and eligible for coverage.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another might say:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;We are unable to verify eligibility at this time.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The sample sends the transcript to Jev Decision Models and asks for a structured result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;choice&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;covered | not_covered | needs_verification&lt;/span&gt;
&lt;span class="na"&gt;score&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0-100 confidence&lt;/span&gt;
&lt;span class="na"&gt;noul&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0-1 confidence this is a hard no-coverage result&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The app then turns that into a front desk notification:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Coverage confirmed&lt;/li&gt;
&lt;li&gt;Not covered, verify before visit&lt;/li&gt;
&lt;li&gt;Needs verification&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is deliberately not a clinical agent. It does not diagnose, approve care, deny care, or replace billing staff. It automates a phone-based eligibility check and routes uncertain cases back to humans.&lt;/p&gt;

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



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/team-telnyx/telnyx-code-examples.git
&lt;span class="nb"&gt;cd &lt;/span&gt;telnyx-code-examples/insurance-verification-caller
npm &lt;span class="nb"&gt;install
cp&lt;/span&gt; .env.example .env
npx tsx smoke_test.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The sample defaults to demo mode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DEMO_MODE=true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That lets you inspect the actor flow without placing real calls or sending live SMS messages.&lt;/p&gt;

&lt;p&gt;For live mode, configure the Telnyx API key, carrier number, outbound caller ID, front desk SMS destination, 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 ship
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;The production version should connect the verification result to the systems staff already use: a PMS, EHR, billing queue, or prior authorization workflow.&lt;/p&gt;

&lt;p&gt;I would also add payer-specific IVR profiles, richer webhook routing, audit views for the attempt ledger, and operational dashboards for retry rates. If one payer's IVR starts failing every morning, that should be visible to staff.&lt;/p&gt;

&lt;p&gt;The core boundary should stay the same: the system handles the communications loop and produces a structured eligibility signal. Humans still own the final administrative and patient-facing decisions.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Code: &lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/insurance-verification-caller" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/telnyx-code-examples/tree/main/insurance-verification-caller&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Call Control dial: &lt;a href="https://developers.telnyx.com/api-reference/call-commands/dial" rel="noopener noreferrer"&gt;https://developers.telnyx.com/api-reference/call-commands/dial&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Send DTMF: &lt;a href="https://developers.telnyx.com/api-reference/call-commands/send-dtmf" rel="noopener noreferrer"&gt;https://developers.telnyx.com/api-reference/call-commands/send-dtmf&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;In-call transcription: &lt;a href="https://developers.telnyx.com/docs/voice/stt/in-call-transcription" rel="noopener noreferrer"&gt;https://developers.telnyx.com/docs/voice/stt/in-call-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 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 Edge docs: &lt;a href="https://developers.telnyx.com/docs/edge" rel="noopener noreferrer"&gt;https://developers.telnyx.com/docs/edge&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>telnyx</category>
      <category>ai</category>
      <category>devrel</category>
    </item>
    <item>
      <title>The Front Desk Voice Agent Should Know When To Stop Being the Front Desk</title>
      <dc:creator>anusha</dc:creator>
      <pubDate>Thu, 08 Oct 2026 17:58:26 +0000</pubDate>
      <link>https://dev.to/botoclock/the-front-desk-voice-agent-should-know-when-to-stop-being-the-front-desk-1n8e</link>
      <guid>https://dev.to/botoclock/the-front-desk-voice-agent-should-know-when-to-stop-being-the-front-desk-1n8e</guid>
      <description>&lt;p&gt;A clinic phone line is not one workflow.&lt;/p&gt;

&lt;p&gt;It is hours and directions. It is billing. It is intake. It is after-hours messages. Sometimes it is a patient saying something that should reach a human immediately.&lt;/p&gt;

&lt;p&gt;That is why I like the &lt;code&gt;clinic-triage-handoff&lt;/code&gt; example. It is not "AI answers the phone" as a party trick. It is a more useful shape: AI handles the routine path, but the system has a deterministic escape hatch when the call becomes urgent.&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/clinic-triage-handoff" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/telnyx-code-examples/tree/main/clinic-triage-handoff&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Pattern
&lt;/h2&gt;

&lt;p&gt;The example uses one Telnyx Edge function as the routing brain.&lt;/p&gt;

&lt;p&gt;It answers inbound clinic calls, records who called, injects routing history, and starts the correct AI assistant persona. There are three assistant roles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;front desk&lt;/li&gt;
&lt;li&gt;billing desk&lt;/li&gt;
&lt;li&gt;clinical desk&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Routine calls move between those personas with context. The caller does not have to start over every time the call moves.&lt;/p&gt;

&lt;p&gt;The interesting part is urgent escalation. If the assistant identifies an urgent symptom, it does not try to solve the problem in the prompt. It calls a webhook tool. From there, code takes over:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;send an SMS alert to the on-call nurse&lt;/li&gt;
&lt;li&gt;dial the nurse&lt;/li&gt;
&lt;li&gt;speak a briefing&lt;/li&gt;
&lt;li&gt;bridge the patient into the live call&lt;/li&gt;
&lt;li&gt;write the event to a durable log&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The AI recognizes the handoff. The system performs the handoff.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Feels Different From a Phone Tree
&lt;/h2&gt;

&lt;p&gt;Phone trees are usually stateless. Each transfer is a reset.&lt;/p&gt;

&lt;p&gt;This sample keeps a durable per-caller routing log. The next call can include context like "this caller last asked about billing" or "this caller had an escalation." The care-team view is rendered from the same log, so the demo can show the operational trail instead of only the voice interaction.&lt;/p&gt;

&lt;p&gt;That matters because front desk work is not only the call. It is the memory of the call, the handoff, and the proof that something happened.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why The Urgent Path Belongs in Code
&lt;/h2&gt;

&lt;p&gt;In healthcare-adjacent demos, the boundary matters.&lt;/p&gt;

&lt;p&gt;The assistant should not diagnose. It should not make clinical decisions. It should not decide how to improvise an emergency procedure.&lt;/p&gt;

&lt;p&gt;In this sample, the urgent path is a code path. The assistant's job is to invoke the escalation tool. The Edge function's job is to execute the workflow every time in the same order.&lt;/p&gt;

&lt;p&gt;That gives you something you can test:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Did the tool endpoint receive the escalation?&lt;/li&gt;
&lt;li&gt;Did the SMS send?&lt;/li&gt;
&lt;li&gt;Did the nurse dial start?&lt;/li&gt;
&lt;li&gt;Did the briefing play?&lt;/li&gt;
&lt;li&gt;Did the bridge happen?&lt;/li&gt;
&lt;li&gt;Did the log update?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is the difference between "the assistant is instructed to escalate" and "the system escalates."&lt;/p&gt;

&lt;h2&gt;
  
  
  The Useful Architecture
&lt;/h2&gt;

&lt;p&gt;The pieces are simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Telnyx Call Control for the call lifecycle&lt;/li&gt;
&lt;li&gt;Telnyx AI Assistants for the phone personas&lt;/li&gt;
&lt;li&gt;Telnyx Edge Compute for routing and state&lt;/li&gt;
&lt;li&gt;Telnyx Messaging for the nurse alert&lt;/li&gt;
&lt;li&gt;durable SQL storage for the routing log&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The result is a workflow where voice, messaging, AI, and compute live close together. You do not need a separate queue, webhook relay, and database just to prove the handoff.&lt;/p&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/clinic-triage-handoff
npm &lt;span class="nb"&gt;install
cp&lt;/span&gt; .env.example .env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create the three assistants, configure the Edge secrets, connect the clinic numbers, and call the front desk line. Test one routine transfer and one urgent escalation. Then open the care-team view and look at the log.&lt;/p&gt;

&lt;p&gt;That is the whole point of the sample: the voice demo is visible, but the handoff record is the product.&lt;/p&gt;

</description>
      <category>telnyx</category>
      <category>ai</category>
      <category>devrel</category>
    </item>
    <item>
      <title>Setup Email API: A Local Dashboard for Your First Tracked Send</title>
      <dc:creator>anusha</dc:creator>
      <pubDate>Thu, 24 Sep 2026 19:56:49 +0000</pubDate>
      <link>https://dev.to/botoclock/setup-email-api-a-local-dashboard-for-your-first-tracked-send-182o</link>
      <guid>https://dev.to/botoclock/setup-email-api-a-local-dashboard-for-your-first-tracked-send-182o</guid>
      <description>&lt;p&gt;I wanted a demo that made email feel less like a black box.&lt;/p&gt;

&lt;p&gt;Most "send your first email" examples stop at the moment the API returns &lt;code&gt;queued&lt;/code&gt;. That is useful, but it is not the whole developer experience. If you are building onboarding emails, receipts, password resets, billing alerts, or lifecycle notifications, the real question is what happens after the send.&lt;/p&gt;

&lt;p&gt;Was it delivered? Did the recipient open it? Did they click? Did it bounce?&lt;/p&gt;

&lt;p&gt;So I built a tiny local dashboard for the Telnyx Email API. It sends one email, polls the event feed, and turns the response into delivery, open, click, bounce, and unsubscribe rates.&lt;/p&gt;

&lt;p&gt;The full code sample is here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/setup-email-api-nodejs" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/telnyx-code-examples/tree/main/setup-email-api-nodejs&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;The sample is intentionally small:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;one Node.js server&lt;/li&gt;
&lt;li&gt;one HTML page&lt;/li&gt;
&lt;li&gt;no npm dependencies&lt;/li&gt;
&lt;li&gt;a &lt;code&gt;.env&lt;/code&gt; file for configuration&lt;/li&gt;
&lt;li&gt;a local dashboard at &lt;code&gt;http://localhost:3000&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ol&gt;
&lt;li&gt;Read &lt;code&gt;TELNYX_API_KEY&lt;/code&gt;, &lt;code&gt;FROM_EMAIL&lt;/code&gt;, and &lt;code&gt;TO_EMAIL&lt;/code&gt; from &lt;code&gt;.env&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Send one email with &lt;code&gt;POST /v2/email_messages&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Enable open and click tracking for that message.&lt;/li&gt;
&lt;li&gt;Poll message events.&lt;/li&gt;
&lt;li&gt;Render the lifecycle in a browser dashboard.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It is not meant to be a full production analytics app. It is meant to show the full first loop: send, deliver, open, click, observe.&lt;/p&gt;

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

&lt;p&gt;The core API call is &lt;code&gt;POST /v2/email_messages&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The sample sends a simple HTML email and turns on tracking for this specific message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;tracking_settings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;open_tracking&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;click_tracking&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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 detail matters. Delivery events come from the normal Email API lifecycle. Open and click events require tracking. If you omit &lt;code&gt;tracking_settings&lt;/code&gt;, the message inherits the sender domain's default tracking settings.&lt;/p&gt;

&lt;p&gt;For a first demo, per-send tracking makes the behavior easy to see: send one email, open it, click the link, and watch the dashboard update.&lt;/p&gt;

&lt;h2&gt;
  
  
  Polling events
&lt;/h2&gt;

&lt;p&gt;Once the API returns a message ID, the app polls:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET /v2/email_messages/{id}/events
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The events look like a timeline:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;queued&lt;/li&gt;
&lt;li&gt;sending&lt;/li&gt;
&lt;li&gt;sent&lt;/li&gt;
&lt;li&gt;delivered&lt;/li&gt;
&lt;li&gt;opened&lt;/li&gt;
&lt;li&gt;clicked&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The dashboard turns those events into rates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;delivery rate&lt;/li&gt;
&lt;li&gt;open rate&lt;/li&gt;
&lt;li&gt;click rate&lt;/li&gt;
&lt;li&gt;bounce rate&lt;/li&gt;
&lt;li&gt;unsubscribe rate&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a one-message demo, a delivered email gives you 100% delivery. Opening it gives you 100% open. Clicking the link gives you 100% click. That makes the lifecycle obvious without needing a large dataset.&lt;/p&gt;

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

&lt;p&gt;This pattern is useful beyond the demo. If you can send a message and track what happens next, you can build better product workflows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;onboarding sequences&lt;/li&gt;
&lt;li&gt;transactional receipts&lt;/li&gt;
&lt;li&gt;billing alerts&lt;/li&gt;
&lt;li&gt;password resets&lt;/li&gt;
&lt;li&gt;operational notifications&lt;/li&gt;
&lt;li&gt;lifecycle nudges&lt;/li&gt;
&lt;li&gt;internal delivery dashboards&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The same event feed that powers the local dashboard can also power support tooling, alerting, reporting, or customer-facing status views.&lt;/p&gt;

&lt;h2&gt;
  
  
  Safe for screen sharing
&lt;/h2&gt;

&lt;p&gt;I also wanted the sample to be safe to demo publicly.&lt;/p&gt;

&lt;p&gt;The server keeps the API key server-side. The browser only sees masked sender and recipient addresses. Local message IDs are stored in &lt;code&gt;data/sent.json&lt;/code&gt;, and that folder is ignored by git. The &lt;code&gt;.env&lt;/code&gt; file is ignored too, while &lt;code&gt;.env.example&lt;/code&gt; contains placeholders.&lt;/p&gt;

&lt;p&gt;That means you can show the app without accidentally exposing the API key or real email addresses.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try 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/setup-email-api-nodejs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create your env file:&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;Fill in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;TELNYX_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;KEY_your_telnyx_api_key_here
&lt;span class="nv"&gt;FROM_EMAIL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;sender@example.com
&lt;span class="nv"&gt;TO_EMAIL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;you@example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

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

&lt;/div&gt;



&lt;p&gt;Click &lt;strong&gt;Send test email&lt;/strong&gt;, open the email when it arrives, click the link, and watch the event log update.&lt;/p&gt;

&lt;h2&gt;
  
  
  What comes next
&lt;/h2&gt;

&lt;p&gt;This sample covers the first end-to-end tracked send. From here, the natural next steps are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;custom sending domain setup&lt;/li&gt;
&lt;li&gt;DNS verification&lt;/li&gt;
&lt;li&gt;templates&lt;/li&gt;
&lt;li&gt;scheduled sends&lt;/li&gt;
&lt;li&gt;suppression handling&lt;/li&gt;
&lt;li&gt;webhooks&lt;/li&gt;
&lt;li&gt;inbound inboxes and replies&lt;/li&gt;
&lt;li&gt;persistent storage instead of local JSON&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But the foundation is the same: send the message, keep the API key safe, and let events tell you what happened.&lt;/p&gt;

&lt;p&gt;That is the part I wanted this sample to make visible.&lt;/p&gt;

</description>
      <category>telnyx</category>
      <category>email</category>
      <category>node</category>
      <category>api</category>
    </item>
    <item>
      <title>I Gave My SIM Card a Say in My Data Plan</title>
      <dc:creator>anusha</dc:creator>
      <pubDate>Mon, 21 Sep 2026 19:07:19 +0000</pubDate>
      <link>https://dev.to/botoclock/i-gave-my-sim-card-a-say-in-my-data-plan-475j</link>
      <guid>https://dev.to/botoclock/i-gave-my-sim-card-a-say-in-my-data-plan-475j</guid>
      <description>&lt;p&gt;The text arrived at 11:47 PM on a Sunday: &lt;em&gt;"You have exceeded your monthly data allowance."&lt;/em&gt; Not a warning. A receipt. The threshold had been crossed sometime that afternoon — the network knew the exact minute — and the alert showed up nine hours later, after the overage was already on my bill.&lt;/p&gt;

&lt;p&gt;I've built enough against telecom APIs to know that latency isn't technical. The usage counters exist. The SMS pipe exists. What's missing is an &lt;em&gt;actor&lt;/em&gt;: something that lives close enough to the SIM to see usage as it happens, remembers what it promised you last cycle, and is allowed to actually do something — not just notify.&lt;/p&gt;

&lt;p&gt;So I built that actor. It's called SIMAgent, and the design constraint is right in the one-line pitch: &lt;strong&gt;the actor IS the SIM.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The most informed party in the room can't speak
&lt;/h2&gt;

&lt;p&gt;Think about who knows what in a data plan:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;SIM&lt;/strong&gt; knows its usage in real time — it's the thing being metered.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;carrier&lt;/strong&gt; knows the plan, the thresholds, and the pricing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You&lt;/strong&gt; know none of the above until someone tells you — usually too late.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The fix isn't a better dashboard. Dashboards still require you to look. The fix is giving the SIM itself a durable agent: memory (usage counters, plan, alert history), a schedule (check thresholds hourly, reset on the billing boundary), a voice (SMS, and even phone calls), and hands (the ability to provision a plan upgrade through the Wireless API when you confirm).&lt;/p&gt;

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

&lt;p&gt;SIMAgent is a TypeScript class — &lt;code&gt;SIMAgent extends Agent&lt;/code&gt; — running on the Telnyx Edge runtime, one instance per SIM. Telnyx Wireless usage webhooks stream into it and update durable state. An hourly schedule checks the numbers; at 80% of the plan it texts &lt;em&gt;you&lt;/em&gt;, not the other way around. When you text back asking whether an upgrade makes sense, it calls Telnyx Inference and answers in plain language, grounded in your real usage — not a marketing page. When you confirm, it calls the Wireless API itself and raises the data limit, then texts the confirmation. On the billing boundary it resets the counters and sends a summary. And if you'd rather talk than text, it answers the call through Call Control and speaks your usage history to you.&lt;/p&gt;

&lt;p&gt;All of it ships in demo mode by default. Every SMS, call action, and provisioning call is simulated and logged, so the first time you run it, nothing costs anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Telnyx
&lt;/h2&gt;

&lt;p&gt;I've built versions of this shape before, and the hard part was never the LLM. It was the plumbing around it: a database for state, a scheduler for the checks, a queue for the webhooks, separate API clients for messaging, voice, and SIM management, and signature verification bolted on at the end when someone remembered to ask about it.&lt;/p&gt;

&lt;p&gt;On Telnyx, the agent runtime collapses most of that. &lt;code&gt;this.getState()&lt;/code&gt; and &lt;code&gt;this.setState()&lt;/code&gt; give me durable state with no database. &lt;code&gt;this.every()&lt;/code&gt; gives me schedules that survive cold starts. &lt;code&gt;this.env.TELNYX&lt;/code&gt; exposes messaging, wireless provisioning, and inference as one surface — that's what Telnyx means by &lt;strong&gt;AI Communications Infrastructure&lt;/strong&gt;: the communications primitives and the AI primitives live in the same runtime, so one agent can perceive (webhooks), remember (durable state), decide (LLM), and act (SMS, voice, provisioning) in a single place.&lt;/p&gt;

&lt;p&gt;The trust details are handled at the platform level too. In live mode, every inbound webhook is verified with an Ed25519 signature before the agent touches its state, and every action lands in a durable event log I can replay when something looks wrong. For something that can spend your money by upgrading your plan, that audit trail isn't a nice-to-have.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo first, live second
&lt;/h2&gt;

&lt;p&gt;The whole thing is reproducible in about five minutes: clone the repo, copy the &lt;code&gt;.env&lt;/code&gt; example, &lt;code&gt;npm install&lt;/code&gt;, &lt;code&gt;npm start&lt;/code&gt;, then &lt;code&gt;npm run smoke&lt;/code&gt;. The smoke test exercises the full demo flow — usage ingest, threshold breach, alert, plan comparison, upgrade — and prints the simulated events as it goes. When you're ready for real behavior, set &lt;code&gt;DEMO_MODE=false&lt;/code&gt;, add your API key, public key, and sender number from the Telnyx Portal, and restart.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd build next
&lt;/h2&gt;

&lt;p&gt;Three things, in order: &lt;strong&gt;fleet mode&lt;/strong&gt;, where one agent class is activated per SIM across an entire deployment; &lt;strong&gt;anomaly detection&lt;/strong&gt; on the usage stream, so the agent texts you when your &lt;em&gt;pattern&lt;/em&gt; changes, not just your total; and &lt;strong&gt;voice-first escalation&lt;/strong&gt;, where the agent calls &lt;em&gt;you&lt;/em&gt; at 100% instead of waiting to be called.&lt;/p&gt;

&lt;p&gt;The broader idea is bigger than SIMs. Any network endpoint with state — a number, a trunk, a room, a device — can be a durable agent that watches its own telemetry and acts on its own behalf. The SIM was just the endpoint whose neglect I'd personally paid for.&lt;/p&gt;

&lt;p&gt;Repo is here: &lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/sim-agent" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/telnyx-code-examples/tree/main/sim-agent&lt;/a&gt; — run the smoke test and see what your SIM would say if it could talk.&lt;/p&gt;

</description>
      <category>telnyx</category>
      <category>ai</category>
      <category>devrel</category>
    </item>
    <item>
      <title>I Built a Debate Club for AI Agents — and Let the Audience Vote</title>
      <dc:creator>anusha</dc:creator>
      <pubDate>Mon, 21 Sep 2026 19:06:56 +0000</pubDate>
      <link>https://dev.to/botoclock/i-built-a-debate-club-for-ai-agents-and-let-the-audience-vote-5l7</link>
      <guid>https://dev.to/botoclock/i-built-a-debate-club-for-ai-agents-and-let-the-audience-vote-5l7</guid>
      <description>&lt;p&gt;A confession: I have never once enjoyed reading an agent transcript. You know the genre — "Here's a fascinating multi-agent conversation between Researcher and Critic!" — followed by forty lines of dialogue that were interesting to the person who built the agents and to no one else. And by the time you read it, it's over. It's a recording of an event nobody attended.&lt;/p&gt;

&lt;p&gt;So when I sat down to build a multi-agent demo, I gave myself one constraint: &lt;strong&gt;it has to feel live.&lt;/strong&gt; Not a pipeline that produces a text file. An event. Something a room of people can watch and, fittingly, argue about in real time.&lt;/p&gt;

&lt;p&gt;What I ended up with is now a public code sample: two AI agents with opposing stances debate a topic, arguments and vote tallies stream live over WebSocket, and the audience votes — with every ballot persisted to a SQL ledger that lives inside the debate room itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem with multi-agent demos
&lt;/h2&gt;

&lt;p&gt;Here's what "make it live" actually demands, and why most demos skip it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Durable shared state.&lt;/strong&gt; The debate has a phase, a current turn, a growing list of arguments. That state has to survive restarts and be consistent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A broadcast layer.&lt;/strong&gt; Every state change needs to reach every connected viewer, immediately, as a diff — not a full re-fetch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A database.&lt;/strong&gt; Audience votes need to be persisted, deduplicated, and tallied. One vote per person, re-votes overwrite.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secret management.&lt;/strong&gt; The agents call a model, which means credentials — somewhere.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each of those is an afternoon of plumbing on its own. None of them is the demo. Most multi-agent samples quietly drop the "live" part and hand you a transcript instead, because the plumbing eats the fun.&lt;/p&gt;

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

&lt;p&gt;The flow is simple to describe. You &lt;code&gt;POST /debate&lt;/code&gt; with a topic. Two &lt;code&gt;DebateAgent&lt;/code&gt; actors — one pro, one con — take turns composing arguments through model inference. A &lt;code&gt;DebateRoom&lt;/code&gt; actor orchestrates the turns and holds the canonical state. Every state change streams automatically to anyone connected to the room's WebSocket. The audience votes with a &lt;code&gt;call&lt;/code&gt; frame over that same socket (or plain HTTP, if they prefer). Votes land in an embedded SQL ledger — one row per voter. When someone hits the end endpoint, the winner is declared straight from the SQL tally and broadcast in state.&lt;/p&gt;

&lt;p&gt;The whole thing runs locally on one port. Demo mode ships with canned arguments, so streaming, voting, and tallying all work before you wire up live inference.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three things made it click
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Zero-credential inference.&lt;/strong&gt; The agents call the model through Telnyx's &lt;code&gt;[telnyx]&lt;/code&gt; binding. There is no API key in the agent code — the platform injects authentication at runtime. My agents hold no secrets, which means I can share the code, and the inference path can't leak a credential it doesn't have.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. State that streams itself.&lt;/strong&gt; This is the one I keep thinking about. The room updates its state with &lt;code&gt;setState()&lt;/code&gt;, which applies a JSON merge-patch, persists it durably, and fans it out to every connected WebSocket client. I did not write a broadcast layer. I did not write a diffing protocol. The room's connection surface &lt;em&gt;is&lt;/em&gt; the broadcast layer. The live part of the demo — the part that makes it an event instead of a transcript — fell out of the state model for free.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. A database per actor, no database server.&lt;/strong&gt; Each room actor carries its own embedded SQL store. The vote ledger is a handful of lines: create the table, upsert the vote with &lt;code&gt;ON CONFLICT&lt;/code&gt;, &lt;code&gt;SELECT&lt;/code&gt; the tally. No provisioning, no connection strings. And because the tally gets written back into agent state, the moment someone votes, every watcher's UI updates.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this is the interesting part
&lt;/h2&gt;

&lt;p&gt;Telnyx frames its platform as &lt;strong&gt;AI Communications Infrastructure&lt;/strong&gt; — a unified stack for building agents that communicate over voice, SMS, and real-time WebSockets. When I first heard that phrase, I filed it under "marketing." Building this sample changed my mind.&lt;/p&gt;

&lt;p&gt;A live debate is a communications event. The audience is connected in real time. The state is the broadcast. And the same Agent runtime that streams my debate state can carry a phone call — which means the obvious next version of this demo is a debate you can &lt;em&gt;call into and hear&lt;/em&gt;, with the same state streaming to a live audience on the web. That's not two products stitched together; it's one runtime whose primitives (durable agents, streaming state, embedded storage, zero-credential inference) happen to be the primitives a live event needs.&lt;/p&gt;

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

&lt;p&gt;The repo is &lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/multi-agent-debate" rel="noopener noreferrer"&gt;team-telnyx/telnyx-code-examples → multi-agent-debate&lt;/a&gt;. &lt;code&gt;npm install&lt;/code&gt;, &lt;code&gt;npm run build&lt;/code&gt;, &lt;code&gt;npm start&lt;/code&gt; (the local actor stack needs Docker and the Edge Compute CLI), and you're at &lt;code&gt;http://localhost:8787&lt;/code&gt;. If you want to kick the tires with nothing but Node, &lt;code&gt;npm run smoke&lt;/code&gt; runs a self-contained test with an in-process actor host.&lt;/p&gt;

&lt;p&gt;Start in demo mode. Watch the arguments stream. Vote. Change your vote. End the debate and watch the winner come out of the SQL ledger.&lt;/p&gt;

&lt;p&gt;The audience is the missing ingredient in most agent demos. Give them a way to vote, and suddenly the transcript becomes a show.&lt;/p&gt;

</description>
      <category>telnyx</category>
      <category>ai</category>
      <category>devrel</category>
    </item>
    <item>
      <title>I Built an Email Campaign That Wakes Itself Up</title>
      <dc:creator>anusha</dc:creator>
      <pubDate>Mon, 21 Sep 2026 19:05:08 +0000</pubDate>
      <link>https://dev.to/botoclock/i-built-an-email-campaign-that-wakes-itself-up-57m</link>
      <guid>https://dev.to/botoclock/i-built-an-email-campaign-that-wakes-itself-up-57m</guid>
      <description>&lt;p&gt;There's a specific kind of dread that comes with sending email in bulk. Not the sending itself — that part is easy. It's the &lt;em&gt;after&lt;/em&gt;: the moment you realize the batch API returned &lt;code&gt;207 Multi-Status&lt;/code&gt;, which is HTTP's way of saying "some of this worked, some of it didn't, good luck figuring out which is which."&lt;/p&gt;

&lt;p&gt;I've lived this enough times that when I sat down to build a sample for Telnyx, I knew exactly what I wanted to make: an email campaign that takes care of itself. Not a script I run and watch. Not a cron job I pray fires on time. A thing that exists, remembers what it's doing, and wakes itself up when it needs to.&lt;/p&gt;

&lt;p&gt;Here's how it went.&lt;/p&gt;

&lt;h2&gt;
  
  
  The scenario that made it real
&lt;/h2&gt;

&lt;p&gt;I framed the sample around a community bank sending fraud alerts to cardholders. Every notice has a deadline: the longer a customer goes without confirming or disputing a suspicious charge, the more money is at risk — and the more the bank's reputation for vigilance erodes. So when a batch of fraud alerts goes out and a third of them fail on the first attempt, "we'll retry later" is not an operations plan. It's a bet that someone is watching. In this scenario, nobody is.&lt;/p&gt;

&lt;p&gt;That's the problem I wanted to solve: not sending email — lots of things send email — but &lt;em&gt;accountability&lt;/em&gt; at the per-message level, and &lt;em&gt;recovery&lt;/em&gt; without a human in the loop.&lt;/p&gt;

&lt;h2&gt;
  
  
  The idea: the campaign is a thing, not a task
&lt;/h2&gt;

&lt;p&gt;The mental shift came from a single sentence in my notes: &lt;em&gt;the actor is the campaign.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Most batch senders are tasks. They run, they finish, they forget. If something fails, the failure lives in a log you'll grep tomorrow, and the retry lives in a cron job you'll debug next week. I flipped it: the campaign is an actor — a durable actor, specifically. A little running thing with its own memory that survives restarts and crashes.&lt;/p&gt;

&lt;p&gt;When you submit a batch, the actor is born. It owns the state of every message: sent or failed, how many attempts, what the last error was, which idempotency key it used. All of that lives in durable storage, so when the platform reboots mid-batch — and it will, eventually — the campaign picks up exactly where it left off. The reboot isn't an incident. It's a pause.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part I'm proudest of
&lt;/h2&gt;

&lt;p&gt;The retry loop. When the initial send comes back with failures, the agent doesn't spin or poll. It schedules a wake-up call with itself — 60 seconds out — and goes to sleep. When it wakes, it retries only the messages that actually failed, each with a fresh idempotency key so every attempt is individually traceable. If they fail again, it waits longer: 60 seconds, then two minutes, then four, capped at five. Messages that exhaust their attempts get marked &lt;code&gt;EXHAUSTED&lt;/code&gt; and left alone.&lt;/p&gt;

&lt;p&gt;No cron. No external scheduler. No while-loop burning CPU. The agent sleeps and wakes exactly when it says it will.&lt;/p&gt;

&lt;p&gt;And when the campaign reaches its final state, the agent texts me. An actual SMS to my phone: "Campaign campaign-2026-03: 98 sent, 0 failed, 2 exhausted." I didn't appreciate how much I wanted this until the first time it happened. The full audit trail is one URL away — &lt;code&gt;GET /campaigns/:id&lt;/code&gt; returns every message, every attempt, every key — but the SMS is the part that means nobody has to go looking.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Telnyx
&lt;/h2&gt;

&lt;p&gt;I work at Telnyx, so take this with the appropriate salt — but the reason this sample came together the way it did is that the pieces are one platform. Telnyx calls it AI Communications Infrastructure, and the name is more than marketing: the Email API handles the batch send with idempotency support and returns honest per-message results; the Edge Agent SDK gives me the durable actor runtime — persistent state, queues, self-waking schedules — as primitives instead of glue code; and the SMS API closes the loop to a human.&lt;/p&gt;

&lt;p&gt;On any other stack, those are three integrations and a state store I have to run myself. Here, the durability is the runtime. The difference shows up in the code: the entire retry engine is a &lt;code&gt;schedule()&lt;/code&gt; call and a filter over failed indices.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd tell you to steal
&lt;/h2&gt;

&lt;p&gt;Two things.&lt;/p&gt;

&lt;p&gt;First: treat &lt;code&gt;207&lt;/code&gt; as data. Parse it per message, update state per message, and never retry a batch wholesale — that's how you double-send the messages that actually succeeded.&lt;/p&gt;

&lt;p&gt;Second: make retries a property of the thing that failed, not a property of your infrastructure. When the campaign owns its own wake-up schedule, "we'll retry later" stops being a hope and becomes a guarantee with a timestamp.&lt;/p&gt;

&lt;p&gt;The full sample — code, smoke test, deploy scripts — is &lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/email-batch-retry-agent" rel="noopener noreferrer"&gt;on GitHub&lt;/a&gt;. Clone it, break it on purpose, and watch it put itself back together. That part never gets old.&lt;/p&gt;

</description>
      <category>telnyx</category>
      <category>ai</category>
      <category>devrel</category>
    </item>
    <item>
      <title>The Agent That Remembers: One StatefulActor, Four Channels, Zero Databases</title>
      <dc:creator>anusha</dc:creator>
      <pubDate>Thu, 17 Sep 2026 17:31:31 +0000</pubDate>
      <link>https://dev.to/botoclock/the-agent-that-remembers-one-statefulactor-four-channels-zero-databases-4eh5</link>
      <guid>https://dev.to/botoclock/the-agent-that-remembers-one-statefulactor-four-channels-zero-databases-4eh5</guid>
      <description>&lt;p&gt;&lt;em&gt;Building an omni-channel lab-results agent on Telnyx Edge Compute&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I built a demo last month that I keep coming back to. Not because it's flashy — although the fax-to-phone journey is fun — but because of how it's architected. One durable actor per patient. Everything in one place. No external database.&lt;/p&gt;

&lt;h2&gt;
  
  
  The scenario
&lt;/h2&gt;

&lt;p&gt;A lab faxes a patient's results to the clinic. A human reviews the PDF and accepts it. The moment they accept, the original fax is &lt;strong&gt;deleted from Telnyx storage&lt;/strong&gt; — the AI never saw it, and it never will. What survives is a reference number and a status. From there, the AI drafts every follow-up: the confirmation email, the SMS replies, the answers on the hotline. A human approves every single message before it reaches the patient.&lt;/p&gt;

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

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

&lt;h2&gt;
  
  
  The architecture decision that made it work
&lt;/h2&gt;

&lt;p&gt;The obvious design is four integrations: one for fax, one for email, one for SMS, one for voice. Four state stores, four identity maps, and an AI that knows nothing about any of them.&lt;/p&gt;

&lt;p&gt;We did it differently. &lt;strong&gt;One StatefulActor per patient&lt;/strong&gt;, on Telnyx Edge Compute, keyed by the patient's phone number. Every channel routes to that same actor:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The fax webhook stores the document on the actor&lt;/li&gt;
&lt;li&gt;The SMS thread lives in the actor's conversation pool&lt;/li&gt;
&lt;li&gt;The email tracking updates the actor's document records&lt;/li&gt;
&lt;li&gt;The hotline call attaches to the same actor before the AI says a word&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The actor carries its own embedded SQL — conversations, messages, documents, appointments. No external database. The state &lt;em&gt;is&lt;/em&gt; the service.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the agent does with that memory
&lt;/h2&gt;

&lt;p&gt;Before every AI reply, the actor injects its own state into the model's context: the appointment status, the lab documents, whether the results email went out. So when a patient calls and says "I never got my lab results," the agent doesn't hallucinate. It reads the actor: &lt;em&gt;"your results were sent to your email on Tuesday — check spam."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;And the privacy is by construction: the AI only ever sees document metadata — a reference number and a status. The actual lab values were deleted from Telnyx storage the moment a human accepted the fax. There's nothing to leak because there's nothing there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Human-in-the-loop, everywhere
&lt;/h2&gt;

&lt;p&gt;Every AI reply lands as a draft. The admin inbox shows it in amber — edit, approve, or kill. Voice drafts get spoken on the live call. Emails go out with a display name and a tracking pixel. SMS through Messaging. The agent drafts; a human decides.&lt;/p&gt;

&lt;h2&gt;
  
  
  The analytics
&lt;/h2&gt;

&lt;p&gt;The insights dashboard tracks sent, delivered, and opened — with a self-hosted tracking pixel served by the same Edge function. When the patient opens the email, the open rate moves live. No third-party tracker.&lt;/p&gt;

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

&lt;p&gt;The full journey — booking, the visit, the fax, the review, the email, the hotline call, the open-rate flip — runs in demo mode. Clone it, ship it to Edge Compute, and it works.&lt;/p&gt;

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

</description>
      <category>telnyx</category>
      <category>ai</category>
      <category>devrel</category>
    </item>
    <item>
      <title>I Built a Circuit Breaker for Phone Lines So Customers Never Hear Dead Air</title>
      <dc:creator>anusha</dc:creator>
      <pubDate>Fri, 11 Sep 2026 23:28:38 +0000</pubDate>
      <link>https://dev.to/botoclock/i-built-a-circuit-breaker-for-phone-lines-so-customers-never-hear-dead-air-5dlc</link>
      <guid>https://dev.to/botoclock/i-built-a-circuit-breaker-for-phone-lines-so-customers-never-hear-dead-air-5dlc</guid>
      <description>&lt;p&gt;I've been thinking about this problem for a while. Every company with a phone line has the same nightmare: the carrier goes down and nobody can call you. For most businesses that's annoying. For a bank, a hospital, or an emergency notification service, it's existential.&lt;/p&gt;

&lt;p&gt;The standard fix is a runbook. A human gets paged, logs into the carrier dashboard, manually reroutes traffic to a backup connection, and hopes nobody called during the gap. Fifteen to forty-five minutes of lost calls, every single time.&lt;/p&gt;

&lt;p&gt;So I built a circuit breaker for voice infrastructure. Not the software kind — the telecom-native kind.&lt;/p&gt;

&lt;p&gt;Here's the demo: &lt;a href="https://github.com/team-telnyx/telnyx-code-examples/tree/main/auto-failover-voice-routing" rel="noopener noreferrer"&gt;https://github.com/team-telnyx/telnyx-code-examples/tree/main/auto-failover-voice-routing&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Scenario
&lt;/h2&gt;

&lt;p&gt;I used a fraud alert line for a fictional bank. Here's how it works: the bank's system detects a suspicious transaction. It calls the customer. An AI voice says "We detected a purchase of $1,240.50 at an electronics retailer in Miami. Press 1 if this was you. Press 2 and we'll block your card immediately." The customer presses a button. The card is confirmed or blocked. The customer gets an SMS receipt with a case number.&lt;/p&gt;

&lt;p&gt;Now imagine the primary carrier fails during this process. Without a circuit breaker, the customer gets dead air. With one, the same call happens over the backup connection — same voice, same alert, same flow — with one additional sentence: "Heads up: we're running on our backup systems right now."&lt;/p&gt;

&lt;p&gt;That one sentence is the only difference the customer notices.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the Circuit Breaker Works
&lt;/h2&gt;

&lt;p&gt;The system has two independent Telnyx Call Control connections. A router sits in front and watches for failure signals. Every call outcome arrives as a signed webhook from the carrier. When failures accumulate — busy, no-answer, timeout — the breaker trips at a threshold and all new calls route through the backup connection.&lt;/p&gt;

&lt;p&gt;After a cooldown period, the breaker goes half-open. The next call is a live probe of the primary. If the probe connects without a failure code, traffic flows back automatically. If the probe fails, backup keeps handling calls.&lt;/p&gt;

&lt;p&gt;The key insight is that the carrier tells you about every call outcome in real time. You don't poll for health. You don't guess. The webhooks are the failure detection layer, and they're signed — so nobody can fake a failure and trick your system into switching to backup.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters More for Voice Than for HTTP
&lt;/h2&gt;

&lt;p&gt;When your API goes down, clients retry. When your phone line goes down, callers hear dead air and hang up. There's no retry. There's no queue. The call is gone.&lt;/p&gt;

&lt;p&gt;That changes the math on failover. In a microservices setup, thirty seconds of downtime means some requests get retried and eventually succeed. In voice, thirty seconds of downtime means customers who called and got nothing. They might try again. They might not.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Technical Bits
&lt;/h2&gt;

&lt;p&gt;The whole thing runs on Telnyx Edge Compute using the Agent SDK. One durable actor — &lt;code&gt;FailoverAgent&lt;/code&gt; — owns the circuit breaker state and the call flow. It uses two Call Control applications as primary and backup connections, monitors carrier webhooks for failure signals, and manages the fraud alert conversation: text-to-speech announcement, keypress collection, SMS receipt.&lt;/p&gt;

&lt;p&gt;The state is simple: a failure counter, a last-failure timestamp, and a tripped flag. The actor increments the counter on every failure webhook, trips the breaker at a threshold (default: three), and resets after a successful probe. The routing decision is one function: closed routes primary, open routes backup, half-open probes primary.&lt;/p&gt;

&lt;p&gt;What makes it interesting is the customer experience design. The backup announcement doesn't hide the outage — it says "we're running on our backup systems right now." That one sentence builds trust. The customer knows something happened, knows the bank is handling it, and gets on with their day.&lt;/p&gt;

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

&lt;p&gt;Building this taught me that failover for voice is not the same problem as failover for HTTP. The failure modes are different. The recovery is different. And the cost of getting it wrong is different — you're not losing a request, you're losing a conversation.&lt;/p&gt;

&lt;p&gt;The circuit breaker pattern has been around forever in distributed systems. Applying it to voice infrastructure, with carrier webhooks as the failure signal and two independent Call Control connections as the failover path, is the telecom-native version of a pattern every backend engineer already knows.&lt;/p&gt;

&lt;p&gt;The sample is open source and includes a demo trigger endpoint so you can test the breaker without waiting for a real carrier outage. Clone it, wire up two connections, and your phone line becomes outage-proof.&lt;/p&gt;

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