API reference
Sections API
A section is one chapter or scene of story text inside a project. Create a standalone story with one section, add sections to a project, reorder them, or delete them.
BetaUpdated
- Base URL
- https://lyricwinter.com/api/v1
- Authentication
- Authorization: Bearer lw_…
- Contract
- openapi.json
Create a standalone story
POST/studio/sections
studio:writeRetry with the same client_mutation_idCreates a standalone Studio story with one section containing the exact text you send. Returns 201 with the new project ID, section, and document, or 200 with idempotent_replay: true when the same client_mutation_id was already applied.
Request body application/json
schema_version1requiredactor_session_idstringrequireduuidclient_mutation_idstringrequireduuidIdempotency key; a replay with the same canonical request returns the committed result.
titlestringrequiredmax 500 charsAt most 500 UTF-16 code units. Must not contain null characters or malformed Unicode.
raw_textstringrequiredExact section text stored as one unparsed block. At most 8 MiB of UTF-8 (the whole request body is also limited to 8 MiB). Must not contain null characters or malformed Unicode.
Response 200
Idempotent replay of the existing standalone story. Fields below are inside data.
project_idstringrequireduuidsectionobjectrequired8 child attributes
idstringrequireduuidproject_idstringrequireduuidtitlestringrequireddocument_versionintegerrequired≥ 1source_text_lengthintegerrequired≥ 0Length of the section's exact source-text projection in UTF-16 code units.
activity_statusstringrequiredNavigator state: working (active workflow or pending audio), audio_failed (latest audio attempt failed), audio_ready (every generatable block has current audio and no unparsed text remains), needs_audio (generatable or unparsed text without complete current audio), or blank (nothing to generate).
One of
blankworkingneeds_audioaudio_readyaudio_failedcreated_atstringrequireddate-timeupdated_atstringrequireddate-time
documentobjectrequiredThe canonical structured script of one section (studio-ast-2): its versions, speakers, and ordered blocks.
Full shape: Get a section document
section_order_versionintegerrequired≥ 1speaker_registry_versionintegerrequired≥ 1idempotent_replaybooleanrequiredTrue when client_mutation_id matched an already committed creation (HTTP 200); false for a new section (HTTP 201).
Response 201
Standalone Studio story created. Fields below are inside data.
project_idstringrequireduuidsectionobjectrequired8 child attributes
idstringrequireduuidproject_idstringrequireduuidtitlestringrequireddocument_versionintegerrequired≥ 1source_text_lengthintegerrequired≥ 0Length of the section's exact source-text projection in UTF-16 code units.
activity_statusstringrequiredNavigator state: working (active workflow or pending audio), audio_failed (latest audio attempt failed), audio_ready (every generatable block has current audio and no unparsed text remains), needs_audio (generatable or unparsed text without complete current audio), or blank (nothing to generate).
One of
blankworkingneeds_audioaudio_readyaudio_failedcreated_atstringrequireddate-timeupdated_atstringrequireddate-time
documentobjectrequiredThe canonical structured script of one section (studio-ast-2): its versions, speakers, and ordered blocks.
Full shape: Get a section document
section_order_versionintegerrequired≥ 1speaker_registry_versionintegerrequired≥ 1idempotent_replaybooleanrequiredTrue when client_mutation_id matched an already committed creation (HTTP 200); false for a new section (HTTP 201).
Errors 400, 401, 409, 500 use the standard error envelope.
curl -X POST "https://lyricwinter.com/api/v1/studio/sections" \
-H "Authorization: Bearer $LYRICWINTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"schema_version": 1,
"actor_session_id": "8f14e45f-ceea-4e7a-9c2d-3b1a5f2e7c10",
"client_mutation_id": "c9f0f895-fb98-4b91-9e3f-1d2a6b7c8d20",
"title": "Chapter One",
"raw_text": "A bell rang. \"Who is there?\" Mira asked."
}'const response = await fetch(`https://lyricwinter.com/api/v1/studio/sections`, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.LYRICWINTER_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"schema_version": 1,
"actor_session_id": "8f14e45f-ceea-4e7a-9c2d-3b1a5f2e7c10",
"client_mutation_id": "c9f0f895-fb98-4b91-9e3f-1d2a6b7c8d20",
"title": "Chapter One",
"raw_text": "A bell rang. \"Who is there?\" Mira asked."
}),
});
const { data, error, request_id } = await response.json();import os
import requests
response = requests.request(
"POST",
f"https://lyricwinter.com/api/v1/studio/sections",
headers={
"Authorization": f"Bearer {os.environ['LYRICWINTER_API_KEY']}",
},
json={
"schema_version": 1,
"actor_session_id": "8f14e45f-ceea-4e7a-9c2d-3b1a5f2e7c10",
"client_mutation_id": "c9f0f895-fb98-4b91-9e3f-1d2a6b7c8d20",
"title": "Chapter One",
"raw_text": "A bell rang. \"Who is there?\" Mira asked."
},
)
payload = response.json(){
"data": {
"project_id": "8f14e45f-ceea-4e7a-9c2d-3b1a5f2e7c10",
"section": {
"id": "c9f0f895-fb98-4b91-9e3f-1d2a6b7c8d20",
"project_id": "45c48cce-2e2d-4fbd-8a1c-5e6f7a8b9c30",
"title": "Chapter One",
"document_version": 1,
"source_text_length": 1,
"activity_status": "blank",
"created_at": "2026-09-30T17:00:00.000Z",
"updated_at": "2026-09-30T17:00:00.000Z"
},
"document": {
"blocks": [],
"different_speaker_gap_ms": 1,
"document_version": 1,
"sfx_enabled": true,
"same_speaker_gap_ms": 1,
"schema_version": "studio-ast-2",
"section_id": "d3d94468-02a4-4c5e-b6f7-0a1b2c3d4e40",
"speaker_registry_version": 1,
"speakers": [],
"title": "Chapter One"
},
"section_order_version": 1,
"speaker_registry_version": 1,
"idempotent_replay": true
},
"request_id": "req_01J9Z3K8QF4"
}List project sections
GET/studio/projects/{projectId}/sections
studio:readReturns a project's sections in order with the project's section_order_version and speaker_registry_version. A project that has not been opened in Studio yet returns initialized: false.
Path parameters
projectIdstringrequireduuid
Response 200
Studio project state and sections. Fields below are inside data.
project_idstringrequireduuidinitializedbooleanrequiredFalse when the owned project has no Studio state yet; the nullable fields are then null and sections is empty.
casting_defaults_modestring | nullrequiredOne of
apply_exactsuggestignoredefault_automation_profilestring | nullrequiredOne of
quickbalanceddirectedsection_order_versioninteger | nullrequired≥ 1speaker_registry_versioninteger | nullrequired≥ 1sectionsarray of objectrequiredActive sections in project order.
8 item attributes
idstringrequireduuidproject_idstringrequireduuidtitlestringrequireddocument_versionintegerrequired≥ 1source_text_lengthintegerrequired≥ 0Length of the section's exact source-text projection in UTF-16 code units.
activity_statusstringrequiredNavigator state: working (active workflow or pending audio), audio_failed (latest audio attempt failed), audio_ready (every generatable block has current audio and no unparsed text remains), needs_audio (generatable or unparsed text without complete current audio), or blank (nothing to generate).
One of
blankworkingneeds_audioaudio_readyaudio_failedcreated_atstringrequireddate-timeupdated_atstringrequireddate-time
Errors 400, 401, 403, 500 use the standard error envelope.
curl "https://lyricwinter.com/api/v1/studio/projects/$PROJECT_ID/sections" \
-H "Authorization: Bearer $LYRICWINTER_API_KEY"const projectId = "<projectId>";
const response = await fetch(`https://lyricwinter.com/api/v1/studio/projects/${projectId}/sections`, {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.LYRICWINTER_API_KEY}`,
},
});
const { data, error, request_id } = await response.json();import os
import requests
project_id = "<projectId>"
response = requests.request(
"GET",
f"https://lyricwinter.com/api/v1/studio/projects/{project_id}/sections",
headers={
"Authorization": f"Bearer {os.environ['LYRICWINTER_API_KEY']}",
},
)
payload = response.json(){
"data": {
"project_id": "8f14e45f-ceea-4e7a-9c2d-3b1a5f2e7c10",
"initialized": true,
"casting_defaults_mode": "apply_exact",
"default_automation_profile": "quick",
"section_order_version": 1,
"speaker_registry_version": 1,
"sections": [
{
"id": "c9f0f895-fb98-4b91-9e3f-1d2a6b7c8d20",
"project_id": "45c48cce-2e2d-4fbd-8a1c-5e6f7a8b9c30",
"title": "Chapter One",
"document_version": 1,
"source_text_length": 1,
"activity_status": "blank",
"created_at": "2026-09-30T17:00:00.000Z",
"updated_at": "2026-09-30T17:00:00.000Z"
}
]
},
"request_id": "req_01J9Z3K8QF4"
}Add a section to a project
POST/studio/projects/{projectId}/sections
studio:writeRetry with the same client_mutation_idAdds one section of unstructured text to a project. Send the section_order_version you last read as base_section_order_version; a stale version returns 409.
Path parameters
projectIdstringrequireduuid
Request body application/json
schema_version1requiredactor_session_idstringrequireduuidclient_mutation_idstringrequireduuidIdempotency key; a replay with the same canonical request returns the committed result.
base_section_order_versioninteger | nullrequired≥ 1Section-order version the client last observed; null when the project has not initialized Studio.
titlestringrequiredmax 500 charsAt most 500 UTF-16 code units. Must not contain null characters or malformed Unicode.
raw_textstringrequiredExact section text stored as one unparsed block. At most 8 MiB of UTF-8 (the whole request body is also limited to 8 MiB). Must not contain null characters or malformed Unicode.
Response 200
Idempotent replay of the existing section. Fields below are inside data.
project_idstringrequireduuidsectionobjectrequired8 child attributes
idstringrequireduuidproject_idstringrequireduuidtitlestringrequireddocument_versionintegerrequired≥ 1source_text_lengthintegerrequired≥ 0Length of the section's exact source-text projection in UTF-16 code units.
activity_statusstringrequiredNavigator state: working (active workflow or pending audio), audio_failed (latest audio attempt failed), audio_ready (every generatable block has current audio and no unparsed text remains), needs_audio (generatable or unparsed text without complete current audio), or blank (nothing to generate).
One of
blankworkingneeds_audioaudio_readyaudio_failedcreated_atstringrequireddate-timeupdated_atstringrequireddate-time
documentobjectrequiredThe canonical structured script of one section (studio-ast-2): its versions, speakers, and ordered blocks.
Full shape: Get a section document
section_order_versionintegerrequired≥ 1speaker_registry_versionintegerrequired≥ 1idempotent_replaybooleanrequiredTrue when client_mutation_id matched an already committed creation (HTTP 200); false for a new section (HTTP 201).
Response 201
Studio section created. Fields below are inside data.
project_idstringrequireduuidsectionobjectrequired8 child attributes
idstringrequireduuidproject_idstringrequireduuidtitlestringrequireddocument_versionintegerrequired≥ 1source_text_lengthintegerrequired≥ 0Length of the section's exact source-text projection in UTF-16 code units.
activity_statusstringrequiredNavigator state: working (active workflow or pending audio), audio_failed (latest audio attempt failed), audio_ready (every generatable block has current audio and no unparsed text remains), needs_audio (generatable or unparsed text without complete current audio), or blank (nothing to generate).
One of
blankworkingneeds_audioaudio_readyaudio_failedcreated_atstringrequireddate-timeupdated_atstringrequireddate-time
documentobjectrequiredThe canonical structured script of one section (studio-ast-2): its versions, speakers, and ordered blocks.
Full shape: Get a section document
section_order_versionintegerrequired≥ 1speaker_registry_versionintegerrequired≥ 1idempotent_replaybooleanrequiredTrue when client_mutation_id matched an already committed creation (HTTP 200); false for a new section (HTTP 201).
Errors 400, 401, 403, 409, 500 use the standard error envelope.
curl -X POST "https://lyricwinter.com/api/v1/studio/projects/$PROJECT_ID/sections" \
-H "Authorization: Bearer $LYRICWINTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"schema_version": 1,
"actor_session_id": "8f14e45f-ceea-4e7a-9c2d-3b1a5f2e7c10",
"client_mutation_id": "c9f0f895-fb98-4b91-9e3f-1d2a6b7c8d20",
"base_section_order_version": 1,
"title": "Chapter One",
"raw_text": "A bell rang. \"Who is there?\" Mira asked."
}'const projectId = "<projectId>";
const response = await fetch(`https://lyricwinter.com/api/v1/studio/projects/${projectId}/sections`, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.LYRICWINTER_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"schema_version": 1,
"actor_session_id": "8f14e45f-ceea-4e7a-9c2d-3b1a5f2e7c10",
"client_mutation_id": "c9f0f895-fb98-4b91-9e3f-1d2a6b7c8d20",
"base_section_order_version": 1,
"title": "Chapter One",
"raw_text": "A bell rang. \"Who is there?\" Mira asked."
}),
});
const { data, error, request_id } = await response.json();import os
import requests
project_id = "<projectId>"
response = requests.request(
"POST",
f"https://lyricwinter.com/api/v1/studio/projects/{project_id}/sections",
headers={
"Authorization": f"Bearer {os.environ['LYRICWINTER_API_KEY']}",
},
json={
"schema_version": 1,
"actor_session_id": "8f14e45f-ceea-4e7a-9c2d-3b1a5f2e7c10",
"client_mutation_id": "c9f0f895-fb98-4b91-9e3f-1d2a6b7c8d20",
"base_section_order_version": 1,
"title": "Chapter One",
"raw_text": "A bell rang. \"Who is there?\" Mira asked."
},
)
payload = response.json(){
"data": {
"project_id": "8f14e45f-ceea-4e7a-9c2d-3b1a5f2e7c10",
"section": {
"id": "c9f0f895-fb98-4b91-9e3f-1d2a6b7c8d20",
"project_id": "45c48cce-2e2d-4fbd-8a1c-5e6f7a8b9c30",
"title": "Chapter One",
"document_version": 1,
"source_text_length": 1,
"activity_status": "blank",
"created_at": "2026-09-30T17:00:00.000Z",
"updated_at": "2026-09-30T17:00:00.000Z"
},
"document": {
"blocks": [],
"different_speaker_gap_ms": 1,
"document_version": 1,
"sfx_enabled": true,
"same_speaker_gap_ms": 1,
"schema_version": "studio-ast-2",
"section_id": "d3d94468-02a4-4c5e-b6f7-0a1b2c3d4e40",
"speaker_registry_version": 1,
"speakers": [],
"title": "Chapter One"
},
"section_order_version": 1,
"speaker_registry_version": 1,
"idempotent_replay": true
},
"request_id": "req_01J9Z3K8QF4"
}Move a section
PATCH/studio/projects/{projectId}/sections/{sectionId}
studio:writeRetry with the same client_mutation_idMoves a section to a new position in the project. Send the current section_order_version; a stale version returns 409.
Path parameters
projectIdstringrequireduuidsectionIdstringrequireduuid
Request body application/json
schema_version1requiredactor_session_idstringrequireduuidclient_mutation_idstringrequireduuidIdempotency key; a replay with the same canonical request returns the committed result.
base_section_order_versionintegerrequired≥ 1to_indexintegerrequired0–4999Zero-based target index in the active section order.
Response 200
Section move committed or replayed. Fields below are inside data.
operationstringrequiredmove_section for PATCH and delete_section for DELETE.
One of
move_sectiondelete_sectionproject_idstringrequireduuidsection_idstringrequireduuidsection_order_versionintegerrequired≥ 1Section-order version committed by this mutation.
idempotent_replaybooleanrequired
Errors 400, 401, 403, 404, 409, 500 use the standard error envelope.
curl -X PATCH "https://lyricwinter.com/api/v1/studio/projects/$PROJECT_ID/sections/$SECTION_ID" \
-H "Authorization: Bearer $LYRICWINTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"schema_version": 1,
"actor_session_id": "8f14e45f-ceea-4e7a-9c2d-3b1a5f2e7c10",
"client_mutation_id": "c9f0f895-fb98-4b91-9e3f-1d2a6b7c8d20",
"base_section_order_version": 1,
"to_index": 1
}'const projectId = "<projectId>";
const sectionId = "<sectionId>";
const response = await fetch(`https://lyricwinter.com/api/v1/studio/projects/${projectId}/sections/${sectionId}`, {
method: "PATCH",
headers: {
Authorization: `Bearer ${process.env.LYRICWINTER_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"schema_version": 1,
"actor_session_id": "8f14e45f-ceea-4e7a-9c2d-3b1a5f2e7c10",
"client_mutation_id": "c9f0f895-fb98-4b91-9e3f-1d2a6b7c8d20",
"base_section_order_version": 1,
"to_index": 1
}),
});
const { data, error, request_id } = await response.json();import os
import requests
project_id = "<projectId>"
section_id = "<sectionId>"
response = requests.request(
"PATCH",
f"https://lyricwinter.com/api/v1/studio/projects/{project_id}/sections/{section_id}",
headers={
"Authorization": f"Bearer {os.environ['LYRICWINTER_API_KEY']}",
},
json={
"schema_version": 1,
"actor_session_id": "8f14e45f-ceea-4e7a-9c2d-3b1a5f2e7c10",
"client_mutation_id": "c9f0f895-fb98-4b91-9e3f-1d2a6b7c8d20",
"base_section_order_version": 1,
"to_index": 1
},
)
payload = response.json(){
"data": {
"operation": "move_section",
"project_id": "8f14e45f-ceea-4e7a-9c2d-3b1a5f2e7c10",
"section_id": "c9f0f895-fb98-4b91-9e3f-1d2a6b7c8d20",
"section_order_version": 1,
"idempotent_replay": true
},
"request_id": "req_01J9Z3K8QF4"
}Preview section deletion
GET/studio/projects/{projectId}/sections/{sectionId}/deletion-info
studio:readReturns counts of the script blocks, generated clips, and active share links that deleting the section would make inaccessible.
Path parameters
projectIdstringrequireduuidsectionIdstringrequireduuid
Response 200
Section deletion preview. Fields below are inside data.
project_idstringrequireduuidsection_idstringrequireduuidblock_countintegerrequired≥ 0generated_clip_countintegerrequired≥ 0active_share_link_countintegerrequired≥ 0
Errors 400, 401, 403, 404, 500 use the standard error envelope.
curl "https://lyricwinter.com/api/v1/studio/projects/$PROJECT_ID/sections/$SECTION_ID/deletion-info" \
-H "Authorization: Bearer $LYRICWINTER_API_KEY"const projectId = "<projectId>";
const sectionId = "<sectionId>";
const response = await fetch(`https://lyricwinter.com/api/v1/studio/projects/${projectId}/sections/${sectionId}/deletion-info`, {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.LYRICWINTER_API_KEY}`,
},
});
const { data, error, request_id } = await response.json();import os
import requests
project_id = "<projectId>"
section_id = "<sectionId>"
response = requests.request(
"GET",
f"https://lyricwinter.com/api/v1/studio/projects/{project_id}/sections/{section_id}/deletion-info",
headers={
"Authorization": f"Bearer {os.environ['LYRICWINTER_API_KEY']}",
},
)
payload = response.json(){
"data": {
"project_id": "8f14e45f-ceea-4e7a-9c2d-3b1a5f2e7c10",
"section_id": "c9f0f895-fb98-4b91-9e3f-1d2a6b7c8d20",
"block_count": 1,
"generated_clip_count": 1,
"active_share_link_count": 1
},
"request_id": "req_01J9Z3K8QF4"
}Delete a section
DELETE/studio/projects/{projectId}/sections/{sectionId}
studio:writeRetry with the same client_mutation_idDeletes a section after checking both the section order version and the document version. This cannot be undone through the API.
Path parameters
projectIdstringrequireduuidsectionIdstringrequireduuid
Request body application/json
schema_version1requiredactor_session_idstringrequireduuidclient_mutation_idstringrequireduuidIdempotency key; a replay with the same canonical request returns the committed result.
base_section_order_versionintegerrequired≥ 1base_document_versionintegerrequired≥ 1
Response 200
Section deletion committed or replayed. Fields below are inside data.
operationstringrequiredmove_section for PATCH and delete_section for DELETE.
One of
move_sectiondelete_sectionproject_idstringrequireduuidsection_idstringrequireduuidsection_order_versionintegerrequired≥ 1Section-order version committed by this mutation.
idempotent_replaybooleanrequired
Errors 400, 401, 403, 404, 409, 500 use the standard error envelope.
curl -X DELETE "https://lyricwinter.com/api/v1/studio/projects/$PROJECT_ID/sections/$SECTION_ID" \
-H "Authorization: Bearer $LYRICWINTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"schema_version": 1,
"actor_session_id": "8f14e45f-ceea-4e7a-9c2d-3b1a5f2e7c10",
"client_mutation_id": "c9f0f895-fb98-4b91-9e3f-1d2a6b7c8d20",
"base_section_order_version": 1,
"base_document_version": 1
}'const projectId = "<projectId>";
const sectionId = "<sectionId>";
const response = await fetch(`https://lyricwinter.com/api/v1/studio/projects/${projectId}/sections/${sectionId}`, {
method: "DELETE",
headers: {
Authorization: `Bearer ${process.env.LYRICWINTER_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"schema_version": 1,
"actor_session_id": "8f14e45f-ceea-4e7a-9c2d-3b1a5f2e7c10",
"client_mutation_id": "c9f0f895-fb98-4b91-9e3f-1d2a6b7c8d20",
"base_section_order_version": 1,
"base_document_version": 1
}),
});
const { data, error, request_id } = await response.json();import os
import requests
project_id = "<projectId>"
section_id = "<sectionId>"
response = requests.request(
"DELETE",
f"https://lyricwinter.com/api/v1/studio/projects/{project_id}/sections/{section_id}",
headers={
"Authorization": f"Bearer {os.environ['LYRICWINTER_API_KEY']}",
},
json={
"schema_version": 1,
"actor_session_id": "8f14e45f-ceea-4e7a-9c2d-3b1a5f2e7c10",
"client_mutation_id": "c9f0f895-fb98-4b91-9e3f-1d2a6b7c8d20",
"base_section_order_version": 1,
"base_document_version": 1
},
)
payload = response.json(){
"data": {
"operation": "move_section",
"project_id": "8f14e45f-ceea-4e7a-9c2d-3b1a5f2e7c10",
"section_id": "c9f0f895-fb98-4b91-9e3f-1d2a6b7c8d20",
"section_order_version": 1,
"idempotent_replay": true
},
"request_id": "req_01J9Z3K8QF4"
}