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

# Recordings

> Access call recordings via signed URLs or direct audio streaming.

Yotel records all answered calls by default. Recordings are stored in
GCP Cloud Storage and accessed via signed URLs or a streaming proxy.
Voice agent calls produce **stereo recordings** (left channel = caller,
right channel = AI).

## Get a recording signed URL

Returns a JSON response with a time-limited signed URL and metadata.

<CodeGroup>
  ```bash curl theme={null}
  curl https://api.yotel.in/api/v1/calls/{call_id}/recording \
    -H "Authorization: Bearer yt_live_YOUR_KEY"
  ```

  ```python Python theme={null}
  recording = client.recordings.get_signed_url("call_id")
  print(recording["url"])       # 7-day signed URL
  print(recording["stereo"])    # True for AI calls
  ```

  ```typescript TypeScript theme={null}
  const recording = await client.recordings.getSignedUrl("call_id");
  console.log(recording.url);     // 7-day signed URL
  console.log(recording.stereo);  // true for AI calls
  ```
</CodeGroup>

**Scope:** `calls:read`  |  **Status:** `200 OK`

### Response

```json theme={null}
{
  "call_id": "call_abc123",
  "url": "https://storage.googleapis.com/yotel-dialer-audio/...",
  "expires_at": "2026-05-25T10:04:48Z",
  "duration_s": 142,
  "stereo": true,
  "format": "audio/wav"
}
```

| Field        | Type            | Description                                                                  |
| ------------ | --------------- | ---------------------------------------------------------------------------- |
| `call_id`    | string          | Call UUID                                                                    |
| `url`        | string          | GCS signed URL — expires in 7 days                                           |
| `expires_at` | string          | ISO 8601 expiration timestamp                                                |
| `duration_s` | integer \| null | Recording duration in seconds                                                |
| `stereo`     | boolean         | `true` for voice agent calls (left=caller, right=AI); `false` for human-only |
| `format`     | string          | MIME type hint, typically `"audio/wav"`                                      |

<Warning>
  The signed URL expires after 7 days. For long-lived access, use the
  audio streaming endpoint below or re-fetch the signed URL.
</Warning>

***

## Stream recording audio

Returns the raw audio bytes directly. Use this for server-side processing
pipelines or when you need a stable URL that doesn't expire.

<CodeGroup>
  ```bash curl theme={null}
  curl https://api.yotel.in/api/v1/calls/{call_id}/recording/audio \
    -H "Authorization: Bearer yt_live_YOUR_KEY" \
    -o recording.wav
  ```

  ```python Python theme={null}
  # Download to disk
  path = client.recordings.download("call_id", "recording.wav")

  # Or load into memory
  audio_bytes = client.recordings.fetch_bytes("call_id")
  ```

  ```typescript TypeScript theme={null}
  // Download to disk (Node.js only)
  await client.recordings.download("call_id", "recording.wav");

  // Or stream to a writable
  import { createWriteStream } from "fs";
  await client.recordings.streamTo("call_id", createWriteStream("out.wav"));
  ```
</CodeGroup>

**Scope:** `recordings:download`  |  **Status:** `200 OK`

Returns `audio/wav` content type with `Cache-Control: private, max-age=300`.
The response is chunked (64 KiB chunks) for memory efficiency.

<Note>
  The `recordings:download` scope is **separate** from `calls:read`.
  A key with only `calls:read` can fetch the signed URL (JSON) but
  cannot stream the raw audio.
</Note>

***

## Stereo recordings

When a [voice agent](/voice-agents/quickstart) handles a call, Yotel
records in stereo:

* **Left channel** — caller (PSTN audio)
* **Right channel** — AI agent (WebSocket audio)

Use the `stereo` field in the signed URL response to detect this. Stereo
recordings enable speaker diarisation without additional processing.

***

## Compliance

Every recording access (both signed URL and stream) is logged to an
internal audit table. The log captures the accessor identity, access
method, and timestamp for compliance purposes.

***

## Errors

| Status | Condition                                                                                                                                                          |
| ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `404`  | Call not found or belongs to another tenant                                                                                                                        |
| `409`  | Recording not yet available — the upload pipeline is still processing. Wait for the [`call.recording_ready`](/webhooks/event-catalog#callrecording_ready) webhook. |
