---
title: "Media and playback API"
description: "Check whether generation can start separately from whether audio already exists. Read per-block media freshness and generation activity, compile a playback manifest for current audio, and mint short-lived URLs. Generation availability checks plan access, voice/compiler eligibility, and confirmed ElevenLabs preparation exhaustion; it is not a promise of audio, synthesis quota, or funding. Free accounts cannot generate with ElevenLabs v4, but existing audio remains playable."
canonical_url: https://lyricwinter.com/docs/api/media
markdown_url: https://lyricwinter.com/docs/api/media.md
last_updated: 2026-10-15
status: beta
---
# LyricWinter API reference: Media and playback

> Check whether generation can start separately from whether audio already exists. Read per-block media freshness and generation activity, compile a playback manifest for current audio, and mint short-lived URLs. Generation availability checks plan access, voice/compiler eligibility, and confirmed ElevenLabs preparation exhaustion; it is not a promise of audio, synthesis quota, or funding. Free accounts cannot generate with ElevenLabs v4, but existing audio remains playable.

The LyricWinter API is in beta. Base URL: `https://lyricwinter.com/api/v1`. Authenticate every request with `Authorization: Bearer lw_...`. Successful responses are wrapped as `{ "data": ..., "request_id": "req_..." }`. Machine-readable contract: https://lyricwinter.com/api/v1/openapi.json.

## Endpoints

