---
title: "List a call's recordings and transcripts"
canonical: "https://bird.com/docs/api/reference/list-voice-call-recordings"
---

# List a call's recordings and transcripts

`GET /v1/voice/calls/{call_id}/recordings`

Returns everything captured across every leg of one call: its stored audio,
each leg's view of it, and every transcription attempt over it. A stream heard on
several legs appears once in `media`, with one view per leg in
`recordings`.

Audio streams, their recording views and transcripts leave the API 30 days
after the source audio starts. Transcription does not restart that period.
Read `truncated` before presenting the result as complete.

The collections arrive independently. A stream can appear before its
recording view, or without a view if delivery fails or the view expires.
Download audio with [`getVoiceLegRecordingAudio`](/docs/api/reference/get-voice-leg-recording-audio), passing the recording
view's `leg_id` and `id`. The download requires a recording view ID, so a
stream cannot be downloaded until its view is available.

A call this workspace never placed, or whose recordings have all expired,
answers with empty collections rather than `404`.

## Servers

- `https://{region}.platform.bird.com`: Regional API endpoint. Use the host for the region your organization is hosted in. Official Bird SDKs and the CLI select it automatically from your API key, so you rarely need to set it by hand.
  - `region`: `us1`, `eu1`. Default: `us1`. The region your organization's data is hosted in.

## Authentication

Use one authentication option:

- `BearerAuth`: HTTP bearer authentication. Pass the API key as a bearer token in the `Authorization` header. Workspace
keys use `bk_{region}_*`; organization master keys use `bm_{region}_*`.
The prefix identifies the region and selects the
API endpoint. Official Bird SDKs and the CLI derive the region from the key.

## Code samples

**TypeScript**

```ts
const inventory = await bird.voice.calls.recordings.list("vcs_01krdgeqcxet5s7t44vh8rt9mg");
for (const recording of inventory.recordings) {
  console.log(recording.leg_id, recording.id);
}
```

Examples: [TypeScript](/docs/api/reference/list-voice-call-recordings.ts.md) · [Python](/docs/api/reference/list-voice-call-recordings.py.md) · [Go](/docs/api/reference/list-voice-call-recordings.go.md) · [PHP](/docs/api/reference/list-voice-call-recordings.php.md) · [CLI](/docs/api/reference/list-voice-call-recordings.cli.md) · [MCP](/docs/api/reference/list-voice-call-recordings.mcp.md) · [cURL](/docs/api/reference/list-voice-call-recordings.curl.md)

## Example response `200`

```json
{
  "media": [
    {
      "id": "vcm_01krdgeqcxet5s7t44vh8rt9mg",
      "leg_id": "vcl_01krdgeqcxet5s7t44vh8rt9mg",
      "call_id": "vcs_01krdgeqcxet5s7t44vh8rt9mg",
      "codec": "PCMU",
      "state": "available",
      "offset_ms": 0,
      "audio_ms": 1000,
      "started_at": "2026-11-01T12:00:00Z",
      "ended_at": "2026-11-01T12:00:01Z",
      "source_party": {
        "endpoint": {
          "type": "pstn"
        },
        "address": "+14155551234"
      }
    }
  ],
  "recordings": [
    {
      "id": "vrc_01krdgeqcxet5s7t44vh8rt9mg",
      "leg_id": "vcl_01krdgeqcxet5s7t44vh8rt9mg",
      "call_id": "vcs_01krdgeqcxet5s7t44vh8rt9mg",
      "number": "+12025550100",
      "channel": "rx",
      "media_id": "vcm_01krdgeqcxet5s7t44vh8rt9mg",
      "media_started_at": "2026-11-01T12:00:00Z",
      "started_at": "2026-11-01T12:00:00Z",
      "ended_at": "2026-11-01T12:00:01Z"
    }
  ],
  "transcripts": [],
  "truncated": false,
  "evaluated_at": "2026-11-01T12:01:00Z"
}
```

## Path parameters

- `call_id` (string, required): The call to read. Every leg carries its call's ID as `call_id`.

## Header parameters

- `X-Workspace-Id` (string, optional): Workspace context for the request. Required for dashboard authentication and master keys. Workspace keys and access tokens already identify their workspace; send that workspace or omit the header. A different one is rejected.
- `X-Organization-Id` (string, optional): Organization context for the request. Required for dashboard authentication. An API key or access token carries its own organization, so send either that organization or no header at all; a different one is rejected.

## Response body

- `media` (array of object, required): The stored audio this read covers, each stream once.
- `media.id` (string, required): Unique identifier for this stream.
- `media.leg_id` (string, required): The leg this stream was captured from.
- `media.call_id` (nullable string): `null` on a stream captured before calls existed, which only its leg addresses.
- `media.codec` (string, required): Codec of the stored payload.
- `media.state` (string, required)

  What became of this stored audio.

  `open` while capture is still running. `available` once the bytes are
  stored. `failed` when capture did not produce any. `purged` is reserved for
  future use. Recordings are removed from the response after their retention
  period.

  Possible values: `open`, `available`, `failed`, `purged`
