---
title: "Score this film: music composed for the cut (costs credits)"
method: POST
path: "/v1/storyboards/{id}/score"
tags: ["Storyboards"]
---

# Score this film: music composed for the cut (costs credits)

`POST /v1/storyboards/{id}/score`

## Request

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

Composes music for the film with ElevenLabs music from a composition plan the film writes itself: one section per stretch of story, as long as it plays on the timeline (short moments grouped, 3 s at least, never across a decision, where the music rests), each section's styles from its moments' beat, action prompt, light and weather and the film's tone, the global styles from the tone, art style, look and setting; always instrumental. Paths share their common start: the first path is composed whole, every other from the decision where it splits, so a branch's music starts there. Each composition joins the film's sounds and goes on the timeline as a music cue (`doc.score.cues`) from its first moment, its `path` the choices it follows: on a path that takes another option it stops at that decision. The player plays the cues at their `level` times the `music` layer (`mix`), dipped about 10 dB under speech. Directed (all free): `{op: "setScoreDirection", text}` sets the owner's words for the whole score (at most 500 characters; null clears), which lead the global styles in place of the tone's; a section's styles can be written by `POST /v1/storyboards/{id}/score/plan` (the AI) or set by hand with `{op: "setScoreSection", key, pos: [..], neg: [..]}` (`clearScoreSection {key}` puts it back), each section keyed as the quote's `sections[].key` names it; the quote marks each section `written`, `edited`, `automatic` or `stale` (written for a stretch the film changed since: not played) and lists in `stale` what names no section any more. Scoring again replaces the score's own cues (`scored: true`); cues placed with `{op: "addMusicCue", cue: {id, soundId, fromPanelId, toPanelId?, path?, offsetS?, fadeInS?, fadeOutS?, level?}}` stay (`updateMusicCue {id, patch}` moves, replaces or levels one, `removeMusicCue {id}` takes it off; all free). **Costs credits**: `audio_music_10s` per 10 s composed (see `/v1/pricing`). Quote first with `dry_run: true`: `credits` is what it costs, per path in `paths`, each with its `sections` on the film and the `composition_plan` that will be sent; `available: false` comes with a `reason`. Without `dry_run` it answers `202 { task_id }`: poll `GET /v1/tasks/{id}`. Each path is charged after its cue is on the storyboard, through your token so its daily cap applies. Paid tier only (`402 audio_requires_purchase` on trial credits). A storyboard whose owner approves agent spending answers `403` except for the quote.

## Request body

- object
  - `dry_run` boolean

## Response `200`

The quote (dry_run)

- object
  - `dry_run` boolean
  - `available` boolean
  - `reason` string
  - `credits` integer, nullable
  - `per_10s` integer
  - `seconds` number: All the music composed, every path.
  - `paths` object[]
    - `path` integer: 1-based, in the order of `paths` on GET.
    - `name` string
    - `from_panel_id` string
    - `from_moment` integer, nullable
    - `starts_at_s` number
    - `seconds` number
    - `credits` integer
    - `follows` object: decision id -> option id: the cue's route.
    - `sections` object[]
      - `name` string
      - `from_s` number
      - `to_s` number
      - `panel_ids` string[]
      - `key` string: The section's signature: its moments and length. setScoreSection and clearScoreSection take it.
      - `status` 'written' | 'edited' | 'automatic' | 'stale'
      - `positive` string[]
      - `negative` string[]
      - `reason` string: Why the AI wrote it so (written sections).
    - `composition_plan` object: As sent to ElevenLabs: positive_global_styles, negative_global_styles, sections [{section_name, positive_local_styles, negative_local_styles, duration_ms, lines}].
  - `direction` string, nullable: setScoreDirection's words.
  - `stale` object[]: Written for sections the film no longer has: kept, not played.
    - `key` string
    - `by` 'ai' | 'owner'
  - `running_task_id` string, nullable

## Other responses

- `202`: The run started
- `400`: Add the first key moment to score the film
- `401`: Missing or invalid token
- `402`: Insufficient credits
- `403`: The owner approves an agent's spending on this storyboard
- `409`: The film is being scored already
- `429`: Rate limit exceeded

## Changes

- **2026-10-20** `7f7d4a72b17c`: 7 info
  - added the optional property `direction` to the response with the `200` status
  - added the optional property `paths/items/sections/items/key` to the response with the `200` status
  - added the optional property `paths/items/sections/items/negative` to the response with the `200` status
  - added the optional property `paths/items/sections/items/positive` to the response with the `200` status
  - …3 more

[Operation history](https://skmtc.dev/nanostudiopro/apis/nano-studio-pro-api/changes/v1/storyboards/:id/score/post.md)

---

[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)
