API reference
Media and playback API
Read the generated audio state for a section, compile a playback manifest for the current document version, and mint short-lived URLs for individual media assets.
BetaUpdated
- Base URL
- https://lyricwinter.com/api/v1
- Authentication
- Authorization: Bearer lw_…
- Contract
- openapi.json
Get section media
GET/studio/sections/{sectionId}/media
studio:readReturns one entry per document block with its audio freshness, generation activity, and a short-lived signed playback_url for the current audio, plus the section's playback duration.
Path parameters
sectionIdstringrequireduuid
Response 200
Current block media read model. Fields below are inside data.
section_idstringrequireduuidplayback_duration_msinteger | nullrequired≥ 0Duration of the newest saved render manifest for the section's current document and speaker-registry versions; null when none exists.
blocksarray of objectrequiredOne entry per document block, in document order.
18 item attributes
block_idstringrequireduuidfreshnessstringrequiredabsent: 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.
One of
absentcurrentstaleunsupportedactivitystringrequiredgenerating: 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.
One of
idlegeneratingfailedactive_block_versioninteger | nullrequired≥ 1Block version the active rendition was requested for.
requested_block_versioninteger | nullrequired≥ 1Block version of the pending rendition or import, if any.
active_rendition_idstring | nullrequireduuidaudio_version_countintegerrequired≥ 0Number of completed audio versions for the block.
generation_batch_idstring | nullrequireduuidGeneration batch of the pending rendition.
playback_urlstring | nullrequireduriShort-lived signed URL for the active rendition's audio; null without an active asset.
playback_url_expires_atstring | nullrequireddate-timecontent_typestring | nullrequiredsize_bytesinteger | nullrequired≥ 1analysis_integrated_lufsnumber | nullrequired-120–24analysis_sample_peak_dbfsnumber | nullrequired-200–24providerstring | nullrequiredProvider that actually produced the active rendition (stored metadata for imports).
modelstring | nullrequiredModel that actually produced the active rendition (stored metadata for imports).
errorstring | nullrequiredLatest generation or import error message.
stale_reasonsarray of stringPresent for speech, narration, and sfx blocks; non-empty only when freshness is stale.
One of
block_kindtextpronunciationexcluded_texttimingvoicegeneration_profiledelivery_controlssound_effect
Errors 400, 401, 403, 404, 500, 503 use the standard error envelope.
curl "https://lyricwinter.com/api/v1/studio/sections/$SECTION_ID/media" \
-H "Authorization: Bearer $LYRICWINTER_API_KEY"const sectionId = "<sectionId>";
const response = await fetch(`https://lyricwinter.com/api/v1/studio/sections/${sectionId}/media`, {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.LYRICWINTER_API_KEY}`,
},
});
const { data, error, request_id } = await response.json();import os
import requests
section_id = "<sectionId>"
response = requests.request(
"GET",
f"https://lyricwinter.com/api/v1/studio/sections/{section_id}/media",
headers={
"Authorization": f"Bearer {os.environ['LYRICWINTER_API_KEY']}",
},
)
payload = response.json(){
"data": {
"section_id": "8f14e45f-ceea-4e7a-9c2d-3b1a5f2e7c10",
"playback_duration_ms": 1,
"blocks": [
{
"block_id": "c9f0f895-fb98-4b91-9e3f-1d2a6b7c8d20",
"freshness": "absent",
"activity": "idle",
"active_block_version": 1,
"requested_block_version": 1,
"active_rendition_id": "45c48cce-2e2d-4fbd-8a1c-5e6f7a8b9c30",
"audio_version_count": 1,
"generation_batch_id": "d3d94468-02a4-4c5e-b6f7-0a1b2c3d4e40",
"playback_url": "https://lyricwinter.com/…",
"playback_url_expires_at": "2026-09-30T17:00:00.000Z",
"content_type": "string",
"size_bytes": 1,
"analysis_integrated_lufs": -120,
"analysis_sample_peak_dbfs": -200,
"provider": "string",
"model": "string",
"error": "string",
"stale_reasons": [
"block_kind"
]
}
]
},
"request_id": "req_01J9Z3K8QF4"
}Get a playback manifest
GET/studio/sections/{sectionId}/playback-manifest
studio:readCompiles the section's current speech, pauses, and sound-effect overlays into one timeline with signed asset URLs for gapless playback. Pass the document_version and speaker_registry_version from the document; any other query parameter returns 400.
Path parameters
sectionIdstringrequireduuid
Query parameters
document_versionintegerrequired≥ 1Saved section document version to compile. If it no longer matches the saved section, the request fails with 409 STUDIO_PLAYBACK_STALE.
speaker_registry_versionintegerrequired≥ 1Saved 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 below are inside data.
schema_version1requiredmastering_versionintegerrequired≥ 1manifest_hashstringrequiredpattern ^[a-f0-9]{64}$SHA-256 of the canonical manifest.
manifestobjectrequiredCompiled playback timeline for one saved section document and speaker-registry version.
14 child attributes
schema_version"studio-render-manifest-2"requiredproject_idstringrequireduuidsection_idstringrequireduuiddocument_versionintegerrequired≥ 1speaker_registry_versionintegerrequired≥ 1mastering_versionintegerrequired≥ 1mastering_profile_hashstringrequiredpattern ^[a-f0-9]{64}$mastering_profileobjectrequired10 child attributes
schema_version1requiredenabledbooleanrequiredtarget_lufsnumberrequired-30–-10strengthnumberrequired0–1max_boost_dbnumberrequired0–24max_reduction_dbnumberrequired0–24silence_threshold_lufsnumberrequired-120–-20peak_ceiling_dbnumberrequired-12–-0.1speaker_adjustments_dbmap of numberrequiredsfx_bus_gain_dbnumberrequired-24–24default 0
completenessstringrequiredcomplete exactly when gaps is empty.
One of
partialcompleteduration_msintegerrequired≥ 0Latest end point of any sequential or overlay unit.
sequential_unitsarray of objectrequiredmax 50000 itemsContiguous from 0 ms in canonical block order; unit IDs are unique across sequential and overlay units.
One of 3 shapes:
unit_type: "asset"unit_idstringrequireduuidblock_idstringrequireduuidblock_indexintegerrequired≥ 0block_versionintegerrequired≥ 1block_kindstringrequiredOne of
speechnarrationstart_msintegerrequired≥ 0duration_msintegerrequired≥ 1rendition_idstringrequireduuidrendition_intent_hashstringrequiredpattern ^[a-f0-9]{64}$unit_type"asset"requiredasset_idstringrequireduuidKey into the payload's assets array.
asset_content_hashstringrequiredpattern ^[a-f0-9]{64}$source_rangesarray of objectrequired1–10000 items2 item attributes
start_utf16integerrequired≥ 0end_utf16integerrequired≥ 0
speaker_idstringrequireduuidanalysis_integrated_lufsnumberrequired-120–12analysis_sample_peak_dbfsnumberrequired-200–12effective_gain_dbnumberrequired-24–24
unit_type: "silence"unit_idstringrequireduuidblock_idstringrequireduuidblock_indexintegerrequired≥ 0block_versionintegerrequired≥ 1block_kindstringrequiredOne of
speechnarrationstart_msintegerrequired≥ 0duration_msintegerrequired≥ 1rendition_idstringrequireduuidrendition_intent_hashstringrequiredpattern ^[a-f0-9]{64}$unit_type"silence"required
unit_type: "spacing", spacing_kind: "inter_passage"unit_idstringrequireduuidblock_idstringrequireduuidblock_indexintegerrequired≥ 0block_versionintegerrequired≥ 1block_kindstringrequiredOne of
speechnarrationstart_msintegerrequired≥ 0duration_msintegerrequired≥ 1unit_type"spacing"requiredspacing_kind"inter_passage"required
overlay_unitsarray of objectrequiredmax 10000 itemsOrdered by start_ms, block_index, block_id, unit_id.
13 item attributes
unit_type"sfx"requiredunit_idstringrequireduuidblock_idstringrequireduuidblock_indexintegerrequired≥ 0block_versionintegerrequired≥ 1intent_hashstringrequiredpattern ^[a-f0-9]{64}$asset_idstringrequireduuidKey into the payload's assets array.
asset_content_hashstringrequiredpattern ^[a-f0-9]{64}$start_msintegerrequired≥ 0duration_msintegerrequired≥ 1gain_dbnumberrequired-60–24fade_in_msintegerrequired≥ 0fade_out_msintegerrequired≥ 0
timed_text_unitsarray of objectrequiredmax 50000 itemsOrdered by start_ms, block_index, block_id, unit_id.
10 item attributes
unit_idstringrequireduuidblock_idstringrequireduuidblock_indexintegerrequired≥ 0block_versionintegerrequired≥ 1speaker_idstringrequireduuidstart_msintegerrequired≥ 0duration_msintegerrequired≥ 1textstringrequired1–100000 charstext_hashstringrequiredpattern ^[a-f0-9]{64}$source_rangesarray of objectrequired1–10000 items2 item attributes
start_utf16integerrequired≥ 0end_utf16integerrequired≥ 0
gapsarray of objectrequiredmax 50000 itemsOrdered by block_index, media_kind, block_id.
One of 2 shapes:
media_kind: "speech"block_idstringrequireduuidblock_indexintegerrequired≥ 0block_versionintegerrequired≥ 1media_kind"speech"requiredreasonstringrequiredOne of
missingstalefailedunsupportedunassignedunresolvedsource_rangesarray of objectrequired1–10000 items2 item attributes
start_utf16integerrequired≥ 0end_utf16integerrequired≥ 0
media_kind: "sfx"block_idstringrequireduuidblock_indexintegerrequired≥ 0block_versionintegerrequired≥ 1media_kind"sfx"requiredreasonstringrequiredOne of
missingstalefailedunsupportedunplacedsource_rangesarray of anyrequiredmax 0 items
assetsarray of objectrequiredmax 60000 itemsExactly one entry per distinct asset_id referenced by asset sequential units and overlay units.
5 item attributes
asset_idstringrequireduuidurlstringrequireduriShort-lived signed inline URL.
expires_atstringrequireddate-timecontent_typestringrequiredmin 1 charssize_bytesintegerrequired≥ 1
Errors 400, 401, 403, 409, 500, 502, 503 use the standard error envelope.
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"const sectionId = "<sectionId>";
const response = await fetch(`https://lyricwinter.com/api/v1/studio/sections/${sectionId}/playback-manifest?document_version=1&speaker_registry_version=1`, {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.LYRICWINTER_API_KEY}`,
},
});
const { data, error, request_id } = await response.json();import os
import requests
section_id = "<sectionId>"
response = requests.request(
"GET",
f"https://lyricwinter.com/api/v1/studio/sections/{section_id}/playback-manifest?document_version=1&speaker_registry_version=1",
headers={
"Authorization": f"Bearer {os.environ['LYRICWINTER_API_KEY']}",
},
)
payload = response.json(){
"data": {
"schema_version": 1,
"mastering_version": 1,
"manifest_hash": "string",
"manifest": {
"schema_version": "studio-render-manifest-2",
"project_id": "8f14e45f-ceea-4e7a-9c2d-3b1a5f2e7c10",
"section_id": "c9f0f895-fb98-4b91-9e3f-1d2a6b7c8d20",
"document_version": 1,
"speaker_registry_version": 1,
"mastering_version": 1,
"mastering_profile_hash": "string",
"mastering_profile": {
"schema_version": 1,
"enabled": true,
"target_lufs": -30,
"strength": 0,
"max_boost_db": 0,
"max_reduction_db": 0,
"silence_threshold_lufs": -120,
"peak_ceiling_db": -12,
"speaker_adjustments_db": {},
"sfx_bus_gain_db": 0
},
"completeness": "partial",
"duration_ms": 1,
"sequential_units": [
{
"unit_id": "45c48cce-2e2d-4fbd-8a1c-5e6f7a8b9c30",
"block_id": "d3d94468-02a4-4c5e-b6f7-0a1b2c3d4e40",
"block_index": 1,
"block_version": 1,
"block_kind": "speech",
"start_ms": 1,
"duration_ms": 1,
"rendition_id": "8f14e45f-ceea-4e7a-9c2d-3b1a5f2e7c10",
"rendition_intent_hash": "string",
"unit_type": "asset",
"asset_id": "c9f0f895-fb98-4b91-9e3f-1d2a6b7c8d20",
"asset_content_hash": "string",
"source_ranges": [
{
"start_utf16": 1,
"end_utf16": 1
}
],
"speaker_id": "45c48cce-2e2d-4fbd-8a1c-5e6f7a8b9c30",
"analysis_integrated_lufs": -120,
"analysis_sample_peak_dbfs": -200,
"effective_gain_db": -24
}
],
"overlay_units": [
{
"unit_type": "sfx",
"unit_id": "d3d94468-02a4-4c5e-b6f7-0a1b2c3d4e40",
"block_id": "8f14e45f-ceea-4e7a-9c2d-3b1a5f2e7c10",
"block_index": 1,
"block_version": 1,
"intent_hash": "string",
"asset_id": "c9f0f895-fb98-4b91-9e3f-1d2a6b7c8d20",
"asset_content_hash": "string",
"start_ms": 1,
"duration_ms": 1,
"gain_db": -60,
"fade_in_ms": 1,
"fade_out_ms": 1
}
],
"timed_text_units": [
{
"unit_id": "45c48cce-2e2d-4fbd-8a1c-5e6f7a8b9c30",
"block_id": "d3d94468-02a4-4c5e-b6f7-0a1b2c3d4e40",
"block_index": 1,
"block_version": 1,
"speaker_id": "8f14e45f-ceea-4e7a-9c2d-3b1a5f2e7c10",
"start_ms": 1,
"duration_ms": 1,
"text": "A bell rang. \"Who is there?\" Mira asked.",
"text_hash": "string",
"source_ranges": [
{
"start_utf16": 1,
"end_utf16": 1
}
]
}
],
"gaps": [
{
"block_id": "c9f0f895-fb98-4b91-9e3f-1d2a6b7c8d20",
"block_index": 1,
"block_version": 1,
"media_kind": "speech",
"reason": "missing",
"source_ranges": [
{
"start_utf16": 1,
"end_utf16": 1
}
]
}
]
},
"assets": [
{
"asset_id": "45c48cce-2e2d-4fbd-8a1c-5e6f7a8b9c30",
"url": "https://lyricwinter.com/…",
"expires_at": "2026-09-30T17:00:00.000Z",
"content_type": "string",
"size_bytes": 1
}
]
},
"request_id": "req_01J9Z3K8QF4"
}Create a media access URL
POST/studio/media-assets/{assetId}/access-url
studio:readReturns 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
assetIdstringrequireduuid
Request body application/json
schema_version1requireddispositionstringrequiredOne of
inlineattachmentfilenamestring1–255 chars
Response 200
Short-lived signed media URL and asset metadata. Fields below are inside data.
schema_version1requiredasset_idstringrequireduuidurlstringrequireduriexpires_atstringrequireddate-timecontent_typestringrequiredmin 1 charssize_bytesintegerrequired≥ 1dispositionstringrequiredOne of
inlineattachment
Errors 400, 401, 403, 404, 409, 500, 503 use the standard error envelope.
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"
}'const assetId = "<assetId>";
const response = await fetch(`https://lyricwinter.com/api/v1/studio/media-assets/${assetId}/access-url`, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.LYRICWINTER_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"schema_version": 1,
"disposition": "attachment",
"filename": "Chapter-1.mp3"
}),
});
const { data, error, request_id } = await response.json();import os
import requests
asset_id = "<assetId>"
response = requests.request(
"POST",
f"https://lyricwinter.com/api/v1/studio/media-assets/{asset_id}/access-url",
headers={
"Authorization": f"Bearer {os.environ['LYRICWINTER_API_KEY']}",
},
json={
"schema_version": 1,
"disposition": "attachment",
"filename": "Chapter-1.mp3"
},
)
payload = response.json(){
"data": {
"schema_version": 1,
"asset_id": "8f14e45f-ceea-4e7a-9c2d-3b1a5f2e7c10",
"url": "https://lyricwinter.com/…",
"expires_at": "2026-09-30T17:00:00.000Z",
"content_type": "string",
"size_bytes": 1,
"disposition": "inline"
},
"request_id": "req_01J9Z3K8QF4"
}