<!-- Generated from openapi/v2.json (md/text-to-scene.md) — do not edit. Run: npm run generate -->

[Interactive reference](https://api-docs.syllaby.io/) · [OpenAPI 3.1 spec](https://api-docs.syllaby.io/openapi/v2.json) · [Docs index (llms.txt)](https://api-docs.syllaby.io/llms.txt)

# Text-to-Scene

A storyboard editor: generate, reorder, chain, and render scenes into a video.

## POST /text-to-scene

**Create a storyboard** (operationId: `createTextToScene`)

Creates a new Text-to-Scene storyboard in draft state. Choose `mode`: `wizard` (guided prompt-to-storyboard generation) or `manual` (build scenes yourself). Keep the returned `id`/`uuid` for follow-up calls.

**Requires an active subscription.**

### Request body

Required.

- `title` (string, required) — Display name of the storyboard (max 255 characters).
- `mode` (string, required) — Authoring mode: `wizard` (guided prompt-to-storyboard generation) or `manual` (you build scenes yourself). Allowed values: `wizard`, `manual`.

Example request:

```json
{
  "title": "Deep-sea documentary",
  "mode": "wizard"
}
```

### Response `201`

```json
{
  "message": "Success.",
  "status": 201,
  "data": {
    "id": 1,
    "uuid": "sb_abc123",
    "status": "wizard",
    "prompt": "A documentary about deep-sea creatures.",
    "settings": {
      "audio": true,
      "aspect_ratio": "16:9",
      "resolution": "1080p"
    },
    "duration": [
      5,
      6,
      5
    ],
    "video": {
      "id": 10,
      "user_id": 1,
      "idea_id": null,
      "scheduler_id": null,
      "title": "Deep-sea documentary",
      "type": "text_to_scene",
      "url": null,
      "status": "rendering",
      "retries": 0,
      "hash": "abc123",
      "synced_at": null,
      "metadata": {
        "ai_labels": true,
        "custom_description": null
      },
      "failure": null,
      "created_at": "2026-01-01T12:00:00.000000Z",
      "updated_at": "2026-01-01T12:00:00.000000Z"
    },
    "scenes": [
      {
        "id": 12,
        "uuid": "scn_abc123",
        "order": 0,
        "video_prompt": "Wide shot of a glowing coral reef.",
        "duration": 5,
        "status": "draft",
        "error": null,
        "created_at": "2026-01-01T12:00:00.000000Z",
        "updated_at": "2026-01-01T12:00:00.000000Z"
      }
    ],
    "created_at": "2026-01-01T12:00:00.000000Z",
    "updated_at": "2026-01-01T12:00:00.000000Z"
  }
}
```

### Errors

- `401` — Unauthenticated
- `403` — No active subscription. The v2 public API has no free tier — every faceless and preset endpoint (reads included) requires an active subscription. Unsubscribed, expired, or canceled callers are rejected with this `403` (code `SUBSCRIPTION-REQUIRED`) before any ownership, credit, or validation check. Only `GET /me`, `GET /credits/costs`, and `GET /credits/history` are reachable without one.
- `422` — Validation error
- `429` — Rate limit exceeded — requests are limited per API token (30 per minute by default). The response carries `Retry-After` (seconds to wait) and `X-RateLimit-Reset` (Unix timestamp when the window resets); back off until then and retry. Successful responses include `X-RateLimit-Limit` and `X-RateLimit-Remaining` so you can pace requests proactively.

Error shapes and examples are shared across endpoints — see the **Error responses** section in [getting-started](getting-started.md).

## POST /text-to-scene/{storyboard}/prompt

**Generate the storyboard from its prompt** (operationId: `generateTextToScenePrompt`)

💳 Charges credits. Kicks off asynchronous generation of the storyboard (subjects, hero image, scene prompts) from its source prompt. Returns HTTP 202; poll `GET /text-to-scene/{id}` and read `data.status`. Send the body as `application/json`, or as `multipart/form-data` to attach reference image files.

### Parameters

- `storyboard` (path, integer, required) — The storyboard ID Example: `1`

### Request body

Required.

- `prompt` (string, required) — The prompt describing the video to generate (max 5000 characters).
- `clip_engine_id` (integer, required) — Identifier of the text-to-video clip engine to use (see faceless options `clip_engines`).
- `duration` (object, required) — Target duration range for the generated video, in seconds.
  - `min` (integer, required) — Minimum total duration in seconds.
  - `max` (integer, required) — Maximum total duration in seconds.
- `settings` (object) — Optional render settings for the storyboard.
  - `audio` (boolean) — Whether the rendered video includes generated audio.
  - `aspect_ratio` (string) — Output aspect ratio (e.g. "16:9", "9:16").
  - `resolution` (string) — Output resolution (e.g. "720p", "1080p").
- `references` (array of string) — Optional reference images to steer the visual style — up to 5. Send as file parts via `multipart/form-data`.
- `context` (array of object) — Optional guided context Q&A pairs that shape the generation — up to 3.
  - `key` (string, required) — The context dimension this answer addresses. Allowed values: `purpose`, `style`, `audience`.
  - `question` (string, required) — The context question (max 500 characters).
  - `answer` (string, required) — The answer to the context question (max 500 characters).

Example request:

```json
{
  "prompt": "A documentary about deep-sea creatures.",
  "clip_engine_id": 9,
  "duration": {
    "min": 30,
    "max": 60
  }
}
```

### Response `202`

```json
{
  "message": "Success.",
  "status": 202,
  "data": {
    "id": 1,
    "uuid": "sb_abc123",
    "status": "generating:prompts",
    "prompt": "A documentary about deep-sea creatures.",
    "settings": {
      "audio": true,
      "aspect_ratio": "16:9",
      "resolution": "1080p"
    },
    "duration": [
      5,
      6,
      5
    ],
    "video": {
      "id": 10,
      "user_id": 1,
      "idea_id": null,
      "scheduler_id": null,
      "title": "Deep-sea documentary",
      "type": "text_to_scene",
      "url": null,
      "status": "rendering",
      "retries": 0,
      "hash": "abc123",
      "synced_at": null,
      "metadata": {
        "ai_labels": true,
        "custom_description": null
      },
      "failure": null,
      "created_at": "2026-01-01T12:00:00.000000Z",
      "updated_at": "2026-01-01T12:00:00.000000Z"
    },
    "scenes": [
      {
        "id": 12,
        "uuid": "scn_abc123",
        "order": 0,
        "video_prompt": "Wide shot of a glowing coral reef.",
        "duration": 5,
        "status": "draft",
        "error": null,
        "created_at": "2026-01-01T12:00:00.000000Z",
        "updated_at": "2026-01-01T12:00:00.000000Z"
      }
    ],
    "created_at": "2026-01-01T12:00:00.000000Z",
    "updated_at": "2026-01-01T12:00:00.000000Z"
  }
}
```

### Errors

- `401` — Unauthenticated
- `402` — Insufficient credits. The request is authenticated and valid, but the account balance is too low — top up and retry.
- `403` — Forbidden — two distinct causes share this status: 1. **No active subscription** (code `SUBSCRIPTION-REQUIRED`, message "An active subscription is required.") — checked first by the active-subscription gate, before any ownership or precondition logic. The v2 public API has no free tier. 2. **Authorization / ownership or precondition failure** — the subscription is active but the action is not allowed: the resource belongs to another account, or a render precondition is unmet (e.g. `A script is required before rendering.`, `Voice was not provided.`, the video is busy, or storage is full).
- `404` — Not found
- `422` — Validation error
- `429` — Rate limit exceeded — requests are limited per API token (30 per minute by default). The response carries `Retry-After` (seconds to wait) and `X-RateLimit-Reset` (Unix timestamp when the window resets); back off until then and retry. Successful responses include `X-RateLimit-Limit` and `X-RateLimit-Remaining` so you can pace requests proactively.

Error shapes and examples are shared across endpoints — see the **Error responses** section in [getting-started](getting-started.md).

## POST /text-to-scene/{storyboard}/context

**Generate narrative context** (operationId: `generateTextToSceneContext`)

💳 Charges credits. Expands the supplied prompt into narrative context used to guide storyboard generation. Returns the generated text under `data`.

### Parameters

- `storyboard` (path, integer, required) — The storyboard ID Example: `1`

### Request body

Required.

- `prompt` (string, required) — Prompt to expand into narrative context for the storyboard (max 5000 characters).

Example request:

```json
{
  "prompt": "A gripping documentary about the creatures of the deep ocean."
}
```

### Response `200`

```json
{
  "message": "Success.",
  "status": 200,
  "data": "In the crushing darkness of the abyss, life finds a way — bioluminescent hunters drift through waters no sunlight has ever touched…"
}
```

### Errors

- `401` — Unauthenticated
- `402` — Insufficient credits. The request is authenticated and valid, but the account balance is too low — top up and retry.
- `403` — Forbidden — two distinct causes share this status: 1. **No active subscription** (code `SUBSCRIPTION-REQUIRED`, message "An active subscription is required.") — checked first by the active-subscription gate, before any ownership or precondition logic. The v2 public API has no free tier. 2. **Authorization / ownership or precondition failure** — the subscription is active but the action is not allowed: the resource belongs to another account, or a render precondition is unmet (e.g. `A script is required before rendering.`, `Voice was not provided.`, the video is busy, or storage is full).
- `404` — Not found
- `422` — Validation error
- `429` — Rate limit exceeded — requests are limited per API token (30 per minute by default). The response carries `Retry-After` (seconds to wait) and `X-RateLimit-Reset` (Unix timestamp when the window resets); back off until then and retry. Successful responses include `X-RateLimit-Limit` and `X-RateLimit-Remaining` so you can pace requests proactively.

Error shapes and examples are shared across endpoints — see the **Error responses** section in [getting-started](getting-started.md).

## GET /text-to-scene/{id}

**Get a storyboard** (operationId: `getTextToScene`)

Returns a single storyboard you own, including its scenes and embedded render state under `data.video`. Responds with `404` when the id does not exist or belongs to another account.

### Parameters

- `id` (path, string, required) — Identifier of the faceless video. Example: `1`

### Response `200`

```json
{
  "message": "Success.",
  "status": 200,
  "data": {
    "id": 1,
    "uuid": "sb_abc123",
    "status": "ready",
    "prompt": "A documentary about deep-sea creatures.",
    "settings": {
      "audio": true,
      "aspect_ratio": "16:9",
      "resolution": "1080p"
    },
    "duration": [
      5,
      6,
      5
    ],
    "video": {
      "id": 10,
      "user_id": 1,
      "idea_id": null,
      "scheduler_id": null,
      "title": "Deep-sea documentary",
      "type": "text_to_scene",
      "url": null,
      "status": "rendering",
      "retries": 0,
      "hash": "abc123",
      "synced_at": null,
      "metadata": {
        "ai_labels": true,
        "custom_description": null
      },
      "failure": null,
      "created_at": "2026-01-01T12:00:00.000000Z",
      "updated_at": "2026-01-01T12:00:00.000000Z"
    },
    "scenes": [
      {
        "id": 12,
        "uuid": "scn_abc123",
        "order": 0,
        "video_prompt": "Wide shot of a glowing coral reef.",
        "duration": 5,
        "status": "draft",
        "error": null,
        "created_at": "2026-01-01T12:00:00.000000Z",
        "updated_at": "2026-01-01T12:00:00.000000Z"
      }
    ],
    "created_at": "2026-01-01T12:00:00.000000Z",
    "updated_at": "2026-01-01T12:00:00.000000Z"
  }
}
```

### Errors

- `401` — Unauthenticated
- `403` — No active subscription. The v2 public API has no free tier — every faceless and preset endpoint (reads included) requires an active subscription. Unsubscribed, expired, or canceled callers are rejected with this `403` (code `SUBSCRIPTION-REQUIRED`) before any ownership, credit, or validation check. Only `GET /me`, `GET /credits/costs`, and `GET /credits/history` are reachable without one.
- `404` — Not found
- `429` — Rate limit exceeded — requests are limited per API token (30 per minute by default). The response carries `Retry-After` (seconds to wait) and `X-RateLimit-Reset` (Unix timestamp when the window resets); back off until then and retry. Successful responses include `X-RateLimit-Limit` and `X-RateLimit-Remaining` so you can pace requests proactively.

Error shapes and examples are shared across endpoints — see the **Error responses** section in [getting-started](getting-started.md).

## PATCH /text-to-scene/{id}

**Update a storyboard** (operationId: `updateTextToScene`)

Updates the storyboard's render settings (audio, aspect ratio, resolution). Only the fields you send are changed.

### Parameters

- `id` (path, string, required) — Identifier of the faceless video. Example: `1`

### Request body

Optional.

- `settings` (object) — Render settings for the storyboard. Only the fields you send are changed.
  - `audio` (boolean) — Whether the rendered video includes generated audio.
  - `aspect_ratio` (string) — Output aspect ratio (e.g. "16:9", "9:16").
  - `resolution` (string) — Output resolution (e.g. "720p", "1080p").

Example request:

```json
{
  "settings": {
    "audio": true,
    "aspect_ratio": "16:9",
    "resolution": "1080p"
  }
}
```

### Response `200`

```json
{
  "message": "Success.",
  "status": 200,
  "data": {
    "id": 1,
    "uuid": "sb_abc123",
    "status": "ready",
    "prompt": "A documentary about deep-sea creatures.",
    "settings": {
      "audio": true,
      "aspect_ratio": "16:9",
      "resolution": "1080p"
    },
    "duration": [
      5,
      6,
      5
    ],
    "video": {
      "id": 10,
      "user_id": 1,
      "idea_id": null,
      "scheduler_id": null,
      "title": "Deep-sea documentary",
      "type": "text_to_scene",
      "url": null,
      "status": "rendering",
      "retries": 0,
      "hash": "abc123",
      "synced_at": null,
      "metadata": {
        "ai_labels": true,
        "custom_description": null
      },
      "failure": null,
      "created_at": "2026-01-01T12:00:00.000000Z",
      "updated_at": "2026-01-01T12:00:00.000000Z"
    },
    "scenes": [
      {
        "id": 12,
        "uuid": "scn_abc123",
        "order": 0,
        "video_prompt": "Wide shot of a glowing coral reef.",
        "duration": 5,
        "status": "draft",
        "error": null,
        "created_at": "2026-01-01T12:00:00.000000Z",
        "updated_at": "2026-01-01T12:00:00.000000Z"
      }
    ],
    "created_at": "2026-01-01T12:00:00.000000Z",
    "updated_at": "2026-01-01T12:00:00.000000Z"
  }
}
```

### Errors

- `401` — Unauthenticated
- `403` — No active subscription. The v2 public API has no free tier — every faceless and preset endpoint (reads included) requires an active subscription. Unsubscribed, expired, or canceled callers are rejected with this `403` (code `SUBSCRIPTION-REQUIRED`) before any ownership, credit, or validation check. Only `GET /me`, `GET /credits/costs`, and `GET /credits/history` are reachable without one.
- `404` — Not found
- `422` — Validation error
- `429` — Rate limit exceeded — requests are limited per API token (30 per minute by default). The response carries `Retry-After` (seconds to wait) and `X-RateLimit-Reset` (Unix timestamp when the window resets); back off until then and retry. Successful responses include `X-RateLimit-Limit` and `X-RateLimit-Remaining` so you can pace requests proactively.

Error shapes and examples are shared across endpoints — see the **Error responses** section in [getting-started](getting-started.md).

## POST /text-to-scene/{storyboard}/scenes

**Add a scene** (operationId: `createTextToSceneScene`)

Adds a new scene to the storyboard at the given order with a video prompt and duration.

### Parameters

- `storyboard` (path, integer, required) — The storyboard ID Example: `1`

### Request body

Optional.

- `order` (integer) — Zero-based position of the scene within the storyboard.
- `raw_video_prompt` (string) — The scene's video prompt (max 5000 characters).
- `duration` (integer) — Scene duration in seconds (at least 1).

Example request:

```json
{
  "order": 0,
  "raw_video_prompt": "Wide shot of a glowing coral reef.",
  "duration": 5
}
```

### Response `201`

```json
{
  "message": "Success.",
  "status": 201,
  "data": {
    "id": 12,
    "uuid": "scn_abc123",
    "order": 0,
    "video_prompt": "Wide shot of a glowing coral reef.",
    "duration": 5,
    "status": "draft",
    "error": null,
    "created_at": "2026-01-01T12:00:00.000000Z",
    "updated_at": "2026-01-01T12:00:00.000000Z"
  }
}
```

### Errors

- `401` — Unauthenticated
- `403` — Forbidden — two distinct causes share this status: 1. **No active subscription** (code `SUBSCRIPTION-REQUIRED`, message "An active subscription is required.") — checked first by the active-subscription gate, before any ownership or precondition logic. The v2 public API has no free tier. 2. **Authorization / ownership or precondition failure** — the subscription is active but the action is not allowed: the resource belongs to another account, or a render precondition is unmet (e.g. `A script is required before rendering.`, `Voice was not provided.`, the video is busy, or storage is full).
- `404` — Not found
- `422` — Validation error
- `429` — Rate limit exceeded — requests are limited per API token (30 per minute by default). The response carries `Retry-After` (seconds to wait) and `X-RateLimit-Reset` (Unix timestamp when the window resets); back off until then and retry. Successful responses include `X-RateLimit-Limit` and `X-RateLimit-Remaining` so you can pace requests proactively.

Error shapes and examples are shared across endpoints — see the **Error responses** section in [getting-started](getting-started.md).

## PATCH /text-to-scene/{storyboard}/scenes/{scene}

**Update a scene** (operationId: `updateTextToSceneScene`)

Updates a scene's order, video prompt, or duration. Only the fields you send are changed.

### Parameters

- `storyboard` (path, integer, required) — The storyboard ID Example: `1`
- `scene` (path, integer, required) — The scene ID Example: `12`

### Request body

Optional.

- `order` (integer) — Zero-based position of the scene within the storyboard.
- `raw_video_prompt` (string) — The scene's video prompt (max 5000 characters).
- `duration` (integer) — Scene duration in seconds (at least 1).

Example request:

```json
{
  "raw_video_prompt": "Close-up of a translucent jellyfish pulsing with light.",
  "duration": 6
}
```

### Response `200`

```json
{
  "message": "Success.",
  "status": 200,
  "data": {
    "id": 12,
    "uuid": "scn_abc123",
    "order": 0,
    "video_prompt": "Wide shot of a glowing coral reef.",
    "duration": 5,
    "status": "draft",
    "error": null,
    "created_at": "2026-01-01T12:00:00.000000Z",
    "updated_at": "2026-01-01T12:00:00.000000Z"
  }
}
```

### Errors

- `401` — Unauthenticated
- `403` — Forbidden — two distinct causes share this status: 1. **No active subscription** (code `SUBSCRIPTION-REQUIRED`, message "An active subscription is required.") — checked first by the active-subscription gate, before any ownership or precondition logic. The v2 public API has no free tier. 2. **Authorization / ownership or precondition failure** — the subscription is active but the action is not allowed: the resource belongs to another account, or a render precondition is unmet (e.g. `A script is required before rendering.`, `Voice was not provided.`, the video is busy, or storage is full).
- `404` — Not found
- `422` — Validation error
- `429` — Rate limit exceeded — requests are limited per API token (30 per minute by default). The response carries `Retry-After` (seconds to wait) and `X-RateLimit-Reset` (Unix timestamp when the window resets); back off until then and retry. Successful responses include `X-RateLimit-Limit` and `X-RateLimit-Remaining` so you can pace requests proactively.

Error shapes and examples are shared across endpoints — see the **Error responses** section in [getting-started](getting-started.md).

## DELETE /text-to-scene/{storyboard}/scenes/{scene}

**Delete a scene** (operationId: `deleteTextToSceneScene`)

Deletes a scene from the storyboard. Returns HTTP 202 while related cleanup runs asynchronously.

### Parameters

- `storyboard` (path, integer, required) — The storyboard ID Example: `1`
- `scene` (path, integer, required) — The scene ID Example: `12`

### Response `202`

```json
{
  "message": "Success.",
  "status": 202,
  "data": {
    "message": "Accepted"
  }
}
```

### Errors

- `401` — Unauthenticated
- `403` — Forbidden — two distinct causes share this status: 1. **No active subscription** (code `SUBSCRIPTION-REQUIRED`, message "An active subscription is required.") — checked first by the active-subscription gate, before any ownership or precondition logic. The v2 public API has no free tier. 2. **Authorization / ownership or precondition failure** — the subscription is active but the action is not allowed: the resource belongs to another account, or a render precondition is unmet (e.g. `A script is required before rendering.`, `Voice was not provided.`, the video is busy, or storage is full).
- `404` — Not found
- `429` — Rate limit exceeded — requests are limited per API token (30 per minute by default). The response carries `Retry-After` (seconds to wait) and `X-RateLimit-Reset` (Unix timestamp when the window resets); back off until then and retry. Successful responses include `X-RateLimit-Limit` and `X-RateLimit-Remaining` so you can pace requests proactively.

Error shapes and examples are shared across endpoints — see the **Error responses** section in [getting-started](getting-started.md).

## GET /text-to-scene/{storyboard}/scenes/{scene}/status

**Get a scene's status** (operationId: `getTextToSceneSceneStatus`)

Returns the current clip-generation status of a single scene — a lightweight endpoint for polling while a scene's clip is generating.

### Parameters

- `storyboard` (path, integer, required) — The storyboard ID Example: `1`
- `scene` (path, integer, required) — The scene ID Example: `12`

### Response `200`

```json
{
  "message": "Success.",
  "status": 200,
  "data": {
    "id": 12,
    "status": "clip:generating",
    "error": null
  }
}
```

### Errors

- `401` — Unauthenticated
- `403` — Forbidden — two distinct causes share this status: 1. **No active subscription** (code `SUBSCRIPTION-REQUIRED`, message "An active subscription is required.") — checked first by the active-subscription gate, before any ownership or precondition logic. The v2 public API has no free tier. 2. **Authorization / ownership or precondition failure** — the subscription is active but the action is not allowed: the resource belongs to another account, or a render precondition is unmet (e.g. `A script is required before rendering.`, `Voice was not provided.`, the video is busy, or storage is full).
- `404` — Not found
- `429` — Rate limit exceeded — requests are limited per API token (30 per minute by default). The response carries `Retry-After` (seconds to wait) and `X-RateLimit-Reset` (Unix timestamp when the window resets); back off until then and retry. Successful responses include `X-RateLimit-Limit` and `X-RateLimit-Remaining` so you can pace requests proactively.

Error shapes and examples are shared across endpoints — see the **Error responses** section in [getting-started](getting-started.md).

## PUT /text-to-scene/{storyboard}/scenes/sort

**Reorder scenes** (operationId: `sortTextToSceneScenes`)

Moves a scene to follow another scene (or to the start when `after_id` is null), reordering the storyboard.

### Parameters

- `storyboard` (path, integer, required) — The storyboard ID Example: `1`

### Request body

Required.

- `scene_id` (integer, required) — Identifier of the scene to move.
- `after_id` (integer|null) — Identifier of the scene it should follow, or null to move it to the start.

Example request:

```json
{
  "scene_id": 12,
  "after_id": 8
}
```

### Response `200`

```json
{
  "message": "Success.",
  "status": 200,
  "data": {
    "message": "Scenes sorted successfully"
  }
}
```

### Errors

- `401` — Unauthenticated
- `403` — Forbidden — two distinct causes share this status: 1. **No active subscription** (code `SUBSCRIPTION-REQUIRED`, message "An active subscription is required.") — checked first by the active-subscription gate, before any ownership or precondition logic. The v2 public API has no free tier. 2. **Authorization / ownership or precondition failure** — the subscription is active but the action is not allowed: the resource belongs to another account, or a render precondition is unmet (e.g. `A script is required before rendering.`, `Voice was not provided.`, the video is busy, or storage is full).
- `404` — Not found
- `422` — Validation error
- `429` — Rate limit exceeded — requests are limited per API token (30 per minute by default). The response carries `Retry-After` (seconds to wait) and `X-RateLimit-Reset` (Unix timestamp when the window resets); back off until then and retry. Successful responses include `X-RateLimit-Limit` and `X-RateLimit-Remaining` so you can pace requests proactively.

Error shapes and examples are shared across endpoints — see the **Error responses** section in [getting-started](getting-started.md).

## PUT /text-to-scene/{storyboard}/scenes/{scene}/frames

**Assign scene frames** (operationId: `assignTextToSceneFrames`)

Sets (or clears) the first and/or last frame assets for a scene. Pass null to clear a frame.

### Parameters

- `storyboard` (path, integer, required) — The storyboard ID Example: `1`
- `scene` (path, integer, required) — The scene ID Example: `12`

### Request body

Optional.

- `first_frame_id` (integer|null) — Asset id to use as the scene's first frame, or null to clear it.
- `last_frame_id` (integer|null) — Asset id to use as the scene's last frame, or null to clear it.

Example request:

```json
{
  "first_frame_id": 101,
  "last_frame_id": 102
}
```

### Response `200`

```json
{
  "message": "Success.",
  "status": 200,
  "data": {
    "id": 12,
    "uuid": "scn_abc123",
    "order": 0,
    "video_prompt": "Wide shot of a glowing coral reef.",
    "duration": 5,
    "status": "draft",
    "error": null,
    "created_at": "2026-01-01T12:00:00.000000Z",
    "updated_at": "2026-01-01T12:00:00.000000Z"
  }
}
```

### Errors

- `401` — Unauthenticated
- `403` — Forbidden — two distinct causes share this status: 1. **No active subscription** (code `SUBSCRIPTION-REQUIRED`, message "An active subscription is required.") — checked first by the active-subscription gate, before any ownership or precondition logic. The v2 public API has no free tier. 2. **Authorization / ownership or precondition failure** — the subscription is active but the action is not allowed: the resource belongs to another account, or a render precondition is unmet (e.g. `A script is required before rendering.`, `Voice was not provided.`, the video is busy, or storage is full).
- `404` — Not found
- `422` — Validation error
- `429` — Rate limit exceeded — requests are limited per API token (30 per minute by default). The response carries `Retry-After` (seconds to wait) and `X-RateLimit-Reset` (Unix timestamp when the window resets); back off until then and retry. Successful responses include `X-RateLimit-Limit` and `X-RateLimit-Remaining` so you can pace requests proactively.

Error shapes and examples are shared across endpoints — see the **Error responses** section in [getting-started](getting-started.md).

## PUT /text-to-scene/{storyboard}/scenes/{scene}/chain

**Chain a scene** (operationId: `chainTextToSceneScene`)

Chains this scene to another scene so the referenced scene's last frame seeds this scene's first frame, keeping motion continuous.

### Parameters

- `storyboard` (path, integer, required) — The storyboard ID Example: `1`
- `scene` (path, integer, required) — The scene ID Example: `12`

### Request body

Required.

- `scene_id` (integer, required) — Identifier of the scene to chain to (its last frame seeds this scene's first frame).

Example request:

```json
{
  "scene_id": 11
}
```

### Response `200`

```json
{
  "message": "Success.",
  "status": 200,
  "data": {
    "id": 12,
    "uuid": "scn_abc123",
    "order": 0,
    "video_prompt": "Wide shot of a glowing coral reef.",
    "duration": 5,
    "status": "draft",
    "error": null,
    "created_at": "2026-01-01T12:00:00.000000Z",
    "updated_at": "2026-01-01T12:00:00.000000Z"
  }
}
```

### Errors

- `401` — Unauthenticated
- `403` — Forbidden — two distinct causes share this status: 1. **No active subscription** (code `SUBSCRIPTION-REQUIRED`, message "An active subscription is required.") — checked first by the active-subscription gate, before any ownership or precondition logic. The v2 public API has no free tier. 2. **Authorization / ownership or precondition failure** — the subscription is active but the action is not allowed: the resource belongs to another account, or a render precondition is unmet (e.g. `A script is required before rendering.`, `Voice was not provided.`, the video is busy, or storage is full).
- `404` — Not found
- `422` — Validation error
- `429` — Rate limit exceeded — requests are limited per API token (30 per minute by default). The response carries `Retry-After` (seconds to wait) and `X-RateLimit-Reset` (Unix timestamp when the window resets); back off until then and retry. Successful responses include `X-RateLimit-Limit` and `X-RateLimit-Remaining` so you can pace requests proactively.

Error shapes and examples are shared across endpoints — see the **Error responses** section in [getting-started](getting-started.md).

## DELETE /text-to-scene/{storyboard}/scenes/{scene}/chain

**Unchain a scene** (operationId: `unchainTextToSceneScene`)

Removes the chaining link from a scene so its first frame is no longer seeded by another scene.

### Parameters

- `storyboard` (path, integer, required) — The storyboard ID Example: `1`
- `scene` (path, integer, required) — The scene ID Example: `12`

### Response `200`

```json
{
  "message": "Success.",
  "status": 200,
  "data": {
    "id": 12,
    "uuid": "scn_abc123",
    "order": 0,
    "video_prompt": "Wide shot of a glowing coral reef.",
    "duration": 5,
    "status": "draft",
    "error": null,
    "created_at": "2026-01-01T12:00:00.000000Z",
    "updated_at": "2026-01-01T12:00:00.000000Z"
  }
}
```

### Errors

- `401` — Unauthenticated
- `403` — Forbidden — two distinct causes share this status: 1. **No active subscription** (code `SUBSCRIPTION-REQUIRED`, message "An active subscription is required.") — checked first by the active-subscription gate, before any ownership or precondition logic. The v2 public API has no free tier. 2. **Authorization / ownership or precondition failure** — the subscription is active but the action is not allowed: the resource belongs to another account, or a render precondition is unmet (e.g. `A script is required before rendering.`, `Voice was not provided.`, the video is busy, or storage is full).
- `404` — Not found
- `429` — Rate limit exceeded — requests are limited per API token (30 per minute by default). The response carries `Retry-After` (seconds to wait) and `X-RateLimit-Reset` (Unix timestamp when the window resets); back off until then and retry. Successful responses include `X-RateLimit-Limit` and `X-RateLimit-Remaining` so you can pace requests proactively.

Error shapes and examples are shared across endpoints — see the **Error responses** section in [getting-started](getting-started.md).

## POST /text-to-scene/{storyboard}/clips

**Generate scene clips** (operationId: `generateTextToSceneClips`)

💳 Charges credits. Queues clip generation for the given scenes (optionally enhancing each scene's prompt first). Returns HTTP 202; poll each scene's status or `GET /text-to-scene/{id}`.

### Parameters

- `storyboard` (path, integer, required) — The storyboard ID Example: `1`

### Request body

Required.

- `scenes` (array of object, required) — The scenes to generate clips for (at least one).
  - `id` (integer, required) — Identifier of a scene belonging to this storyboard.
  - `enhance` (boolean, required) — Whether to enhance the scene's prompt before generating its clip.

Example request:

```json
{
  "scenes": [
    {
      "id": 12,
      "enhance": true
    }
  ]
}
```

### Response `202`

```json
{
  "message": "Success.",
  "status": 202,
  "data": [
    {
      "id": 12,
      "uuid": "scn_abc123",
      "order": 0,
      "video_prompt": "Wide shot of a glowing coral reef.",
      "duration": 5,
      "status": "clip:queued",
      "error": null,
      "created_at": "2026-01-01T12:00:00.000000Z",
      "updated_at": "2026-01-01T12:00:00.000000Z"
    }
  ]
}
```

### Errors

- `401` — Unauthenticated
- `402` — Insufficient credits. The request is authenticated and valid, but the account balance is too low — top up and retry.
- `403` — Forbidden — two distinct causes share this status: 1. **No active subscription** (code `SUBSCRIPTION-REQUIRED`, message "An active subscription is required.") — checked first by the active-subscription gate, before any ownership or precondition logic. The v2 public API has no free tier. 2. **Authorization / ownership or precondition failure** — the subscription is active but the action is not allowed: the resource belongs to another account, or a render precondition is unmet (e.g. `A script is required before rendering.`, `Voice was not provided.`, the video is busy, or storage is full).
- `404` — Not found
- `422` — Validation error
- `429` — Rate limit exceeded — requests are limited per API token (30 per minute by default). The response carries `Retry-After` (seconds to wait) and `X-RateLimit-Reset` (Unix timestamp when the window resets); back off until then and retry. Successful responses include `X-RateLimit-Limit` and `X-RateLimit-Remaining` so you can pace requests proactively.

Error shapes and examples are shared across endpoints — see the **Error responses** section in [getting-started](getting-started.md).

## POST /text-to-scene/{storyboard}/moodboard/upload

**Add moodboard references** (operationId: `uploadTextToSceneMoodboard`)

Attaches reference imagery to the storyboard's moodboard to steer its visual style. Returns the moodboard entries.

### Parameters

- `storyboard` (path, integer, required) — The storyboard ID Example: `1`

### Request body

Required.

- `prompt` (string, required) — Prompt describing the moodboard imagery to generate (max 5000 characters).
- `enhance` (boolean) — Whether to enhance the prompt before generating.
- `references` (array of integer) — Optional asset ids to use as visual references.

Example request:

```json
{
  "prompt": "Warm cinematic tones, deep blues and bioluminescent accents.",
  "references": [
    5,
    6
  ]
}
```

### Response `201`

```json
{
  "message": "Success.",
  "status": 201,
  "data": [
    {
      "id": 1,
      "asset_id": 5,
      "source": "upload",
      "type": "ai-image",
      "status": "success",
      "url": "https://cdn.syllaby.dev/moodboard/1.png",
      "created_at": "2026-01-01T12:00:00.000000Z"
    }
  ]
}
```

### Errors

- `401` — Unauthenticated
- `403` — Forbidden — two distinct causes share this status: 1. **No active subscription** (code `SUBSCRIPTION-REQUIRED`, message "An active subscription is required.") — checked first by the active-subscription gate, before any ownership or precondition logic. The v2 public API has no free tier. 2. **Authorization / ownership or precondition failure** — the subscription is active but the action is not allowed: the resource belongs to another account, or a render precondition is unmet (e.g. `A script is required before rendering.`, `Voice was not provided.`, the video is busy, or storage is full).
- `404` — Not found
- `422` — Validation error
- `429` — Rate limit exceeded — requests are limited per API token (30 per minute by default). The response carries `Retry-After` (seconds to wait) and `X-RateLimit-Reset` (Unix timestamp when the window resets); back off until then and retry. Successful responses include `X-RateLimit-Limit` and `X-RateLimit-Remaining` so you can pace requests proactively.

Error shapes and examples are shared across endpoints — see the **Error responses** section in [getting-started](getting-started.md).

## POST /text-to-scene/{storyboard}/moodboard/generate

**Generate a moodboard image** (operationId: `generateTextToSceneMoodboard`)

💳 Charges credits. Generates a moodboard image from the supplied prompt (optionally enhanced) to steer the storyboard's visual style. Returns HTTP 202.

### Parameters

- `storyboard` (path, integer, required) — The storyboard ID Example: `1`

### Request body

Required.

- `prompt` (string, required) — Prompt describing the moodboard imagery to generate (max 5000 characters).
- `enhance` (boolean) — Whether to enhance the prompt before generating.
- `references` (array of integer) — Optional asset ids to use as visual references.

Example request:

```json
{
  "prompt": "Warm cinematic color palette with deep ocean blues.",
  "enhance": true
}
```

### Response `202`

```json
{
  "message": "Success.",
  "status": 202,
  "data": {
    "id": 1,
    "asset_id": 5,
    "source": "generated",
    "type": "ai-image",
    "status": "success",
    "url": "https://cdn.syllaby.dev/moodboard/1.png",
    "created_at": "2026-01-01T12:00:00.000000Z"
  }
}
```

### Errors

- `401` — Unauthenticated
- `402` — Insufficient credits. The request is authenticated and valid, but the account balance is too low — top up and retry.
- `403` — Forbidden — two distinct causes share this status: 1. **No active subscription** (code `SUBSCRIPTION-REQUIRED`, message "An active subscription is required.") — checked first by the active-subscription gate, before any ownership or precondition logic. The v2 public API has no free tier. 2. **Authorization / ownership or precondition failure** — the subscription is active but the action is not allowed: the resource belongs to another account, or a render precondition is unmet (e.g. `A script is required before rendering.`, `Voice was not provided.`, the video is busy, or storage is full).
- `404` — Not found
- `422` — Validation error
- `429` — Rate limit exceeded — requests are limited per API token (30 per minute by default). The response carries `Retry-After` (seconds to wait) and `X-RateLimit-Reset` (Unix timestamp when the window resets); back off until then and retry. Successful responses include `X-RateLimit-Limit` and `X-RateLimit-Remaining` so you can pace requests proactively.

Error shapes and examples are shared across endpoints — see the **Error responses** section in [getting-started](getting-started.md).

## GET /text-to-scene/{storyboard}/estimate

**Estimate storyboard render credits** (operationId: `estimateTextToScene`)

Estimates the credits required to render the storyboard and compares them against your available balance.

### Parameters

- `storyboard` (path, integer, required) — The storyboard ID Example: `1`

### Response `200`

```json
{
  "message": "Success.",
  "status": 200,
  "data": {
    "required": 45,
    "available": 120,
    "sufficient": true
  }
}
```

### Errors

- `401` — Unauthenticated
- `403` — Forbidden — two distinct causes share this status: 1. **No active subscription** (code `SUBSCRIPTION-REQUIRED`, message "An active subscription is required.") — checked first by the active-subscription gate, before any ownership or precondition logic. The v2 public API has no free tier. 2. **Authorization / ownership or precondition failure** — the subscription is active but the action is not allowed: the resource belongs to another account, or a render precondition is unmet (e.g. `A script is required before rendering.`, `Voice was not provided.`, the video is busy, or storage is full).
- `404` — Not found
- `429` — Rate limit exceeded — requests are limited per API token (30 per minute by default). The response carries `Retry-After` (seconds to wait) and `X-RateLimit-Reset` (Unix timestamp when the window resets); back off until then and retry. Successful responses include `X-RateLimit-Limit` and `X-RateLimit-Remaining` so you can pace requests proactively.

Error shapes and examples are shared across endpoints — see the **Error responses** section in [getting-started](getting-started.md).

## POST /text-to-scene/{storyboard}/render

**Render the storyboard** (operationId: `renderTextToScene`)

💳 Charges credits. The response `meta.credits` shows the cost and your remaining balance.

Starts rendering the finished storyboard into a video asynchronously and returns HTTP 202. Track progress by polling `GET /text-to-scene/{id}` and reading `data.video.status`.

### Parameters

- `storyboard` (path, integer, required) — The storyboard ID Example: `1`

### Response `202`

```json
{
  "message": "Success.",
  "status": 202,
  "data": {
    "id": 1,
    "uuid": "sb_abc123",
    "status": "processing",
    "prompt": "A documentary about deep-sea creatures.",
    "settings": {
      "audio": true,
      "aspect_ratio": "16:9",
      "resolution": "1080p"
    },
    "duration": [
      5,
      6,
      5
    ],
    "video": {
      "id": 10,
      "user_id": 1,
      "idea_id": null,
      "scheduler_id": null,
      "title": "Deep-sea documentary",
      "type": "text_to_scene",
      "url": null,
      "status": "rendering",
      "retries": 0,
      "hash": "abc123",
      "synced_at": null,
      "metadata": {
        "ai_labels": true,
        "custom_description": null
      },
      "failure": null,
      "created_at": "2026-01-01T12:00:00.000000Z",
      "updated_at": "2026-01-01T12:00:00.000000Z"
    },
    "scenes": [
      {
        "id": 12,
        "uuid": "scn_abc123",
        "order": 0,
        "video_prompt": "Wide shot of a glowing coral reef.",
        "duration": 5,
        "status": "draft",
        "error": null,
        "created_at": "2026-01-01T12:00:00.000000Z",
        "updated_at": "2026-01-01T12:00:00.000000Z"
      }
    ],
    "created_at": "2026-01-01T12:00:00.000000Z",
    "updated_at": "2026-01-01T12:00:00.000000Z"
  },
  "meta": {
    "credits": {
      "cost": 90,
      "remaining": 810
    }
  }
}
```

### Errors

- `401` — Unauthenticated
- `402` — Insufficient credits. The request is authenticated and valid, but the account balance is too low — top up and retry.
- `403` — Forbidden — two distinct causes share this status: 1. **No active subscription** (code `SUBSCRIPTION-REQUIRED`, message "An active subscription is required.") — checked first by the active-subscription gate, before any ownership or precondition logic. The v2 public API has no free tier. 2. **Authorization / ownership or precondition failure** — the subscription is active but the action is not allowed: the resource belongs to another account, or a render precondition is unmet (e.g. `A script is required before rendering.`, `Voice was not provided.`, the video is busy, or storage is full).
- `404` — Not found
- `429` — Rate limit exceeded — requests are limited per API token (30 per minute by default). The response carries `Retry-After` (seconds to wait) and `X-RateLimit-Reset` (Unix timestamp when the window resets); back off until then and retry. Successful responses include `X-RateLimit-Limit` and `X-RateLimit-Remaining` so you can pace requests proactively.

Error shapes and examples are shared across endpoints — see the **Error responses** section in [getting-started](getting-started.md).
