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
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" };
}
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;
Then it downloads the audio recording through the Telnyx binding:
const audioBuffer = await telnyx.calls.downloadRecording({
call_control_id: callControlId,
recording_id: recordingId,
});
From there, the recording becomes text:
const transcription = await telnyx.ai.stt.transcribe({
audio: audioBuffer,
language: "en-US",
});
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,
});
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}`,
});
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}`,
});
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",
});
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
Install dependencies:
npm install
Create your environment file:
cp .env.example .env
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
Then typecheck, test, and deploy:
npx tsc --noEmit
npm test
npm run deploy
After deployment, point your Call Control voicemail webhook at the deployed Edge URL.
Resources
- Code example: https://github.com/team-telnyx/telnyx-code-examples/tree/main/voicemail-to-sms-agent
- Telnyx AI toolkit: https://github.com/team-telnyx/ai
- Telnyx Call Control docs: https://developers.telnyx.com/docs/voice/programmable-voice/call-control-overview
- Telnyx AI Inference docs: https://developers.telnyx.com/docs/inference
- Telnyx Messaging docs: https://developers.telnyx.com/docs/messaging
- Telnyx Portal: https://portal.telnyx.com
Top comments (0)