> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.telekesher.dev/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.telekesher.dev/_mcp/server.

# Playback events

Playback events track audio playback on active calls, including start, completion, and error states. Each playback command also emits a dedicated domain lifecycle event correlated via `operation_uuid`.

The correlation field on every webhook here is `operation_uuid`. Lifecycle
events normally echo the `202 Accepted` response for the command that owns that
playback. A terminal event caused by a later `playback/stop` retains the original
start or silence operation UUID.

## playback.started

Fired when the call leg begins playing the requested audio or silence stream. Receipt of [`playback.started`](#playbackstarted) means playback is active on the leg. For a multi-file URL playlist, it does **not** guarantee that every later item has already been fetched or decoded.

**Payload schema**

| Field            | Type      | Description                                                                                                                                                                                                                                   |
| ---------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `event`          | string    | Always `"playback.started"`. **Required**                                                                                                                                                                                                     |
| `session_uuid`   | string    | Unique session identifier. **Required**                                                                                                                                                                                                       |
| `operation_uuid` | string    | The `operation_uuid` echoed from the 202 ack of the `playback/play` request. **Required**                                                                                                                                                     |
| `urls`           | string\[] | The original URL(s) from a `playback/play` request, as an array. File and stream sources contain their original URL(s); silence emits `["silence"]`. **Required**                                                                             |
| `timestamp`      | string    | RFC 3339 / ISO 8601 UTC timestamp at which audio actually started playing on the leg (e.g. `"2024-06-10T06:13:20.000Z"`). If the sound-start timestamp is missing or unparseable, the platform uses event emission time instead. **Required** |

```json
{
  "event": "playback.started",
  "session_uuid": "acW68-f47ac10b-58cc-4372-a567-0e02b2c3d479",
  "operation_uuid": "a1b2c3d4-e5f6-4890-abcd-ef1234567890",
  "urls": ["https://cdn.example.com/audio/greeting.wav"],
  "timestamp": "2024-06-10T06:13:20.000Z"
}
```

### Measuring playback start latency

The `timestamp` field lets you estimate how long it took between requesting playback and audio reaching the caller. Parse the RFC 3339 string to a millisecond epoch value, then subtract the wall-clock milliseconds at which you sent the `POST /playback/play` request:

```text
latency_ms = Date.parse(playback_started.timestamp) − (the wall-clock time, in unix ms, at which you sent the POST /playback/play request)
```

Record the time you issued the `playback/play` request, parse `timestamp` when the [`playback.started`](#playbackstarted) webhook arrives, and subtract.

**Cross-clock caveat — treat the result as approximate.** The event timestamp and your POST-request time come from different clocks and can differ slightly. Use the computed latency for trend monitoring and relative comparisons, not as an exact, sub-millisecond measurement. A small negative value (e.g. the webhook timestamp appearing slightly *before* your send time) is possible under clock skew and should be treated as \~0.

## playback.stopped

Fired when playback completes normally or is stopped via the `playback/stop` command. The `offset_ms` field indicates where finite file playback ended. Each accepted playback that starts emits exactly one [`playback.stopped`](#playbackstopped) event on completion or interruption. An accepted playback that cannot start emits [`playback.failed`](#playbackfailed). A live stream or silence normally runs until it is replaced, stopped, or the session ends. A finite source rejected by the request-time probe emits no playback lifecycle event.

**Payload schema**

| Field            | Type      | Description                                                                                                                                   |
| ---------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `event`          | string    | Always `"playback.stopped"`. **Required**                                                                                                     |
| `session_uuid`   | string    | Unique session identifier. **Required**                                                                                                       |
| `operation_uuid` | string    | The `operation_uuid` of the `playback/play` operation that created the playback, including when a later `playback/stop` ends it. **Required** |
| `urls`           | string\[] | The original file or stream URL(s) from a `playback/play` request, or `["silence"]` for silence. Omitted when unavailable. *Conditional*      |
| `offset_ms`      | integer   | Playback position at stop time in milliseconds. Omitted when unavailable; treat a missing value as unknown, not 0. *Conditional*              |
| `duration_ms`    | integer   | Total playback duration in milliseconds. Omitted when unavailable; treat a missing value as unknown, not 0. *Conditional*                     |
| `timestamp`      | string    | RFC 3339 / ISO 8601 UTC emission timestamp. **Required**                                                                                      |

```json
{
  "event": "playback.stopped",
  "session_uuid": "acW68-f47ac10b-58cc-4372-a567-0e02b2c3d479",
  "operation_uuid": "a1b2c3d4-e5f6-4890-abcd-ef1234567890",
  "urls": ["https://cdn.example.com/audio/greeting.wav"],
  "offset_ms": 15230,
  "duration_ms": 15230,
  "timestamp": "2025-06-06T08:53:25.678Z"
}
```

## playback.failed

Fired when an accepted playback command cannot be completed. Before a finite `type: files` request is accepted, the gateway probes each URL with HEAD or a ranged GET; if a source cannot be prepared, the HTTP request fails synchronously and no playback webhook is emitted. After acceptance, `playback.failed` covers playback that still cannot start within the allotted window. Stream and silence playback can also fail with a timeout, and `playback/restart` fails when no playback is active. `play_and_get_digits` does not emit this event. Inspect the `reason` field to determine why the command failed.

**Payload schema**

| Field            | Type      | Description                                                                                                                                                                                                                                                                                                                                                                                       |
| ---------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `event`          | string    | Always `"playback.failed"`. **Required**                                                                                                                                                                                                                                                                                                                                                          |
| `session_uuid`   | string    | Unique session identifier. **Required**                                                                                                                                                                                                                                                                                                                                                           |
| `operation_uuid` | string    | The `operation_uuid` echoed from the `202 Accepted` response for the `playback/play` or `playback/restart` request that failed. **Required**                                                                                                                                                                                                                                                      |
| `reason`         | string    | Machine-readable failure reason. Known values: `"file_not_found"` (requested audio could not be found or fetched at play time), `"timeout"` (a start or silence stream never started within the allotted window), and `"no_active_playback"` (`playback/restart` was requested while nothing was playing). Branch on this value; treat any unrecognized reason as a generic failure. **Required** |
| `urls`           | string\[] | For a failed file or stream `playback/play`, the original URL(s) that failed, as an array. Omitted when unavailable and for failures that have no source URLs. *Optional*                                                                                                                                                                                                                         |
| `timestamp`      | string    | RFC 3339 / ISO 8601 UTC timestamp at which the event was emitted. Present on every webhook event. **Required**                                                                                                                                                                                                                                                                                    |

```json
{
  "event": "playback.failed",
  "session_uuid": "acW68-f47ac10b-58cc-4372-a567-0e02b2c3d479",
  "operation_uuid": "a1b2c3d4-e5f6-4890-abcd-ef1234567890",
  "reason": "file_not_found",
  "urls": ["https://cdn.example.com/audio/missing.wav"],
  "timestamp": "2025-06-06T08:53:23.123Z"
}
```

## command.playback.pause.accepted

Fired when an active playback pause command is accepted and dispatched. This
event confirms command dispatch only.

**Payload schema**

| Field            | Type   | Description                                                                                |
| ---------------- | ------ | ------------------------------------------------------------------------------------------ |
| `event`          | string | Always `"command.playback.pause.accepted"`. **Required**                                   |
| `session_uuid`   | string | Unique session identifier. **Required**                                                    |
| `operation_uuid` | string | The `operation_uuid` echoed from the 202 ack of the `playback/pause` request. **Required** |
| `timestamp`      | string | RFC 3339 / ISO 8601 UTC timestamp at which the event was emitted. **Required**             |

```json
{
  "event": "command.playback.pause.accepted",
  "session_uuid": "acW68-f47ac10b-58cc-4372-a567-0e02b2c3d479",
  "operation_uuid": "a1b2c3d4-e5f6-4890-abcd-ef1234567890",
  "timestamp": "2025-06-06T08:53:24.100Z"
}
```

## command.playback.resume.accepted

Fired when a playback resume command is accepted and dispatched. This event
confirms command dispatch only.

**Payload schema**

| Field            | Type   | Description                                                                                 |
| ---------------- | ------ | ------------------------------------------------------------------------------------------- |
| `event`          | string | Always `"command.playback.resume.accepted"`. **Required**                                   |
| `session_uuid`   | string | Unique session identifier. **Required**                                                     |
| `operation_uuid` | string | The `operation_uuid` echoed from the 202 ack of the `playback/resume` request. **Required** |
| `timestamp`      | string | RFC 3339 / ISO 8601 UTC timestamp at which the event was emitted. **Required**              |

```json
{
  "event": "command.playback.resume.accepted",
  "session_uuid": "acW68-f47ac10b-58cc-4372-a567-0e02b2c3d479",
  "operation_uuid": "a1b2c3d4-e5f6-4890-abcd-ef1234567890",
  "timestamp": "2025-06-06T08:53:25.200Z"
}
```

## command.playback.seek.accepted

Fired when a playback seek command is accepted and dispatched. The `offset_ms`
field carries the requested position. This event confirms command dispatch only.

**Payload schema**

| Field            | Type    | Description                                                                                                                 |
| ---------------- | ------- | --------------------------------------------------------------------------------------------------------------------------- |
| `event`          | string  | Always `"command.playback.seek.accepted"`. **Required**                                                                     |
| `session_uuid`   | string  | Unique session identifier. **Required**                                                                                     |
| `operation_uuid` | string  | The `operation_uuid` echoed from the 202 ack of the `playback/seek` request. **Required**                                   |
| `offset_ms`      | integer | The new playback position in milliseconds, as requested by the `seek_ms` field of the `playback/seek` command. **Required** |
| `timestamp`      | string  | RFC 3339 / ISO 8601 UTC timestamp at which the event was emitted. **Required**                                              |

```json
{
  "event": "command.playback.seek.accepted",
  "session_uuid": "acW68-f47ac10b-58cc-4372-a567-0e02b2c3d479",
  "operation_uuid": "a1b2c3d4-e5f6-4890-abcd-ef1234567890",
  "offset_ms": 5000,
  "timestamp": "2025-06-06T08:53:26.300Z"
}
```

## command.playback.restart.accepted

Fired when a playback restart command is accepted and dispatched. This event
confirms command dispatch only.

**Payload schema**

| Field            | Type   | Description                                                                                  |
| ---------------- | ------ | -------------------------------------------------------------------------------------------- |
| `event`          | string | Always `"command.playback.restart.accepted"`. **Required**                                   |
| `session_uuid`   | string | Unique session identifier. **Required**                                                      |
| `operation_uuid` | string | The `operation_uuid` echoed from the 202 ack of the `playback/restart` request. **Required** |
| `timestamp`      | string | RFC 3339 / ISO 8601 UTC timestamp at which the event was emitted. **Required**               |

```json
{
  "event": "command.playback.restart.accepted",
  "session_uuid": "acW68-f47ac10b-58cc-4372-a567-0e02b2c3d479",
  "operation_uuid": "a1b2c3d4-e5f6-4890-abcd-ef1234567890",
  "timestamp": "2025-06-06T08:53:27.400Z"
}
```

## Triggered by

These events are produced by the following commands:

* [`playback.started`](#playbackstarted) — [playback/play](/api/playback/play) (Asynchronous).
* [`playback.stopped`](#playbackstopped) — [playback/play](/api/playback/play) (completion or interruption) and [playback/stop](/api/playback/stop).
* [`playback.failed`](#playbackfailed) — [playback/play](/api/playback/play) and [playback/restart](/api/playback/restart).
* [`command.playback.pause.accepted`](#commandplaybackpauseaccepted) — [playback/pause](/api/playback/pause) (Immediate).
* [`command.playback.resume.accepted`](#commandplaybackresumeaccepted) — [playback/resume](/api/playback/resume) (Immediate).
* [`command.playback.seek.accepted`](#commandplaybackseekaccepted) — [playback/seek](/api/playback/seek) (Immediate).
* [`command.playback.restart.accepted`](#commandplaybackrestartaccepted) — [playback/restart](/api/playback/restart) (Immediate).