> ## Documentation Index
> Fetch the complete documentation index at: https://developers.telnyx.com/llms.txt
> Use this file to discover all available pages before exploring further.

# The Call Webhook

```text theme={null}
Dial +15550001234 from any phone.
```

```text {5,16,20} theme={null}
support-agent/
├── agent.json
├── instructions.md
└── .telnyx/                        written by deploy
    ├── index.ts                                       ◀
    ├── telnyx.toml
    ├── telnyx-env.d.ts
    └── package.json

@telnyx/acp/                      the package
├── managed/
│   ├── index.ts
│   ├── agent.ts
│   ├── model.ts
│   ├── memory.ts
│   ├── tools.ts
│   ├── mcp.ts
│   ├── voice.ts                                       ◀
│   ├── direct-turn.ts
│   └── filler.ts
├── channels/
│   ├── index.ts                                       ◀
│   ├── phone.ts
│   ├── email.ts
│   ├── web.ts
│   └── wire.ts
├── synth/
│   ├── index.ts
│   ├── code-entry.ts
│   └── vendor.ts
└── spec/
    ├── schema.ts
    ├── model.ts
    ├── phone.ts
    ├── project.ts
    └── io.ts
```

`POST /webhooks/call_control_application/<id>`. `channelRoutes` verifies the signature, then the call handler in `channels/index.ts` acts on `call.initiated` for an incoming leg.

1. It wakes the call's instance, `warmCall(from)` on the conversation named by the call's id, so that the instance is placed and running and the caller's memory read has begun. The caller hears ringing meanwhile, typically one to three seconds.
2. It answers the inbound leg with the media stream requested in the same command, so the media starts the moment the leg is up.
3. The media server opens a WebSocket to `/agents/<name>/<call_control_id>?media=call`. [`mountAgents`](https://developers.telnyx.com/docs/agent-sdk/internals/mount/mountAgents) hands it, query and all, to the instance's [`webSocket()`](https://developers.telnyx.com/docs/agent-sdk/interface/webSocket), where the voice class answers the call. The `media=call` pair is what tells it a call's media from any other socket to the conversation: an upgrade without it is a client on the agent socket protocol, and goes to the text agent's `webSocket()` instead.

The first two steps are the handler's own:

```ts theme={null}
// channels/index.ts, voiceWebhook()
await warm(callControlId, typeof payload["from"] === "string" ? payload["from"] : undefined);
await calls.command(callControlId, "answer", {
  client_state: INBOUND_LEG_STATE,
  stream_url: mediaStreamUrl(`wss://${host}`, agent, callControlId), // …/agents/<name>/<call_control_id>?media=call
  stream_track: "inbound_track",
  // …a bidirectional PCMU stream at 8000 Hz, back to the same leg
});
```

`calls` is `createCallControlClient()` from [`@telnyx/edge-runtime/voice`](https://developers.telnyx.com/docs/agent-sdk/runtime-sdk). Its `command` goes out over the runtime's own connection, so the function holds no key. `warm` calls `warmCall` on the instance's stub, as [The Messaging Webhook](https://developers.telnyx.com/docs/agent-sdk/inside/request-handling/messaging-webhook.md) shows for a text.

The instance takes the call in `managed/voice.ts`:

```ts theme={null}
// managed/voice.ts
webSocket(ws, req) {
  // Only a call's media stream is answered with the voice layer: its address
  // carries `?media=call`. Any other upgrade is a client on the agent socket
  // protocol, which the text agent's `webSocket` serves.
  if (!isMediaUpgrade(req)) return super.webSocket(ws, req);
  this.answer(ws, answerOptions(spec));
  const engine = this.turnEngine();
  void (engine instanceof Promise ? engine : engine.warm()).catch(() => undefined);
}
warmCall(from) {
  this.nameCaller(from);
  const engine = this.turnEngine();
  void (engine instanceof Promise ? engine : engine.warm()).catch(() => undefined);
}
```

`webSocket(ws, req)` overrides the runtime SDK's [`webSocket()`](https://developers.telnyx.com/docs/agent-sdk/interface/webSocket).

* It reads the mark off the upgrade's address: `?media=call` is the media server's socket, answered with the voice layer and never handed to `super.webSocket()`; anything else is a client on the agent socket protocol and goes to `super.webSocket(ws, req)`, as on a text agent.
* `this.answer(ws, answerOptions(spec))` is the `voice()` mixin's `answer`. It takes the media socket and the options from `voice`, and runs the call from then on.
* Both methods start the turn engine's first reading, the harness's `ready()` or the direct engine's `warm()`, so that the first turn does not wait for it.
* `warmCall` carries no [`@rpc`](https://developers.telnyx.com/docs/agent-sdk/interface/rpc), so over the mount it is private, and only the handler reaches it.

`streaming.started`, `streaming.failed` and `call.hangup` on the answered leg are logged as one line each; a call that connects but stays silent explains itself in `streaming.failed`. [The Voice Loop](https://developers.telnyx.com/docs/agent-sdk/inside/turn-taking/voice-loop.md) is the turn taking that follows.
