# Build a Voice Agent That Holds a Live Call on Telnyx Edge Compute

> Source: <https://dev.to/sonam_50a41a4ced7e6b4f3fa/build-a-voice-agent-that-holds-a-live-call-on-telnyx-edge-compute-33im>
> Published: 2026-08-12 00:35:01+00:00

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](https://github.com/team-telnyx/telnyx-code-examples/tree/main/edge-voice-agent-holds-call)

The core flow is:

``` php
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
```

Each live call gets a `VoiceAgent`

actor keyed by `call_control_id`

.

That actor stores:

`POST /webhooks/voice`

receives Call Control webhooks`GET /debug/call?call_control_id=<call_id>`

inspects actor state`POST /debug/respond`

tests an LLM turn without a live call`GET /health/liveness`

and `GET /health/readiness`

support health checksThe `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()`

and `this.getState()`

for durable call state`this.env.TELNYX.ai.openai.chat.createCompletion()`

for zero-credential inference through the `[telnyx]`

bindingThe model call looks like this:

``` js
const completion = await this.env.TELNYX.ai.openai.chat.createCompletion({
  model,
  messages: [{ role: "system", content: SYSTEM_PROMPT }, ...history],
  max_tokens: 200,
  temperature: 0.5,
});
```

The webhook handler speaks the returned text through Call Control TTS.

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:

```
npm install
telnyx-edge ship
```

Set the Call Control API key as an Edge secret:

```
telnyx-edge secret set TELNYX_API_KEY your_telnyx_api_key
```

Point your Call Control application webhook to:

```
https://edge-voice-agent-holds-call-<id>.telnyxcompute.com/webhooks/voice
```

Then call the Telnyx number assigned to that Call Control application.

Before shipping this pattern for real callers, I would add:

But even as a starter, this is a useful reference for building a voice agent that can actually stay on the call.

Resources:
