> For the complete documentation index, see [llms.txt](https://docs.castmagic.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.castmagic.io/endpoints/recordings.md).

# Recordings

List, fetch, and create recordings, and read their transcripts and outputs.

A recording is a piece of media (audio or video) in a space, together with its transcript and any AI-generated outputs. This is the recording-centric flow — as opposed to [Transcripts](/endpoints/transcripts.md), which transcribes a URL without creating anything.

To create a recording you either [upload a file](/endpoints/uploads.md) and call `POST /v1/recordings` with the returned `object_id`, or [import from a URL](/endpoints/imports.md).

{% hint style="info" %}
**Tier:** `core` for all endpoints on this page (transcript translation via `?language=` requires `automation`). Base URL `https://app.castmagic.io`.
{% endhint %}

## List recordings

<mark style="color:blue;">`GET`</mark> `https://app.castmagic.io/v1/recordings`

Newest-first. Results are paginated with an opaque cursor — see [Pagination](#pagination) below.

#### Query Parameters

| Name      | Type    | Description                                             |
| --------- | ------- | ------------------------------------------------------- |
| space\_id | String  | Restrict to one space                                   |
| query     | String  | Match against recording titles                          |
| limit     | Integer | Default `25`, max `100`                                 |
| cursor    | String  | A `next_cursor` from a previous page, to fetch the next |

{% tabs %}
{% tab title="200: OK" %}

```json
{
    "recordings": [
        {
            "id": "e1...",
            "title": "Episode 12 — Pricing",
            "space": { "id": "a1...", "name": "Acme Podcast" },
            "created_at": "2026-08-21T22:00:00Z",
            "published_at": null,
            "language": "en",
            "transcript_status": "completed",
            "duration_minutes": 42,
            "has_transcript": true,
            "number": 12,
            "episode_number": 12,
            "season_number": 2,
            "rss": {
                "guid": "acme-ep-12",
                "published_at": "2026-08-20T14:00:00Z",
                "episode_number": 12
            }
        }
    ],
    "count": 1,
    "next_cursor": "MjAyNi0wOC0yMVQyMjowMDowMHxlMS4uLg"
}
```

{% endtab %}
{% endtabs %}

### Pagination

The list is paginated newest-first with a keyset cursor. To walk the full library, request pages until `next_cursor` is `null`, passing each response's `next_cursor` as the next request's `cursor`:

```
GET /v1/recordings?limit=100
GET /v1/recordings?limit=100&cursor=<next_cursor from page 1>
...until next_cursor is null
```

Cursors are opaque — don't construct or parse them; an unrecognizable cursor returns `400`. A page may contain fewer items than `limit` (recordings in inactive spaces are filtered out) yet still carry a `next_cursor`, so always page until it is `null` rather than stopping on a short page.

### Recording fields

| Field            | Description                                                                                 |
| ---------------- | ------------------------------------------------------------------------------------------- |
| `number`         | Castmagic's per-space ordinal for the recording (assigned on creation)                      |
| `episode_number` | Podcast episode number, when set                                                            |
| `season_number`  | Podcast season number, when set                                                             |
| `published_at`   | When the recording appeared in Castmagic                                                    |
| `rss`            | Feed provenance for RSS-imported recordings (see below); `null` for uploads and URL imports |

For recordings imported from an RSS feed, `rss` carries the feed's own identifiers — distinct from `published_at` (which is when it appeared in Castmagic):

| `rss` field      | Description                                    |
| ---------------- | ---------------------------------------------- |
| `guid`           | The feed item's GUID                           |
| `published_at`   | The feed's original publish date               |
| `episode_number` | The feed's `<itunes:episode>` number, when set |

## Fetch a recording

<mark style="color:blue;">`GET`</mark> `https://app.castmagic.io/v1/recordings/:id`

Returns a single recording, wrapped under `recording`, with the same shape as the list items above.

{% tabs %}
{% tab title="200: OK" %}

```json
{
    "recording": {
        "id": "e1...",
        "title": "Episode 12 — Pricing",
        "space": { "id": "a1...", "name": "Acme Podcast" },
        "created_at": "2026-08-21T22:00:00Z",
        "published_at": null,
        "language": "en",
        "transcript_status": "completed",
        "duration_minutes": 42,
        "has_transcript": true
    }
}
```

{% endtab %}

{% tab title="404: Not Found" %}

```json
{ "error": "Recording not found: e1..." }
```

{% endtab %}
{% endtabs %}

## Create a recording

<mark style="color:green;">`POST`</mark> `https://app.castmagic.io/v1/recordings`

Create a recording from an [uploaded object](/endpoints/uploads.md) and start transcription. Numbering is automatic. Transcription and — if the space has it enabled — content generation run automatically, so subscribe a webhook to `transcript.completed` and `output.completed` instead of polling.

#### Request Body

| Name                                         | Type    | Description                                   |
| -------------------------------------------- | ------- | --------------------------------------------- |
| object\_id<mark style="color:red;">\*</mark> | String  | The `object_id` from `POST /v1/uploads`       |
| space\_id                                    | String  | Defaults to the upload's space                |
| title                                        | String  | Defaults to the uploaded filename             |
| language                                     | String  | Language code for transcription               |
| speakers\_expected                           | Integer | Hint for the number of speakers               |
| context\_prompt                              | String  | Context to guide transcription and generation |

{% tabs %}
{% tab title="201: Created" %}

```json
{
    "recording": {
        "id": "e1...",
        "title": "interview.m4a",
        "space": { "id": "a1...", "name": "Acme Podcast" },
        "created_at": "2026-08-21T22:00:00Z",
        "published_at": null,
        "language": "en",
        "transcript_status": "queued",
        "duration_minutes": null,
        "has_transcript": false
    }
}
```

{% endtab %}

{% tab title="422: Unprocessable Entity Bytes never arrived in storage" %}

```json
{ "error": "upload not found in storage" }
```

{% endtab %}
{% endtabs %}

## Fetch a recording's transcript

<mark style="color:blue;">`GET`</mark> `https://app.castmagic.io/v1/recordings/:id/transcript`

Requires a completed transcript (`400` otherwise). `json` returns speaker utterances; text and document formats return the raw body with a matching content type.

#### Query Parameters

| Name               | Type    | Description                                                                                                              |
| ------------------ | ------- | ------------------------------------------------------------------------------------------------------------------------ |
| format             | String  | `json` (default) · `txt` · `vtt` · `srt` · `csv` · `pdf` · `docx`                                                        |
| language           | String  | Translate to a supported language code (requires `automation`); preserves the space's custom spellings and boosted words |
| include\_timestamp | Boolean | `false` to omit timestamps from `txt`                                                                                    |

{% tabs %}
{% tab title="200: OK format=json" %}

```json
{
    "recording_id": "e1...",
    "id": "t1...",
    "status": "completed",
    "duration_minutes": 42,
    "language": "en",
    "utterances": [...]
}
```

{% endtab %}

{% tab title="400: Bad Request Transcript not ready" %}

```json
{ "error": "transcript is not ready (status: processing)" }
```

{% endtab %}
{% endtabs %}

## Fetch a recording's outputs

<mark style="color:blue;">`GET`</mark> `https://app.castmagic.io/v1/recordings/:id/outputs`

Generated content for a recording. `text` is markdown for AI-written items. Pass `type` to filter to one output type (e.g. `episode_overview`).

#### Query Parameters

| Name | Type   | Description                    |
| ---- | ------ | ------------------------------ |
| type | String | Filter to a single output type |

{% tabs %}
{% tab title="200: OK" %}

```json
{
    "recording_id": "e1...",
    "recording_title": "Episode 12 — Pricing",
    "outputs": [
        {
            "id": "o1...",
            "type": "episode_overview",
            "status": "completed",
            "created_at": "2026-08-21T22:05:00Z",
            "text": "## Overview\n..."
        }
    ],
    "count": 1
}
```

{% endtab %}
{% endtabs %}
