Getting an AI agent to answer a phone call is only the beginning.
The harder part is keeping the call going.
This example builds a voice agent on Telnyx Edge Compute that answers an inbound call, speaks a greeting, listens with streaming transcription, sends the caller’s speech to Telnyx AI Inference, speaks the reply with TTS, and then goes back to listening.
Code: https://github.com/team-telnyx/telnyx-code-examples/tree/main/edge-voice-agent-holds-call
The call loop
The core flow is:
call.initiated
-> answer
call.answered
-> speak greeting
call.speak.ended
-> start transcription
call.transcription final
-> stop transcription
-> run LLM turn
-> speak reply
call.speak.ended
-> listen again
call.hangup
-> persist final call state
Enter fullscreen mode Exit fullscreen mode
Each live call gets a VoiceAgent actor keyed by call_control_id.
That actor stores:
- call phase
- caller and callee
- turn count
- last transcript
- last reply
- durable message history
The main routes
-
POST /webhooks/voicereceives Call Control webhooks -
GET /debug/call?call_control_id=<call_id>inspects actor state -
POST /debug/respondtests an LLM turn without a live call -
GET /health/livenessandGET /health/readinesssupport health checks
The Agent SDK piece
The VoiceAgent extends the Agent SDK Agent class from @telnyx/edge-runtime.
It uses:
-
this.messages.add()to store caller and assistant turns -
this.messages.toOpenAI()to format history for the model -
this.setState()andthis.getState()for durable call state -
this.env.TELNYX.ai.openai.chat.createCompletion()for zero-credential inference through the[telnyx]binding
The model call looks like this:
const completion = await this.env.TELNYX.ai.openai.chat.createCompletion({
model,
messages: [{ role: "system", content: SYSTEM_PROMPT }, ...history],
max_tokens: 200,
temperature: 0.5,
});
Enter fullscreen mode Exit fullscreen mode
The webhook handler speaks the returned text through Call Control TTS.
Why streaming transcription?
The sample uses Call Control streaming transcription because the app owns the conversation loop.
The caller speaks. Telnyx sends a final transcript. The app stops transcription, runs the LLM turn, speaks the reply, and starts transcription again after TTS ends.
That gives you room to add your own logic:
- human handoff
- CRM lookup
- order status tools
- turn limits
- cost tracking
- transcript summaries
- safety rules
Deploy
npm install
telnyx-edge ship
Enter fullscreen mode Exit fullscreen mode
Set the Call Control API key as an Edge secret:
telnyx-edge secret set TELNYX_API_KEY your_telnyx_api_key
Enter fullscreen mode Exit fullscreen mode
Point your Call Control application webhook to:
https://edge-voice-agent-holds-call-<id>.telnyxcompute.com/webhooks/voice
Enter fullscreen mode Exit fullscreen mode
Then call the Telnyx number assigned to that Call Control application.
Production notes
Before shipping this pattern for real callers, I would add:
- webhook signature verification
- idempotency for Call Control actions
- max turn count and call-duration limits
- human transfer
- transcript redaction
- latency monitoring across STT, LLM, and TTS
- stronger barge-in handling
- observability around call phases
But even as a starter, this is a useful reference for building a voice agent that can actually stay on the call.
Resources:
- Code: https://github.com/team-telnyx/telnyx-code-examples/tree/main/edge-voice-agent-holds-call
- Agent SDK docs: https://developers.telnyx.com/docs/agent-sdk
- Edge Compute docs: https://developers.telnyx.com/docs/edge-compute
- Call Control API reference: https://developers.telnyx.com/api-reference/call-control
- Streaming transcription guide: https://developers.telnyx.com/docs/voice/programmable-voice/transcription
- Telnyx AI Inference docs: https://developers.telnyx.com/docs/inference
- Telnyx AI skills and toolkits: https://github.com/team-telnyx/ai
답글 남기기