---
title: "Stream the agent-to-business audio behind a playback token"
method: GET
path: "/voice/recordings/{token}"
tags: ["voice"]
---

# Stream the agent-to-business audio behind a playback token

`GET /voice/recordings/{token}`

## Request

- Base URL: `https://corebee.ai/api/v1`
- URL: `https://corebee.ai/api/v1/voice/recordings/{token}`
- Auth: HTTP bearer

Streams the saved audio as `audio/mpeg`. Mint the token with GET /voice/bridge/{id}/recording; this URL is what that endpoint returns as `playback_url`.

⛔ THERE IS NO Authorization HEADER ON THIS ENDPOINT, AND THAT IS DELIBERATE. A browser `<audio src>` cannot send one, so a byte endpoint that demanded a bearer token could not be played at all. The token in the path IS the credential: 256 random bits, checked against our database on every request including every seek, good for exactly one recording, and dead fifteen minutes after it was minted. Treat the whole URL as a secret with a short fuse, it is not an object-store link and there is nothing in it to decode.

RANGE REQUESTS ARE SUPPORTED AND ARE HOW SEEKING WORKS. Send `Range: bytes=<start>-<end>` and you get 206 with `Content-Range`; every response carries `Accept-Ranges: bytes`. A multi-range request, a non-`bytes` unit or a malformed header is IGNORED and answered 200 with the whole file rather than refused, so a player that asks badly still gets audio, it just does not get a seek.

⛔ EVERY REFUSAL IS THE SAME EMPTY 404: unknown token, expired token, revoked token, a recording that no longer belongs to the organization the token names, and a deployment with the feature off. Do not branch on the reason, because there is no reason in the response. If playback stops working, mint a new token; if that also fails, the audio is genuinely unavailable to you. A 502 is different and IS retryable: the token was good and the byte source failed.

SCOPE: agent-to-business audio only, exactly as on the mint endpoint. The `Content-Disposition` filename says so as well, because a downloaded file outlives the JSON that explained what it was. Responses are `Cache-Control: private, no-store` and `Referrer-Policy: no-referrer`; do not put them behind a shared cache.

## Path parameters

- `token` string, required

## Headers

- `Range` string

## Response `200`

The whole file. Carries `Accept-Ranges: bytes`.

## Other responses

- `206`: Partial content, with `Content-Range`. This is what a seeking player gets.
- `404`: Empty body. Unknown, expired or revoked token, a recording outside the token's organization, or the feature is off. Deliberately indistinguishable.
- `416`: Range Not Satisfiable, with `Content-Range: bytes */<length>`. The range you asked for starts past the end of the file. A player that seeks to the very end can produce this; read the length off the header and re-ask. It is NOT an upstream failure and retrying the same range will produce it again.
- `429`: Too Many Requests, with `Retry-After`. ⛔ THIS ROUTE IS THROTTLED PER CLIENT IP, NOT per API key, it takes no API key at all, because a browser audio element cannot send one. The document-level x-rate-limit above describes the key-scoped limits and DOES NOT APPLY here. A single listen is a handful of requests, so a normal player never approaches the limit; several people listening from behind one office egress address share the same budget. If that is your deployment, tell us and we will raise it rather than have you retry.
- `502`: Empty body. The token was valid and the byte source failed. Retryable.

---

[API](https://skmtc.dev/corebee/apis/corebee-api.md) · [All operations](https://skmtc.dev/corebee/apis/corebee-api/llms.txt) · [OpenAPI document](https://skmtc.dev/corebee/apis/corebee-api/revisions/a48ea543b4dc?raw)
