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

# Get Recording URL

> Mint a short-lived playback URL for a click-to-call's recording

Mint a playback URL for the recording of a click-to-call placed with `record: true`.

<Note>
  **This is a POST because it mints a credential** — a signed URL to a customer's conversation. That is not a safe, idempotent read, and it must not end up in a browser history, a proxy log or a prefetch. The URL expires in minutes and is never stored on our side; mint a new one when you need it.
</Note>

`{id}` accepts the `call_id`, your `reference_id`, or the `recording_id`.

## Ordering

Recording audio is produced by the call platform and uploaded **after** the call ends, so it lands a little later than the terminal event. Check `recording_available` on the call first — and pass `?refresh=1` when you do, which is what makes a finished call go look for audio that has since appeared:

```bash theme={null}
curl "https://api.60db.ai/dialer/click2call/CALL_ID?refresh=1" \
  -H "Authorization: Bearer $SIXTYDB_API_KEY"
```

## Request

### Headers

<ParamField header="Authorization" type="string" required>
  Bearer token with your API key
</ParamField>

### Path

<ParamField path="id" type="string" required>
  The `call_id`, the `reference_id`, or the `recording_id`
</ParamField>

## Response

<ResponseField name="recording" type="object">
  <Expandable title="properties">
    <ResponseField name="call_id" type="string">The call this recording belongs to</ResponseField>
    <ResponseField name="recording_id" type="string">Opaque recording id</ResponseField>
    <ResponseField name="recording_status" type="string">e.g. `uploaded`</ResponseField>
    <ResponseField name="recording_format" type="string | null">Audio format, when the platform reports one</ResponseField>
    <ResponseField name="recording_size" type="integer | null">Size in bytes, when the platform reports one</ResponseField>
    <ResponseField name="playback_url" type="string">The signed URL. Short-lived — play or download it now, don't persist it</ResponseField>
    <ResponseField name="expires_at" type="string">ISO 8601 expiry</ResponseField>
  </Expandable>
</ResponseField>

## Errors

| Status | Code                     | Meaning                                                                      |
| ------ | ------------------------ | ---------------------------------------------------------------------------- |
| 404    | `CALL_NOT_FOUND`         | No such call, reference or recording in this workspace                       |
| 404    | `RECORDING_NOT_OWNED`    | The recording belongs to another user in your workspace                      |
| 404    | `RECORDING_UNAVAILABLE`  | The call wasn't recorded, or the platform hasn't produced a recording for it |
| 409    | `DIALER_NOT_PROVISIONED` | Trunk not set up (Gate T)                                                    |

<Note>
  `RECORDING_UNAVAILABLE` reads as two different messages on purpose — "this call was not recorded" and "this call asked to be recorded, but no recording was produced" have different remedies. The first means turn `record` on before placing the call; the second is worth reporting rather than retrying.

  A recorded conversation belongs to whoever placed the call, so a teammate gets `RECORDING_NOT_OWNED` rather than the audio.
</Note>

## Example

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST https://api.60db.ai/dialer/click2call/7d004ba2-565f-4ee2-9aa2-19523d8db7ae/recording \
    -H "Authorization: Bearer your-api-key"
  ```

  ```javascript JavaScript theme={null}
  // 1. Has the audio landed yet?
  const call = await fetch(
    `https://api.60db.ai/dialer/click2call/${callId}?refresh=1`,
    { headers: { Authorization: `Bearer ${key}` } },
  ).then((r) => r.json());

  // 2. Mint the URL
  if (call.data.call.recording_available) {
    const res = await fetch(
      `https://api.60db.ai/dialer/click2call/${callId}/recording`,
      { method: 'POST', headers: { Authorization: `Bearer ${key}` } },
    );
    const { data } = await res.json();
    console.log('Play at:', data.recording.playback_url);
  }
  ```

  ```python Python theme={null}
  res = requests.post(
      f"https://api.60db.ai/dialer/click2call/{call_id}/recording",
      headers={"Authorization": f"Bearer {os.environ['SIXTYDB_API_KEY']}"},
  )
  rec = res.json()["data"]["recording"]
  print(rec["playback_url"], rec["expires_at"])
  ```
</RequestExample>

<ResponseExample>
  ```json 200 OK theme={null}
  {
    "success": true,
    "data": {
      "recording": {
        "call_id": "7d004ba2-565f-4ee2-9aa2-19523d8db7ae",
        "recording_id": "rec_01J9Z4K7",
        "recording_status": "uploaded",
        "recording_format": "wav",
        "recording_size": 1048576,
        "playback_url": "https://cdn.60db.ai/recordings/...",
        "expires_at": "2026-09-23T14:20:00Z"
      }
    }
  }
  ```

  ```json 404 Not recorded theme={null}
  {
    "success": false,
    "message": "This call was not recorded. Turn recording on before placing the call.",
    "code": "RECORDING_UNAVAILABLE"
  }
  ```
</ResponseExample>
