DEV Community

Sonam
Sonam

Posted on

Build an Agent That Turns Voicemails Into SMS Summaries

Voicemail is one of those workflows that still feels weirdly stuck.

Someone leaves a message. You get a badge. Then you have to listen through a minute of audio just to find the ten seconds that matter: who called, what they need, whether there is a deadline, and whether you need to call back.

The voicemail-to-sms-agent example turns that into an event-driven AI workflow.

When a voicemail is left, the agent transcribes the recording, summarizes it with an LLM, sends the summary over SMS, and archives the original audio. The result is closer to voicemail triage than voicemail playback.

Code: https://github.com/team-telnyx/telnyx-code-examples/tree/main/voicemail-to-sms-agent

What the app does

This is a TypeScript example built for Telnyx Edge.

The flow is:

Caller leaves voicemail
  -> Call Control webhook fires
  -> Edge agent receives the task
  -> agent downloads the recording
  -> Speech-to-Text transcribes the audio
  -> AI Inference summarizes the transcript
  -> Messaging sends the SMS summary
  -> Cloud Storage archives the audio
Enter fullscreen mode Exit fullscreen mode

Instead of checking voicemail manually, the mailbox owner gets a concise text message with the important parts.

The trigger

The agent reacts to a Call Control webhook.

In the sample, the agent checks that the incoming task is a voicemail event before doing any work:

const payload = task.input as WebhookPayload;

if (
  !payload ||
  payload.event !== "call.status" ||
  payload.data?.payload?.call_status !== "voicemail"
) {
  return { status: "ignored" };
}
Enter fullscreen mode Exit fullscreen mode

That keeps the agent focused. It does not process every call event. It only starts the pipeline when the call actually ends in voicemail.

The pipeline

Once the voicemail event is accepted, the agent pulls the key pieces out of the webhook payload:

const callControlId = payload.data.payload.call_control_id;
const recordingId = payload.data.payload.recording?.id;
const callerNumber = payload.data.payload.from;
Enter fullscreen mode Exit fullscreen mode

Then it downloads the audio recording through the Telnyx binding:

const audioBuffer = await telnyx.calls.downloadRecording({
  call_control_id: callControlId,
  recording_id: recordingId,
});
Enter fullscreen mode Exit fullscreen mode

From there, the recording becomes text:

const transcription = await telnyx.ai.stt.transcribe({
  audio: audioBuffer,
  language: "en-US",
});
Enter fullscreen mode Exit fullscreen mode

That transcript is what the LLM sees.

Summarizing the voicemail

The summary step uses Telnyx AI through an OpenAI-compatible chat completion call:

const summaryResponse = await telnyx.ai.openai.chat.createCompletion({
  model: "gpt-4o-mini",
  messages: [
    {
      role: "system",
      content:
        "Summarize the following voicemail transcription into a concise SMS message.",
    },
    {
      role: "user",
      content: `Caller: ${callerNumber}\nTranscription: ${transcriptText}`,
    },
  ],
  max_tokens: 60,
});
Enter fullscreen mode Exit fullscreen mode

The LLM is not being asked to run the whole workflow. It has one job: take a transcript and make it useful enough to read quickly on a phone.

That is the part I like about this pattern. The workflow stays explicit, but the language-heavy step gets handled by AI.

Demo mode vs live mode

The sample includes a LIVE_MODE flag.

When LIVE_MODE=false, the agent logs the SMS it would send:

log.info("Demo mode: SMS not sent. Would send:", {
  to: destinationNumber,
  text: `Voicemail from ${callerNumber}: ${summaryText}`,
});
Enter fullscreen mode Exit fullscreen mode

When LIVE_MODE=true, it sends the message:

await telnyx.messages.send({
  from: this.env.config?.TELNYX_SMS_NUMBER,
  to: destinationNumber,
  text: `Voicemail from ${callerNumber}: ${summaryText}`,
});
Enter fullscreen mode Exit fullscreen mode

That makes the example easier to test safely. You can run the whole pipeline first, inspect the generated SMS payload, and only flip to live mode once you are ready.

Archiving the original audio

The agent also stores the voicemail recording in Telnyx Cloud Storage:

await telnyx.storage.put({
  bucket: this.env.config?.STORAGE_BUCKET || "voicemail-archives",
  key: `voicemails/${recordingId}.mp3`,
  body: audioBuffer,
  contentType: "audio/mpeg",
});
Enter fullscreen mode Exit fullscreen mode

That gives you a lightweight audit trail. The SMS summary is the fast path, but the original audio is still available if the owner needs to review it later.

Why this is useful

This example is small, but the pattern is production-shaped:

  • react to a communications event
  • pull the relevant media
  • run AI on it
  • notify someone through the right channel
  • store the source artifact

You could adapt the same shape for customer support inboxes, missed sales calls, appointment reminders, after-hours service lines, or any workflow where voice messages need to become structured follow-up.

Running it

Clone the examples repo:

git clone https://github.com/team-telnyx/telnyx-code-examples.git
cd telnyx-code-examples/voicemail-to-sms-agent
Enter fullscreen mode Exit fullscreen mode

Install dependencies:

npm install
Enter fullscreen mode Exit fullscreen mode

Create your environment file:

cp .env.example .env
Enter fullscreen mode Exit fullscreen mode

Configure values like:

TELNYX_API_KEY=<your_telnyx_api_key>
TELNYX_SMS_NUMBER=<your_telnyx_sms_number>
MAILBOX_OWNER_NUMBER=<summary_recipient_number>
STORAGE_BUCKET=voicemail-archives
LIVE_MODE=false
Enter fullscreen mode Exit fullscreen mode

Then typecheck, test, and deploy:

npx tsc --noEmit
npm test
npm run deploy
Enter fullscreen mode Exit fullscreen mode

After deployment, point your Call Control voicemail webhook at the deployed Edge URL.

Resources

Top comments (0)