---
title: "Projects API"
description: "A Studio project groups ordered sections (chapters) that share one cast of speakers. Standalone stories appear as unassigned stories until you turn them into a named project."
canonical_url: https://lyricwinter.com/docs/api/projects
markdown_url: https://lyricwinter.com/docs/api/projects.md
last_updated: 2026-09-30
status: beta
---
# LyricWinter API reference: Projects

> A Studio project groups ordered sections (chapters) that share one cast of speakers. Standalone stories appear as unassigned stories until you turn them into a named project.

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

- [List projects and standalone stories](https://lyricwinter.com/docs/api/projects#list-studio-projects): `GET /studio/projects`
- [Turn a standalone story into a project](https://lyricwinter.com/docs/api/projects#promote-studio-project): `PATCH /studio/projects/{projectId}`
- [Preview project deletion](https://lyricwinter.com/docs/api/projects#get-studio-project-deletion-info): `GET /studio/projects/{projectId}/deletion-info`
- [Delete a project](https://lyricwinter.com/docs/api/projects#delete-studio-project): `DELETE /studio/projects/{projectId}`

### List projects and standalone stories

`GET /studio/projects`

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

Returns named projects in `projects` and standalone stories in `unassigned_sections`. Every standalone story still has a `project_id` that works with project-scoped endpoints.

#### Response 200

Named projects and unassigned Studio story summaries.

Fields inside `data`:

- `projects` (array of object; required)
  - `id` (string, uuid; required)
  - `name` (string; required)
  - `project_kind` ("named"; required)
- `unassigned_sections` (array of object; required): Standalone Studio stories shown under the virtual Unassigned group, one section per hidden project.
  - `project_id` (string, uuid; required): Hidden standalone project container that owns the story.
  - `section` (object; required)
    - `id` (string, uuid; required)
    - `project_id` (string, uuid; required)
    - `title` (string; required)
    - `document_version` (integer, ≥ 1; required)
    - `source_text_length` (integer, ≥ 0; required): Length of the section's exact source-text projection in UTF-16 code units.
    - `activity_status` (string, one of `blank`, `working`, `needs_audio`, `audio_ready`, `audio_failed`; required): Navigator 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).
    - `created_at` (string, date-time; required)
    - `updated_at` (string, date-time; required)

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

#### Example request

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

### Turn a standalone story into a project

`PATCH /studio/projects/{projectId}`

Scope: `studio:write` · Operation ID: `promoteStudioProject`

Gives a standalone story a project name so you can add more sections to it. Its section, cast, audio, and share links are kept.

#### Path parameters

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

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

- `schema_version` (1; required)
- `name` (string, min 1 chars, pattern \S; required): Project name. Leading and trailing whitespace is trimmed; the trimmed name must be 1-120 characters and must not contain null characters or malformed Unicode.

#### Response 200

The promoted named project, or the existing project when it is already named with the same name.

Fields inside `data`:

- `project` (object; required)
  - `id` (string, uuid; required)
  - `name` (string; required)
  - `project_kind` ("named"; required)

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

#### Example request

```bash
curl -X PATCH "https://lyricwinter.com/api/v1/studio/projects/$PROJECT_ID" \
  -H "Authorization: Bearer $LYRICWINTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "schema_version": 1,
  "name": "My integration"
}'
```

### Preview project deletion

`GET /studio/projects/{projectId}/deletion-info`

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

Returns counts of the sections, script blocks, generated audio, and active share links that deleting the project would remove. Call it before deleting.

#### Path parameters

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

#### Response 200

Project deletion preview.

Fields inside `data`:

- `project_id` (string, uuid; required)
- `section_count` (integer, ≥ 0; required)
- `other_story_count` (integer, ≥ 0; required)
- `block_count` (integer, ≥ 0; required)
- `audio_run_count` (integer, ≥ 0; required)
- `clip_count` (integer, ≥ 0; required)
- `studio_media_count` (integer, ≥ 0; required)
- `active_share_link_count` (integer, ≥ 0; required)
- `ai_designed_voice_count` (integer, ≥ 0; required)

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

#### Example request

```bash
curl "https://lyricwinter.com/api/v1/studio/projects/$PROJECT_ID/deletion-info" \
  -H "Authorization: Bearer $LYRICWINTER_API_KEY"
```

### Delete a project

`DELETE /studio/projects/{projectId}`

Scope: `studio:write` · Operation ID: `deleteStudioProject`

Deletes a project with all of its sections, in-progress work, playback, and share links. This cannot be undone through the API.

#### Path parameters

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

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

- `schema_version` (1; required)
- `expected_project_kind` (string, one of `standalone`, `named`; required): The project kind the deleting client most recently observed. The server rejects deletion if the live kind has changed.
- `delete_ai_designed_voices` (boolean; required)

#### Response 200

Project deletion counts and optional AI-designed voice cleanup result.

Fields inside `data`:

- `success` (true; required)
- `project_id` (string, uuid; required)
- `mode` ("project_and_stories"; required)
- `stories_deleted` (integer, ≥ 0; required)
- `audio_runs_deleted` (integer, ≥ 0; required)
- `clips_deleted` (integer, ≥ 0; required)
- `public_stories_deleted` (integer, ≥ 0; required)
- `shared_stories_deleted` (integer, ≥ 0; required)
- `active_share_links_deleted` (integer, ≥ 0; required)
- `studio_sections_deleted` (integer, ≥ 0; required)
- `studio_media_marked_for_deletion` (integer, ≥ 0; required)
- `files_deleted` (integer, ≥ 0; required)
- `ai_designed_voices_deleted` (integer, ≥ 0; required)
- `ai_designed_voices_retained` (integer, ≥ 0; required)

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

#### Example request

```bash
curl -X DELETE "https://lyricwinter.com/api/v1/studio/projects/$PROJECT_ID" \
  -H "Authorization: Bearer $LYRICWINTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "schema_version": 1,
  "expected_project_kind": "standalone",
  "delete_ai_designed_voices": true
}'
```

