---
title: "Create a storyboard"
method: POST
path: "/v1/storyboards"
tags: ["Storyboards"]
---

# Create a storyboard

`POST /v1/storyboards`

## Request

- Base URL: `https://nanostudiopro.com/api`
- URL: `https://nanostudiopro.com/api/v1/storyboards`
- Auth: HTTP bearer

A new, empty storyboard. It gets its own project, named like it, so every picture and clip it makes lands together — unless `project_id` names one of yours. Logged as the agent's. Free.

## Request body

- object
  - `name` string
  - `project_id` string, nullable

## Response `201`

The new storyboard

- Storyboard: A storyboard as an agent plans from it. `doc` is the document the mutations address (camelCase inside, like the operations); everything around it is snake_case.
  - `id` string
  - `name` string
  - `project_id` string, nullable
  - `url` string, uri: The storyboard's page, where the owner watches.
  - `rev` integer
  - `can_undo` boolean
  - `can_redo` boolean
  - `read_only` boolean: Saved by a newer version of the app: `doc` is null and nothing can change.
  - `doc` object, nullable: idea, cast, places, panels (keyframes), segments, decisions, cues, startPanelId, agentSpend; and when set: theme (the title music), sounds (the film's sound list), ambience (place id -> its sound), endScreens (ending keyframe id -> its end screen), mix (the sound layers' levels set by hand), score (the music on the timeline: `cues`, each {id, soundId, fromPanelId, toPanelId?, path?, offsetS?, fadeInS?, fadeOutS?, level?, scored?}); each take's `effects` (sound effects at a second of its clip: {id, soundId, atS, level?, pitch?, rise?}).
  - `poster` StoryboardImage
    - `generation_id` string, nullable
    - `asset_id` string, nullable
    - `url` string, uri, nullable: Signed; expires. Null when the picture is gone.
    - `missing` boolean
  - `posters` object[]: Every poster made for the film, newest first (at most 12). `setPoster { generationId }` picks one (a new id joins the list), `removePoster { generationId }` takes one out (the picture stays in the library; the one in use falls back to the newest left).
    - `generation_id` string, nullable
    - `asset_id` string, nullable
    - `url` string, uri, nullable: Signed; expires. Null when the picture is gone.
    - `missing` boolean
    - `in_use` boolean
  - `keyframes` object[]
    - `id` string
    - `beat` string
    - `entity_ids` string[]: Pass these to `POST /v1/generations` as `entity_ids` for this keyframe's picture.
    - `entity_ids_left_out` string[]: Over the cap of 12, by priority.
    - `image` StoryboardImage
      - `generation_id` string, nullable
      - `asset_id` string, nullable
      - `url` string, uri, nullable: Signed; expires. Null when the picture is gone.
      - `missing` boolean
  - `issues` object[]
    - `target` object
      - `kind` 'panel' | 'segment' | 'decision' | 'cue'
      - `id` string
    - `level` 'error' | 'advice'
    - `message` string
    - `rule` string: The rule that fired.
    - `key` string: Stable across edits. An advice that is intended is marked so with the mutation {op: "ackIssue", key} and stops showing ({op: "unackIssue", key} brings it back). Errors cannot be marked.
  - `acknowledged` string[]: The issue keys marked as intended (doc.acknowledged).
  - `ai_feedback` object: What the owner said of each AI continuity finding, by its key: {verdict: intended|wrong|correct, note?, at, version (of the check), text (what it said), takeIds}. Set with the mutation {op: "setAiFeedback", key, verdict, note?} (note up to 300 characters; verdict null clears it). "intended" also marks it as intended; "wrong" hides it and a re-run on the same takes does not raise it again; the note is told to the next check of that clip or cut.
  - `readiness` object, nullable: Ready for video? Every issue told where it is in the film, errors first. Null when read_only.
    - `ready` boolean: No error left and every keyframe has its picture.
    - `errors` integer
    - `advice` integer
    - `acknowledged` integer: Advice marked as intended: left out of rows.
    - `ai_wrong` integer: AI findings marked "AI is wrong" (setAiFeedback): left out of rows.
    - `rows` object[]
      - `where` string: For example "Moment 3" or "Moments 2 to 3".
      - `key` string: The issue's stable key (see issues).
      - `level` 'error' | 'advice'
      - `message` string
      - `target` object
        - `kind` 'panel' | 'segment' | 'decision' | 'cue'
        - `id` string
      - `feedback` object: An AI finding the owner marked: {verdict: intended|wrong|correct, note?}.
  - `paths` object[]: Every route from the start to an ending, decisions branching it.
    - `panel_ids` string[]
    - `segment_ids` string[]
    - `choices` object: decision id -> option id taken on this path.
    - `length_s` number: The plan's length: every segment's seconds and the end hold.
    - `played_length_s` number: As the player plays it: trims, speed ramps and dissolves (which overlap) counted, with the clips finished so far.
    - `timeline` object[]
      - `panel_id` string
      - `segment_id` string, nullable
      - `start_s` number
      - `hold_s` number
      - `played_start_s` number
      - `played_s` number
      - `enter` StoryboardTransition: How a moment arrives: computed from what the storyboard knows (same place: dissolve; another place, time of day or weather: dip; a decision's keyframe: cut), its length from the shorter clip, unless set by hand (`manual`).
        - `kind` 'cut' | 'dissolve' | 'dip'
        - `s` number: Seconds; 0 for a cut. A dissolve overlaps the two pictures; a dip goes through black.
        - `manual` boolean
        - `why` string
      - `after_choice` StoryboardTransition: How a moment arrives: computed from what the storyboard knows (same place: dissolve; another place, time of day or weather: dip; a decision's keyframe: cut), its length from the shorter clip, unless set by hand (`manual`).
        - `kind` 'cut' | 'dissolve' | 'dip'
        - `s` number: Seconds; 0 for a cut. A dissolve overlaps the two pictures; a dip goes through black.
        - `manual` boolean
        - `why` string
      - `sound_tail_s` number: Seconds of the previous clip's sound carried under this moment.
      - `sound_lead_s` number: Seconds this moment's sound starts before its picture.
  - `title_screen` object, nullable: What a shared film shows before Play: its title (the storyboard's name), poster and title music (`updateTitleScreen`).
    - `title` string
    - `poster` StoryboardImage
      - `generation_id` string, nullable
      - `asset_id` string, nullable
      - `url` string, uri, nullable: Signed; expires. Null when the picture is gone.
      - `missing` boolean
    - `music` object, nullable
      - `name` string, nullable
      - `sound_id` string, nullable: Null for music chosen before the sound list.
      - `volume` number, nullable
      - `loop` boolean
  - `end_screens` object[]: One per keyframe a path ends on (`updateEndScreen`); null fields fall back to the film's title and the title music.
    - `panel_id` string
    - `title` string, nullable
    - `image` StoryboardImage
      - `generation_id` string, nullable
      - `asset_id` string, nullable
      - `url` string, uri, nullable: Signed; expires. Null when the picture is gone.
      - `missing` boolean
    - `music` object, nullable
      - `sound_id` string
      - `volume` number, nullable
      - `loop` boolean

## Other responses

- `401`: Missing or invalid token
- `404`: Project not found
- `429`: Rate limit exceeded

---

[API](https://skmtc.dev/nanostudiopro/apis/nano-studio-pro-api.md) · [All operations](https://skmtc.dev/nanostudiopro/apis/nano-studio-pro-api/llms.txt) · [OpenAPI document](https://skmtc.dev/nanostudiopro/apis/nano-studio-pro-api/revisions/b454211e598e?raw)