- [Get section media](https://lyricwinter.com/docs/api/media#get-studio-section-media): `GET /studio/sections/{sectionId}/media`
- [Check generation eligibility](https://lyricwinter.com/docs/api/media#get-studio-section-generation-availability): `GET /studio/sections/{sectionId}/generation-availability`
- [Get a playback manifest](https://lyricwinter.com/docs/api/media#get-studio-playback-manifest): `GET /studio/sections/{sectionId}/playback-manifest`
- [Create a media access URL](https://lyricwinter.com/docs/api/media#create-studio-media-access-url): `POST /studio/media-assets/{assetId}/access-url`

### Get section media

`GET /studio/sections/{sectionId}/media`

Scope: `studio:read` · Operation ID: `getStudioSectionMedia`

Returns one entry per document block with audio freshness, independent generation activity, the latest generation/import error, and a short-lived signed playback_url for active audio. A failed retake can coexist with current playable audio. Available generation does not imply audio exists. Join by block_id. Request ?schema_version=2 for boundary_silence_handling from the active take's stored intent: timeline, fixed_in_asset, unsupported, or unavailable. The default response remains unchanged. Timeline handling applies only to boundary pauses; internal timed silences remain part of generated audio.

#### Path parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `sectionId` | string, uuid | Yes |  |

#### Query parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `schema_version` | integer, default 1, one of `1`, `2` | No | 1 or omitted preserves the existing unversioned payload. 2 returns active-take boundary_silence_handling. Other values return 400. |

#### Response 200

Current block media read model; schema_version=2 additionally describes active-take boundary silence handling.

Fields inside `data`:

- One of:
  - **StudioSectionMediaPayload**: Current per-block media state for a Studio section.
    - `section_id` (string, uuid; required)
    - `playback_duration_ms` (integer | null, ≥ 0; required): Duration of the newest saved render manifest for the section's current document and speaker-registry versions; null when none exists.
    - `blocks` (array of object; required): One entry per document block, in document order.
      - `block_id` (string, uuid; required)
      - `freshness` (string, one of `absent`, `current`, `stale`, `unsupported`; required): absent: no active rendition. current: the active rendition matches the block's current generation intent. stale: it differs (see stale_reasons). unsupported: the block kind is not speech, narration, or sfx.
      - `activity` (string, one of `idle`, `generating`, `failed`; required): generating: a pending rendition is queued, running, or waiting to retry, or imported audio is being copied. failed: the latest request or import failed. idle otherwise. Independent of freshness.
      - `active_block_version` (integer | null, ≥ 1; required): Block version the active rendition was requested for.
      - `requested_block_version` (integer | null, ≥ 1; required): Block version of the pending rendition or import, if any.
      - `active_rendition_id` (string | null, uuid; required)
      - `audio_version_count` (integer, ≥ 0; required): Number of completed audio versions for the block.
      - `generation_batch_id` (string | null, uuid; required): Generation batch of the pending rendition.
      - `playback_url` (string | null, uri; required): Short-lived signed URL for the active rendition's audio; null without an active asset.
      - `playback_url_expires_at` (string | null, date-time; required)
      - `content_type` (string | null; required)
      - `size_bytes` (integer | null, ≥ 1; required)
      - `analysis_integrated_lufs` (number | null, -120–24; required)
      - `analysis_sample_peak_dbfs` (number | null, -200–24; required)
      - `provider` (string | null; required): Provider that actually produced the active rendition (stored metadata for imports).
      - `model` (string | null; required): Model that actually produced the active rendition (stored metadata for imports).
      - `error` (string | null; required): Latest generation or import error message.
      - `stale_reasons` (array of string, one of `block_kind`, `text`, `pronunciation`, `excluded_text`, `timing`, `voice`, `generation_profile`, `delivery_controls`, `sound_effect`; optional): Present for speech, narration, and sfx blocks; non-empty only when freshness is stale.
  - **schema_version: 2**: Opt-in media response with authoritative active-take boundary silence handling.
    - `schema_version` (2; required)
    - `section_id` (string, uuid; required)
    - `playback_duration_ms` (integer | null, ≥ 0; optional)
    - `blocks` (array of object; required)
      - `block_id` (string, uuid; required)
      - `freshness` (string, one of `absent`, `current`, `stale`, `unsupported`; required): absent: no active rendition. current: the active rendition matches the block's current generation intent. stale: it differs (see stale_reasons). unsupported: the block kind is not speech, narration, or sfx.
      - `activity` (string, one of `idle`, `generating`, `failed`; required): generating: a pending rendition is queued, running, or waiting to retry, or imported audio is being copied. failed: the latest request or import failed. idle otherwise. Independent of freshness.
      - `active_block_version` (integer | null, ≥ 1; required): Block version the active rendition was requested for.
      - `requested_block_version` (integer | null, ≥ 1; required): Block version of the pending rendition or import, if any.
      - `active_rendition_id` (string | null, uuid; required)
      - `audio_version_count` (integer, ≥ 0; required): Number of completed audio versions for the block.
      - `generation_batch_id` (string | null, uuid; required): Generation batch of the pending rendition.
      - `playback_url` (string | null, uri; required): Short-lived signed URL for the active rendition's audio; null without an active asset.
      - `playback_url_expires_at` (string | null, date-time; required)
      - `content_type` (string | null; required)
      - `size_bytes` (integer | null, ≥ 1; required)
      - `analysis_integrated_lufs` (number | null, -120–24; required)
      - `analysis_sample_peak_dbfs` (number | null, -200–24; required)
      - `provider` (string | null; required): Provider that actually produced the active rendition (stored metadata for imports).
      - `model` (string | null; required): Model that actually produced the active rendition (stored metadata for imports).
      - `error` (string | null; required): Latest generation or import error message.
      - `stale_reasons` (array of string, one of `block_kind`, `text`, `pronunciation`, `excluded_text`, `timing`, `voice`, `generation_profile`, `delivery_controls`, `sound_effect`; optional): Present for speech, narration, and sfx blocks; non-empty only when freshness is stale.
      - `boundary_silence_handling` (string, one of `timeline`, `fixed_in_asset`, `unsupported`, `unavailable`; required): Boundary silence handling of the active take, not a promise about internal pauses. timeline: stored intent uses timeline-v1, so boundary pauses are assembled outside synthesis. fixed_in_asset: generated speech uses legacy baked timing. unsupported: the active media does not expose supported boundary-pause semantics. unavailable: no active media. Internal timed silences remain fixed in the asset even with timeline handling.

Errors: `400`, `401`, `403`, `404`, `500`, `503` return the standard error envelope.

#### Example request

```bash
curl "https://lyricwinter.com/api/v1/studio/sections/$SECTION_ID/media" \
  -H "Authorization: Bearer $LYRICWINTER_API_KEY"
```

### Check generation eligibility

`GET /studio/sections/{sectionId}/generation-availability`

Scope: `studio:read` · Operation ID: `getStudioSectionGenerationAvailability`

Returns each block's effective requested profile, plan access, voice/provider materialization requirements, and compiler diagnostics. ElevenLabs v4 is unavailable on free plans even for a prepared voice. On paid plans, confirmed ElevenLabs preparation exhaustion makes an unprepared ElevenLabs option unavailable; already prepared current-revision voices remain eligible. `generation.status: available` does not mean audio exists or guarantee live synthesis quota or funding. Use `/media` for freshness, activity, errors, and playable audio. Pinned project_sound blocks use library materialization rather than synthesis and have generation: null, as do blocks without provider generation. Null does not claim a sound is present or playable.

#### Path parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `sectionId` | string, uuid | Yes |  |

#### Response 200

Current block generation eligibility, not existing-audio readiness or live provider-account health.

Fields inside `data`:

- `section_id` (string, uuid; required)
- `document_version` (integer, ≥ 1; required)
- `speaker_registry_version` (integer, ≥ 1; required)
- `blocks` (array of object; required)
  - `block_id` (string, uuid; required)
  - `generation` (object | null; required): Whether generation can be requested under current voice/compiler authority, including confirmed ElevenLabs voice-preparation exhaustion for uncached voices. Available does not mean audio exists; check /media freshness, activity, and playback_url. This is not a general provider synthesis quota or billing check.
    - `requested_profile` (object; required): Saved profile read shape, including historical options. For new full-profile requests use StudioGenerationProfileRequest; its provider/model branches list accepted option keys. Generation eligibility and actual audio readiness are separate reads.
      - `provider` (string, pattern ^[a-z][a-z0-9_-]{0,63}$; required)
      - `model` (string, 1–200 chars; required)
      - `fallback` (string, one of `strict`, `best_effort`; required)
      - `controls` (string, one of `strict`, `best_effort`; required)
      - `options` (object; required)
    - `status` (string, one of `available`, `materialization_required`, `unavailable`; required)
    - `reason` (string | null; required)
    - `reason_code` (string, one of `provider_asset_missing`, `acoustic_revision_changed`; optional)
    - `diagnostics` (array of object; required)
      - `code` (string, min 1 chars; required)
      - `severity` (string, one of `warning`, `error`; required)
      - `message` (string, min 1 chars; required)
    - `options` (array of object; required)
      - `provider` (string, min 1 chars; required)
      - `model` (string, min 1 chars; required)
      - `status` (string, one of `available`, `materialization_required`, `unavailable`; required)
      - `reason` (string | null; required)
      - `reason_code` (string, one of `provider_asset_missing`, `acoustic_revision_changed`; optional)
      - `diagnostics` (array of object; required)
        - `code` (object; required)
        - `severity` (object; required)
        - `message` (object; required)

Errors: `400`, `401`, `403`, `404`, `500` return the standard error envelope.

#### Example request

```bash
curl "https://lyricwinter.com/api/v1/studio/sections/$SECTION_ID/generation-availability" \
  -H "Authorization: Bearer $LYRICWINTER_API_KEY"
```

### Get a playback manifest

`GET /studio/sections/{sectionId}/playback-manifest`

Scope: `studio:read` · Operation ID: `getStudioPlaybackManifest`

Compiles the section's current speech, pauses, and sound-effect overlays into one timeline with signed asset URLs. Pass document_version and speaker_registry_version from the document. In sequential_units, unit_type: asset has the audio asset's own duration, unit_type: silence is an authored pause assembled separately on the timeline, and unit_type: spacing is an inter-block gap; sound effects are overlay_units. A speech or narration export block sums its contiguous asset and silence unit durations, excluding spacing, so do not equate its duration_ms with one asset unit's duration. Join blocks by stable block_id and per-unit records by unit_id, not by start_ms. Optional schema_version=2 adds unit_word_timings sidecars keyed by unit_id, block_id, and asset_id with provider-aligned section-relative words or word_timing: unavailable. Alignment is bound to the actual asset content hash; old/unsupported/invalid alignment is not estimated or transcribed. The default response and hashed audio manifest remain unchanged. Unknown query keys return 400.

#### Path parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `sectionId` | string, uuid | Yes |  |

#### Query parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `document_version` | integer, ≥ 1 | Yes | Saved section document version to compile. If it no longer matches the saved section, the request fails with 409 STUDIO_PLAYBACK_STALE. |
| `schema_version` | integer, default 1, one of `1`, `2` | No | Opt in to provider word timing sidecars. The default retains the V1 response. |
| `speaker_registry_version` | integer, ≥ 1 | Yes | Saved speaker-registry (cast) version to compile. If it no longer matches, the request fails with 409 STUDIO_PLAYBACK_STALE. |

#### Response 200

Current private playback manifest and signed asset URLs.

Fields inside `data`:

- One of:
  - **schema_version: 1**: Ephemeral current playback manifest with signed asset URLs; no render run is created.
    - `schema_version` (1; required)
    - `mastering_version` (integer, ≥ 1; required)
    - `manifest_hash` (string, pattern ^[a-f0-9]{64}$; required): SHA-256 of the canonical manifest.
    - `manifest` (object; required): Compiled playback timeline for one saved section document and speaker-registry version.
      - `schema_version` ("studio-render-manifest-2"; required)
      - `project_id` (string, uuid; required)
      - `section_id` (string, uuid; required)
      - `document_version` (integer, ≥ 1; required)
      - `speaker_registry_version` (integer, ≥ 1; required)
      - `mastering_version` (integer, ≥ 1; required)
      - `mastering_profile_hash` (string, pattern ^[a-f0-9]{64}$; required)
      - `mastering_profile` (object; required)
        - `schema_version` (1; required)
        - `enabled` (boolean; required)
        - `target_lufs` (number, -30–-10; required)
        - `strength` (number, 0–1; required)
        - `max_boost_db` (number, 0–24; required)
        - `max_reduction_db` (number, 0–24; required)
        - `silence_threshold_lufs` (number, -120–-20; required)
        - `peak_ceiling_db` (number, -12–-0.1; required)
        - `speaker_adjustments_db` (map of number; required)
        - `sfx_bus_gain_db` (number, -24–24, default 0; required)
      - `completeness` (string, one of `partial`, `complete`; required): complete exactly when gaps is empty.
      - `duration_ms` (integer, ≥ 0; required): Latest end point of any sequential or overlay unit.
      - `sequential_units` (array of object, max 50000 items; required): Contiguous from 0 ms in canonical block order; unit IDs are unique across sequential and overlay units.
        - One of:
          - **unit_type: "asset"**
            - `unit_id` (string, uuid; required)
            - `block_id` (string, uuid; required)
            - `block_index` (integer, ≥ 0; required)
            - `block_version` (integer, ≥ 1; required)
            - `block_kind` (string, one of `speech`, `narration`; required)
            - `start_ms` (integer, ≥ 0; required)
            - `duration_ms` (integer, ≥ 1; required)
            - `rendition_id` (string, uuid; required)
            - `rendition_intent_hash` (string, pattern ^[a-f0-9]{64}$; required)
            - `unit_type` ("asset"; required)
            - `asset_id` (string, uuid; required): Key into the payload's assets array.
            - `asset_content_hash` (string, pattern ^[a-f0-9]{64}$; required)
            - `source_ranges` (array of object, 1–10000 items; required)
              - `start_utf16` (integer, ≥ 0; required)
              - `end_utf16` (integer, ≥ 0; required)
            - `speaker_id` (string, uuid; required)
            - `analysis_integrated_lufs` (number, -120–12; required)
            - `analysis_sample_peak_dbfs` (number, -200–12; required)
            - `effective_gain_db` (number, -24–24; required)
          - **unit_type: "silence"**
            - `unit_id` (string, uuid; required)
            - `block_id` (string, uuid; required)
            - `block_index` (integer, ≥ 0; required)
            - `block_version` (integer, ≥ 1; required)
            - `block_kind` (string, one of `speech`, `narration`; required)
            - `start_ms` (integer, ≥ 0; required)
            - `duration_ms` (integer, ≥ 1; required)
            - `rendition_id` (string, uuid; required)
            - `rendition_intent_hash` (string, pattern ^[a-f0-9]{64}$; required)
            - `unit_type` ("silence"; required)
          - **unit_type: "spacing", spacing_kind: "inter_passage"**
            - `unit_id` (string, uuid; required)
            - `block_id` (string, uuid; required)
            - `block_index` (integer, ≥ 0; required)
            - `block_version` (integer, ≥ 1; required)
            - `block_kind` (string, one of `speech`, `narration`; required)
            - `start_ms` (integer, ≥ 0; required)
            - `duration_ms` (integer, ≥ 1; required)
            - `unit_type` ("spacing"; required)
            - `spacing_kind` ("inter_passage"; required)
      - `overlay_units` (array of object, max 10000 items; required): Ordered by start_ms, block_index, block_id, unit_id.
        - `unit_type` ("sfx"; required)
        - `unit_id` (string, uuid; required)
        - `block_id` (string, uuid; required)
        - `block_index` (integer, ≥ 0; required)
        - `block_version` (integer, ≥ 1; required)
        - `intent_hash` (string, pattern ^[a-f0-9]{64}$; required)
        - `asset_id` (string, uuid; required): Key into the payload's assets array.
        - `asset_content_hash` (string, pattern ^[a-f0-9]{64}$; required)
        - `start_ms` (integer, ≥ 0; required)
        - `duration_ms` (integer, ≥ 1; required)
        - `gain_db` (number, -60–24; required)
        - `fade_in_ms` (integer, ≥ 0; required)
        - `fade_out_ms` (integer, ≥ 0; required)
      - `timed_text_units` (array of object, max 50000 items; required): Ordered by start_ms, block_index, block_id, unit_id.
        - `unit_id` (string, uuid; required)
        - `block_id` (string, uuid; required)
        - `block_index` (integer, ≥ 0; required)
        - `block_version` (integer, ≥ 1; required)
        - `speaker_id` (string, uuid; required)
        - `start_ms` (integer, ≥ 0; required)
        - `duration_ms` (integer, ≥ 1; required)
        - `text` (string, 1–100000 chars; required)
        - `text_hash` (string, pattern ^[a-f0-9]{64}$; required)
        - `source_ranges` (array of object, 1–10000 items; required)
          - `start_utf16` (integer, ≥ 0; required)
          - `end_utf16` (integer, ≥ 0; required)
      - `gaps` (array of object, max 50000 items; required): Ordered by block_index, media_kind, block_id.
        - One of:
          - **media_kind: "speech"**
            - `block_id` (string, uuid; required)
            - `block_index` (integer, ≥ 0; required)
            - `block_version` (integer, ≥ 1; required)
            - `media_kind` ("speech"; required)
            - `reason` (string, one of `missing`, `stale`, `failed`, `unsupported`, `unassigned`, `unresolved`; required)
            - `source_ranges` (array of object, 1–10000 items; required)
              - `start_utf16` (integer, ≥ 0; required)
              - `end_utf16` (integer, ≥ 0; required)
          - **media_kind: "sfx"**
            - `block_id` (string, uuid; required)
            - `block_index` (integer, ≥ 0; required)
            - `block_version` (integer, ≥ 1; required)
            - `media_kind` ("sfx"; required)
            - `reason` (string, one of `missing`, `stale`, `failed`, `unsupported`, `unplaced`; required)
            - `source_ranges` (array of any, max 0 items; required)
    - `assets` (array of object, max 60000 items; required): Exactly one entry per distinct asset_id referenced by asset sequential units and overlay units.
      - `asset_id` (string, uuid; required)
      - `url` (string, uri; required): Short-lived signed inline URL.
      - `expires_at` (string, date-time; required)
      - `content_type` (string, min 1 chars; required)
      - `size_bytes` (integer, ≥ 1; required)
  - **schema_version: 2**: Opt-in playback payload with take-bound word timing sidecars. manifest_hash still hashes the unchanged audio manifest, not these optional sidecars.
    - `schema_version` (2; required)
    - `mastering_version` (integer, ≥ 1; required)
    - `manifest_hash` (string, pattern ^[a-f0-9]{64}$; required): SHA-256 of the canonical manifest.
    - `manifest` (object; required): Compiled playback timeline for one saved section document and speaker-registry version.
      - `schema_version` ("studio-render-manifest-2"; required)
      - `project_id` (string, uuid; required)
      - `section_id` (string, uuid; required)
      - `document_version` (integer, ≥ 1; required)
      - `speaker_registry_version` (integer, ≥ 1; required)
      - `mastering_version` (integer, ≥ 1; required)
      - `mastering_profile_hash` (string, pattern ^[a-f0-9]{64}$; required)
      - `mastering_profile` (object; required)
        - `schema_version` (1; required)
        - `enabled` (boolean; required)
        - `target_lufs` (number, -30–-10; required)
        - `strength` (number, 0–1; required)
        - `max_boost_db` (number, 0–24; required)
        - `max_reduction_db` (number, 0–24; required)
        - `silence_threshold_lufs` (number, -120–-20; required)
        - `peak_ceiling_db` (number, -12–-0.1; required)
        - `speaker_adjustments_db` (map of number; required)
        - `sfx_bus_gain_db` (number, -24–24, default 0; required)
      - `completeness` (string, one of `partial`, `complete`; required): complete exactly when gaps is empty.
      - `duration_ms` (integer, ≥ 0; required): Latest end point of any sequential or overlay unit.
      - `sequential_units` (array of object, max 50000 items; required): Contiguous from 0 ms in canonical block order; unit IDs are unique across sequential and overlay units.
        - One of:
          - **unit_type: "asset"**
            - `unit_id` (string, uuid; required)
            - `block_id` (string, uuid; required)
            - `block_index` (integer, ≥ 0; required)
            - `block_version` (integer, ≥ 1; required)
            - `block_kind` (string, one of `speech`, `narration`; required)
            - `start_ms` (integer, ≥ 0; required)
            - `duration_ms` (integer, ≥ 1; required)
            - `rendition_id` (string, uuid; required)
            - `rendition_intent_hash` (string, pattern ^[a-f0-9]{64}$; required)
            - `unit_type` ("asset"; required)
            - `asset_id` (string, uuid; required): Key into the payload's assets array.
            - `asset_content_hash` (string, pattern ^[a-f0-9]{64}$; required)
            - `source_ranges` (array of object, 1–10000 items; required)
              - `start_utf16` (integer, ≥ 0; required)
              - `end_utf16` (integer, ≥ 0; required)
            - `speaker_id` (string, uuid; required)
            - `analysis_integrated_lufs` (number, -120–12; required)
            - `analysis_sample_peak_dbfs` (number, -200–12; required)
            - `effective_gain_db` (number, -24–24; required)
          - **unit_type: "silence"**
            - `unit_id` (string, uuid; required)
            - `block_id` (string, uuid; required)
            - `block_index` (integer, ≥ 0; required)
            - `block_version` (integer, ≥ 1; required)
            - `block_kind` (string, one of `speech`, `narration`; required)
            - `start_ms` (integer, ≥ 0; required)
            - `duration_ms` (integer, ≥ 1; required)
            - `rendition_id` (string, uuid; required)
            - `rendition_intent_hash` (string, pattern ^[a-f0-9]{64}$; required)
            - `unit_type` ("silence"; required)
          - **unit_type: "spacing", spacing_kind: "inter_passage"**
            - `unit_id` (string, uuid; required)
            - `block_id` (string, uuid; required)
            - `block_index` (integer, ≥ 0; required)
            - `block_version` (integer, ≥ 1; required)
            - `block_kind` (string, one of `speech`, `narration`; required)
            - `start_ms` (integer, ≥ 0; required)
            - `duration_ms` (integer, ≥ 1; required)
            - `unit_type` ("spacing"; required)
            - `spacing_kind` ("inter_passage"; required)
      - `overlay_units` (array of object, max 10000 items; required): Ordered by start_ms, block_index, block_id, unit_id.
        - `unit_type` ("sfx"; required)
        - `unit_id` (string, uuid; required)
        - `block_id` (string, uuid; required)
        - `block_index` (integer, ≥ 0; required)
        - `block_version` (integer, ≥ 1; required)
        - `intent_hash` (string, pattern ^[a-f0-9]{64}$; required)
        - `asset_id` (string, uuid; required): Key into the payload's assets array.
        - `asset_content_hash` (string, pattern ^[a-f0-9]{64}$; required)
        - `start_ms` (integer, ≥ 0; required)
        - `duration_ms` (integer, ≥ 1; required)
        - `gain_db` (number, -60–24; required)
        - `fade_in_ms` (integer, ≥ 0; required)
        - `fade_out_ms` (integer, ≥ 0; required)
      - `timed_text_units` (array of object, max 50000 items; required): Ordered by start_ms, block_index, block_id, unit_id.
        - `unit_id` (string, uuid; required)
        - `block_id` (string, uuid; required)
        - `block_index` (integer, ≥ 0; required)
        - `block_version` (integer, ≥ 1; required)
        - `speaker_id` (string, uuid; required)
        - `start_ms` (integer, ≥ 0; required)
        - `duration_ms` (integer, ≥ 1; required)
        - `text` (string, 1–100000 chars; required)
        - `text_hash` (string, pattern ^[a-f0-9]{64}$; required)
        - `source_ranges` (array of object, 1–10000 items; required)
          - `start_utf16` (integer, ≥ 0; required)
          - `end_utf16` (integer, ≥ 0; required)
      - `gaps` (array of object, max 50000 items; required): Ordered by block_index, media_kind, block_id.
        - One of:
          - **media_kind: "speech"**
            - `block_id` (string, uuid; required)
            - `block_index` (integer, ≥ 0; required)
            - `block_version` (integer, ≥ 1; required)
            - `media_kind` ("speech"; required)
            - `reason` (string, one of `missing`, `stale`, `failed`, `unsupported`, `unassigned`, `unresolved`; required)
            - `source_ranges` (array of object, 1–10000 items; required)
              - `start_utf16` (integer, ≥ 0; required)
              - `end_utf16` (integer, ≥ 0; required)
          - **media_kind: "sfx"**
            - `block_id` (string, uuid; required)
            - `block_index` (integer, ≥ 0; required)
            - `block_version` (integer, ≥ 1; required)
            - `media_kind` ("sfx"; required)
            - `reason` (string, one of `missing`, `stale`, `failed`, `unsupported`, `unplaced`; required)
            - `source_ranges` (array of any, max 0 items; required)
    - `assets` (array of object, max 60000 items; required): Exactly one entry per distinct asset_id referenced by asset sequential units and overlay units.
      - `asset_id` (string, uuid; required)
      - `url` (string, uri; required): Short-lived signed inline URL.
      - `expires_at` (string, date-time; required)
      - `content_type` (string, min 1 chars; required)
      - `size_bytes` (integer, ≥ 1; required)
    - `unit_word_timings` (array of object, max 60000 items; required)
      - One of:
        - **word_timing: "provider"**
          - `unit_id` (string, uuid; required)
          - `block_id` (string, uuid; required)
          - `asset_id` (string, uuid; required)
          - `word_timing` ("provider"; required)
          - `words` (array of object, 1–10000 items; required)
            - `text` (string, 1–4096 chars; required)
            - `start_ms` (integer, ≥ 0; required)
            - `end_ms` (integer, ≥ 0; required)
        - **word_timing: "unavailable"**
          - `unit_id` (string, uuid; required)
          - `block_id` (string, uuid; required)
          - `asset_id` (string, uuid; required)
          - `word_timing` ("unavailable"; required)

Errors: `400`, `401`, `403`, `409`, `500`, `502`, `503` return the standard error envelope.

#### Example request

```bash
curl "https://lyricwinter.com/api/v1/studio/sections/$SECTION_ID/playback-manifest?document_version=1&speaker_registry_version=1" \
  -H "Authorization: Bearer $LYRICWINTER_API_KEY"
```

### Create a media access URL

`POST /studio/media-assets/{assetId}/access-url`

Scope: `studio:read` · Operation ID: `createStudioMediaAccessUrl`

Returns a signed URL for one audio asset: six hours for inline playback or twelve hours for download. The URL supports HTTP range requests, so audio players can seek.

#### Path parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `assetId` | string, uuid | Yes |  |

#### Request body (application/json, required)

- `schema_version` (1; required)
- `disposition` (string, one of `inline`, `attachment`; required)
- `filename` (string, 1–255 chars; optional)

#### Response 200

Short-lived signed media URL and asset metadata.

Fields inside `data`:

- `schema_version` (1; required)
- `asset_id` (string, uuid; required)
- `url` (string, uri; required)
- `expires_at` (string, date-time; required)
- `content_type` (string, min 1 chars; required)
- `size_bytes` (integer, ≥ 1; required)
- `disposition` (string, one of `inline`, `attachment`; required)

Errors: `400`, `401`, `403`, `404`, `409`, `500`, `503` return the standard error envelope.

#### Example request

```bash
curl -X POST "https://lyricwinter.com/api/v1/studio/media-assets/$ASSET_ID/access-url" \
  -H "Authorization: Bearer $LYRICWINTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "schema_version": 1,
  "disposition": "attachment",
  "filename": "Chapter-1.mp3"
}'
```