- `media.offset_ms` (integer, required): Length of the announcement that played before capture started. Zero when nothing was announced. It is not a position on a call clock.
- `media.audio_ms` (integer, required): Length of the payload itself. This runs shorter than `ended_at - started_at` by the silence before the first packet, so use it for anything about the sound and the window for anything about the leg.
- `media.source_party` (nullable object)

  The party this audio was captured from, which is what says whose voice it
  carries. `channel` only says which direction of the leg the stream came
  from. A recording view's `number` identifies a number associated with
  that view, which can be an outbound destination.

  `null` on a stream stored before the platform recorded a party for it.
- `media.source_party.endpoint` (nullable object)

  What kind of participant sat on this side of a leg, and the coordinate that kind carries: a telephone endpoint off the platform, a SIP or WebRTC endpoint, Bird answering, or the platform placing a leg onward. It does not name a person.

  `null` on an observation this API could not read. The entry stays, because the call counted it when it deduplicated, and dropping it here would report fewer participants than were observed.
- `media.source_party.endpoint.type` (string, required)

  What kind of participant sat on this side of a leg. It selects which payload below is present, and most kinds carry none because the party's `address` is already their coordinate.

  Possible values (may grow over time): `pstn`, `sip`, `voicemail`, `bridge_pstn`, `bridge_sip`, `webhook`
- `media.source_party.endpoint.sip` (object): The registered contact of a `sip` endpoint. Absent when the observation recorded none, and on every other kind of endpoint.
- `media.source_party.endpoint.sip.contact` (string, required): The address the user agent registered, as a `sip:` or `sips:` URI. It is where the endpoint asked to be reached, which is not always the address that was dialled.
- `media.source_party.endpoint.bridge_pstn` (object): Where the platform placed the leg onward, on a `bridge_pstn` endpoint. Absent on every other kind of endpoint.
- `media.source_party.endpoint.bridge_pstn.forward_to` (string, required): The number the platform placed the leg onward to, in E.164. The party's own `address` is the number that was dialled, so the two together are one hop of the call.
- `media.source_party.endpoint.bridge_pstn.forward_as` (string, required)

  Which number the forwarded leg presented to the far end.

  Possible values: `dialed_number`, `calling_number`
- `media.source_party.endpoint.bridge_sip` (object): The trunk the platform delivered the leg onward to, on a `bridge_sip` endpoint. Absent on every other kind of endpoint.
- `media.source_party.endpoint.bridge_sip.trunk_id` (string, required): The workspace trunk the leg was delivered onward to. It is distinct from the party's own `trunk_id`, which is the trunk this side itself sat behind.
- `media.source_party.address` (nullable string): This side's own address, in E.164 or as a `sip:` URI. `null` when the observation carried none, which does not say whether one was withheld, missing, or nonexistent.
- `media.source_party.overdial_suffix` (string): Digits dialled after this party's number, including leading zeroes. Absent when no overdial was received.
- `media.source_party.trunk_id` (nullable string): The workspace trunk on this side of the leg. `null` when this side sat behind no trunk.
- `media.source_party.user_id` (nullable string): The workspace user this side acted for, such as a user's browser, the ring group member whose side a recording captured, or the user who started a sequence call. `null` for every other participant, including an API key.
- `media.started_at` (string, required): When capture started, which is after any announcement rather than at the answer.
- `media.ended_at` (nullable string, required): When capture ended, or null while capture is open or its end is unknown.
- `recordings` (array of object, required): Each leg's view of that audio.
- `recordings.id` (string, required): Unique identifier for this view.
- `recordings.leg_id` (string, required): The leg this view belongs to.
- `recordings.call_id` (nullable string): `null` on a recording captured before calls existed.
- `recordings.number` (string): The number associated with this recording view, when one was recorded. It can be an outbound destination. Omitted for numberless calls.
- `recordings.channel` (string, required)

  It does not say whose voice it is.

  Possible values: `rx`, `tx`
- `recordings.media_id` (string, required): The stream this view refers to. A stream captured on another leg or omitted by retention or truncation may be absent from `media` in this response.
- `recordings.media_started_at` (string, required): When the referenced stream started, which a view captured from the far leg does not share with its own `started_at`.
- `recordings.started_at` (string, required)
- `recordings.ended_at` (nullable string, required): When capture ended, or null while capture is open or its end is unknown.
- `transcripts` (array of object, required): Every transcription attempt over that audio, including failed ones.
- `transcripts.id` (string, required): Unique identifier for this attempt.
- `transcripts.leg_id` (string, required): The leg the transcribed stream was captured from.
- `transcripts.call_id` (nullable string): `null` on a transcript of a stream captured before calls existed.
- `transcripts.media_id` (string, required): The stream this attempt describes.
- `transcripts.media_started_at` (string, required): When that stream started. A transcript is retained with the stream it describes rather than on its own transcription lag.
- `transcripts.state` (string, required)

  Whether the attempt produced a transcript. `failed` is an outcome, not evidence that a later attempt cannot succeed.

  Possible values: `available`, `failed`
