Most support bots can answer a message.
That is useful, but it is not quite the same thing as support.
Real support has memory. It knows what the customer asked. It knows what answer was sent. It knows whether the conversation is still unresolved. And if a customer goes quiet after getting help, a good support workflow can check back in without someone manually setting a reminder.
That is the pattern this example demonstrates.
sms-support-agent-with-followup is a TypeScript example that runs on Telnyx Edge Compute with the Telnyx Agent SDK. It receives inbound SMS messages, answers with Telnyx AI Inference, sends the reply through Telnyx Messaging, stores the conversation per phone number, and schedules a follow-up check-in 24 hours later.
Code: https://github.com/team-telnyx/telnyx-code-examples/tree/main/sms-support-agent-with-followup
What we are building
The app is an SMS support agent.
A customer texts a Telnyx number with a support question. Telnyx sends a message.received webhook to the Edge Compute function. The function routes that message to a SupportAgent actor instance keyed by the sender's phone number.
From there, the agent owns the support turn:
Inbound SMS
-> Edge Compute webhook
-> SupportAgent.receive()
-> durable message history
-> queued AI turn
-> Telnyx AI Inference
-> outbound SMS reply
-> scheduled 24h follow-up
The customer experiences this as a normal SMS conversation. The developer gets a durable agent behind the scenes.
Why the Agent SDK matters here
The interesting part is not that the app calls an LLM. A lot of examples do that.
The interesting part is that the support workflow has lifecycle primitives built in.
The SupportAgent extends the Agent SDK Agent class. That gives the app:
- durable message history with
this.messages.add() - OpenAI-shaped conversation export with
this.messages.toOpenAI() - queued background work with
this.queue() - scheduled follow-up work with
this.schedule() - per-conversation state with
this.setState()andthis.getState() - zero-credential access to Telnyx APIs through
this.env.TELNYX
That combination is what turns a simple webhook into an agent-shaped application.
The webhook does not need to hold the request open while the model runs. It records the incoming message, queues the processing step, and returns quickly. The AI turn runs in the background, then sends the SMS response.
For messaging apps, that shape matters. Webhooks should acknowledge quickly. Long-running model calls should not make webhook delivery fragile.
No API key in the application code
The sample uses the [telnyx] binding in telnyx.toml:
[telnyx]
binding = "TELNYX"
Inside the agent, that exposes a pre-authenticated Telnyx client:
await this.env.TELNYX.ai.openai.chat.createCompletion(...)
await this.env.TELNYX.messages.send(...)
So the same Edge Compute function can call Telnyx AI Inference and Telnyx Messaging without hardcoding an API key into the app.
That is a small detail with a big operational payoff. The agent code focuses on the support workflow, while credentials stay in the platform binding.
The support turn
When an SMS comes in, the front-door handler extracts the sender, recipient, and message text from the Telnyx webhook payload.
Then it routes the message to a per-phone-number actor:
await env.SUPPORT.idFromName(actorName(from)).receive({
text,
from,
to,
});
Inside receive(), the agent stores the message and queues the AI turn:
await this.setState({ from, to });
await this.messages.add("user", text);
await this.queue("process");
The process() method reads the conversation history, calls Telnyx AI Inference, stores the assistant reply, sends the SMS, and schedules a follow-up:
const history = await this.messages.toOpenAI();
const completion = await this.env.TELNYX.ai.openai.chat.createCompletion({
model: "zai-org/GLM-5.2",
messages: [{ role: "system", content: SYSTEM_PROMPT }, ...history],
max_tokens: 1000,
temperature: 0.7,
});
After the reply is sent, the agent schedules:
await this.schedule(86400, "followup", null, {
id: `followup-${state.from}`,
});
That creates the 24-hour check-in.
The follow-up
The follow-up behavior is intentionally simple.
When the scheduled task fires, the agent checks the last message in the conversation. If the last message is still from the assistant, that means the customer has not replied since the support answer.
In that case, the agent sends:
Did that solve your problem? Reply yes or no, or ask for a human.
If the customer already replied, the follow-up is skipped.
This is a tiny pattern, but it is useful. A lot of support workflows are not one message long. The actual experience is closer to:
- customer asks a question
- agent answers
- customer gets busy
- workflow checks back later
- unresolved issues can be escalated
The Agent SDK gives you the primitives to model that lifecycle without adding a separate worker queue, scheduler, conversation database, and messaging integration just to get the first version working.
Testing it
The app includes a debug route so you can simulate an inbound SMS before wiring up a real messaging profile:
curl -X POST https://sms-support-agent-<id>.telnyxcompute.com/debug/message \
-H "Content-Type: application/json" \
-d '{
"from":"+15551230000",
"to":"+15559870000",
"text":"How do I send an SMS?"
}'
The response confirms the message was queued:
{
"action": "queued",
"from": "+15551230000",
"to": "+15559870000"
}
For real SMS, point your Telnyx Messaging Profile webhook to:
https://sms-support-agent-<id>.telnyxcompute.com/webhooks/sms
Then text the Telnyx number.
Where this pattern goes next
This sample is intentionally small, but the architecture is production-shaped.
The same pattern can grow into:
- customer support triage
- order status agents
- appointment reminders
- billing support
- onboarding assistants
- renewal check-ins
- human handoff workflows
Before using it in production, I would add webhook signature verification, opt-out handling, idempotent outbound sends, rate limits, human escalation rules, and clear retention policies for message history.
But the core idea is already here.
Your support agent should not just answer.
It should remember, continue work in the background, and know when to check back.