- `transcripts.source` (string, required)

  What the times in `cues` are worth: `whole` for a pass that times nothing, `aligned` for offline forced alignment, `live` for a streaming producer.

  Possible values (may grow over time): `whole`, `aligned`, `live`
- `transcripts.model` (string): The model that produced it.
- `transcripts.channel` (string, required)

  The direction of the transcribed stream.

  Possible values: `rx`, `tx`
- `transcripts.cues` (array of object, required): The transcript, in order. Concatenating cues across streams reads as prose nobody spoke, and a reader matching over it can match the wrong party's words.
- `transcripts.cues.seq` (integer, required): Position of this cue in the transcript.
- `transcripts.cues.start_ms` (nullable integer): Offset of this cue from the start of its stream, in milliseconds. Set on `aligned` cues; `null` when nothing measured it, because a zero would be a claim about timing that was never made.
- `transcripts.cues.end_ms` (nullable integer): Offset of the end of this cue from the start of its stream. `null` on the same terms as `start_ms`.
- `transcripts.cues.text` (string, required): What was transcribed for this cue. Empty on the single cue of a stream where nothing was heard.
- `transcripts.source_party` (nullable object)

  The party this audio was captured from, which is what says whose voice it
  carries. `channel` only says which direction of the leg the stream came
  from. A recording view's `number` identifies a number associated with
  that view, which can be an outbound destination.

  `null` on a stream stored before the platform recorded a party for it.
- `transcripts.source_party.endpoint` (nullable object)

  What kind of participant sat on this side of a leg, and the coordinate that kind carries: a telephone endpoint off the platform, a SIP or WebRTC endpoint, Bird answering, or the platform placing a leg onward. It does not name a person.

  `null` on an observation this API could not read. The entry stays, because the call counted it when it deduplicated, and dropping it here would report fewer participants than were observed.
- `transcripts.source_party.endpoint.type` (string, required)

  What kind of participant sat on this side of a leg. It selects which payload below is present, and most kinds carry none because the party's `address` is already their coordinate.

  Possible values (may grow over time): `pstn`, `sip`, `voicemail`, `bridge_pstn`, `bridge_sip`, `webhook`
- `transcripts.source_party.endpoint.sip` (object): The registered contact of a `sip` endpoint. Absent when the observation recorded none, and on every other kind of endpoint.
- `transcripts.source_party.endpoint.sip.contact` (string, required): The address the user agent registered, as a `sip:` or `sips:` URI. It is where the endpoint asked to be reached, which is not always the address that was dialled.
- `transcripts.source_party.endpoint.bridge_pstn` (object): Where the platform placed the leg onward, on a `bridge_pstn` endpoint. Absent on every other kind of endpoint.
- `transcripts.source_party.endpoint.bridge_pstn.forward_to` (string, required): The number the platform placed the leg onward to, in E.164. The party's own `address` is the number that was dialled, so the two together are one hop of the call.
- `transcripts.source_party.endpoint.bridge_pstn.forward_as` (string, required)

  Which number the forwarded leg presented to the far end.

  Possible values: `dialed_number`, `calling_number`
- `transcripts.source_party.endpoint.bridge_sip` (object): The trunk the platform delivered the leg onward to, on a `bridge_sip` endpoint. Absent on every other kind of endpoint.
- `transcripts.source_party.endpoint.bridge_sip.trunk_id` (string, required): The workspace trunk the leg was delivered onward to. It is distinct from the party's own `trunk_id`, which is the trunk this side itself sat behind.
- `transcripts.source_party.address` (nullable string): This side's own address, in E.164 or as a `sip:` URI. `null` when the observation carried none, which does not say whether one was withheld, missing, or nonexistent.
- `transcripts.source_party.overdial_suffix` (string): Digits dialled after this party's number, including leading zeroes. Absent when no overdial was received.
- `transcripts.source_party.trunk_id` (nullable string): The workspace trunk on this side of the leg. `null` when this side sat behind no trunk.
- `transcripts.source_party.user_id` (nullable string): The workspace user this side acted for, such as a user's browser, the ring group member whose side a recording captured, or the user who started a sequence call. `null` for every other participant, including an API key.
- `transcripts.created_at` (string, required)
- `truncated` (boolean, required)

  Whether a collection above was cut at this read's per-collection limit of
  500. When `true`, there is more than is shown here and this response is not
  the whole set.

  `false` means no collection hit that limit, and a collection that could
  not be read in full fails the request rather than returning a short
  answer. What `false` does not establish is that every recording made has
  arrived: delivery into this store is asynchronous, so a recording made
  moments ago can be absent from a complete read.
- `evaluated_at` (string, required): The instant each collection's retention was decided at. Nothing here stays true because you are still holding the response. A recording can expire after it.

## Related resources

- [Should I use a Bird SDK or call the API directly?](/explained/platform/should-i-use-an-sdk-or-call-the-api-directly) (answer)
- [Build your first integration](/learn/paths/integration) (course)
- [Send your first email](/docs/get-started/send-your-first-email) (docs)

[Get an implementation brief](/learn/workspace?topic=api-basics)
