---
title: "Price, then start, the render of a draft deck's plan."
method: POST
path: "/pitch-decks/{jobId}/plan/approve"
tags: ["Pitch Deck"]
---

# Price, then start, the render of a draft deck's plan.

`POST /pitch-decks/{jobId}/plan/approve`

## Request

- Base URL: `https://docs.sleeperhit.studio/api/v1`
- URL: `https://docs.sleeperhit.studio/api/v1/pitch-decks/{jobId}/plan/approve`
- Auth: HTTP bearer

Approves a draft deck's chapter plan into the render — the next step of EVERY deck job: A pitch deck job always stops at its plan: it plans the chapters onto a `draft` deck (the job's `progress.pitchDeckJobId`) and renders and reserves nothing, so planning is free. The next step is approve_pitch_deck_plan (POST /pitch-decks/{jobId}/plan/approve; CLI: pitch-deck approve-plan <jobId>), priced first: its quote (quote_approve_pitch_deck_plan, or the route without `confirmed`) returns the 480p render quote and reserves nothing; with `confirmed: true` it reserves exactly the quoted calls and renders them. Approving THIS job renders the deck that was scored; creating a new plan re-runs the stochastic planner and renders an ungraded draw instead. TWO CALLS, PRICE FIRST: sent WITHOUT `confirmed` it runs the coverage gate, compiles and voices every footage chapter (free to the writer) and prices the 480p render — a line per call at its voiced seconds and a line per location plate the renders make first — reserving nothing (`quotedOnly: true`); sent again with `confirmed: true` it reserves exactly the quoted calls and starts the render (`rendering`, then `review`). A chapter's line is spoken by a cast member in their own voice when its `narrationVoice` names one — on camera, lip-synced, when they are on its `visibleCharacters`, else as a voice-over — otherwise by the narrator over the cut; a take that leaves a line unheard or says it in another character's voice, has lips out of step with its lines, or misses its opening framing is retaken once automatically at our cost. Refused before the price, with the action that fixes it (`details.refusal`, and `details.blocked` per chapter): an unscored or failing plan, a chapter whose cast has no body figure or whose speaker has no cast voice, a chapter whose words one render cannot carry. Each blocked chapter carries its `chapterIndex` and `chapterNumber`: fix it with `chapters/refine` (free, on the draft) and price the approval again — the deck is not planned again. Preparing the price (voicing each line and planning each shot) can take a few minutes, so it runs in the background: an answer with `status: "preparing"` carries no price yet and reserved nothing — tell the writer, then ask again with the same call and the same inputs after `checkAgainSeconds` (about 20) until the price (or a refusal) comes back. Asking again while it is being prepared never starts a second one. A confirmed call that finds the price no longer prepared (the plan changed since) reserves nothing and answers the same way: price it again. A plan the gate refuses on the judge's scores alone says so (`details.overridable: true`, `details.overrideWhy`): the writer may render it anyway by sending `acknowledgePlanCoverage: { reason }` on the price and the confirm alike — only on their explicit choice. `details.overridable: false` cannot be rendered over (a blocking defect decided in code, or a missing, running, failed, stale or old-rubric report). The confirmed approval records the override on the report (the coverage read's `writerOverride`); the answer carries it as `planCoverageOverride`. Draft only. Requires `Idempotency-Key`. Scope: `story:write`. A pitch deck is priced by the second: 1 credit a second to render at 480p, plus 1 credit a second to finish at 720p (2 credits a second in all; a 1080p finish is 4 credits a second instead). Planning is free. The exact price is quoted per shot once the plan is ready, before anything renders; location plates and on-screen text are itemized in the same quotes.

## Headers

- `Idempotency-Key` string, required

## Request body

- PitchDeckPlanApproveRequest: Say whether this call is the price or the purchase.
  - `confirmed` boolean: Omit (or false) to get the 480p render quote WITHOUT spending anything — the chapters are compiled and voiced (free to the writer), nothing is reserved. Send true only after the writer has seen that price and said go: it reserves exactly the quoted calls and starts the render.
  - `acknowledgePlanCoverage` PlanCoverageAcknowledgement: Only on the writer's explicit choice, with their reason: render over a plan the coverage judge scored under the bar. It overrides the judge's verdict only (an overall score or a judged veto under the bar on a complete report of the plan as it stands), never a code-checked blocking defect, and never a missing, running, failed, stale or old-rubric report. The confirmed approval records who, when, why and what it overrode on the report. Send it on the price and the confirm alike. Refused (400, with `details.overridable: false` and `details.overrideWhy`) for anything but a judged fail of a complete, current report.
    - `reason` string, required: Why the writer renders over the plan gate, in their own words. Recorded on the report.

## Response `200`

The 480p render quote (no `confirmed`) or the started render — or, while the price is being prepared, `status: "preparing"` (nothing reserved; ask again).

- union: The plan approval's answer: its price or its started render (`PitchDeckRenderAnswer`), or — while the price is being prepared — `RenderQuotePreparing` (`status: "preparing"`).
  - PitchDeckRenderAnswer: Either the PRICE of a render (plan approval, or a chapter rendered again) or the queued render itself — branch on `quotedOnly`. Without `confirmed` nothing is reserved; with `confirmed: true` exactly the quoted lines are held and the calls are queued.
    - `quotedOnly` boolean, required: True when this was a price and NOTHING was spent; false when the render was reserved and queued.
    - `queued` boolean: Confirmed call only.
    - `jobId` string, required
    - `credits` number, required: What a confirmed call reserves (quote), or what it reserved (confirmed).
    - `costLabel` string, nullable: What the whole quote buys, in one label — the shots, the chapters they span, their seconds at 480p and the location plates made first, e.g. "Pitch deck render: 7 shots across 4 chapters (33s at 480p) + 1 location plate" (quote only).
    - `lines` object[]: Quote only. Every line a confirmed call would reserve: one per call at its voiced seconds, one per location plate the renders make first, and — a plan approval — the automatic-fix allowance (kind `allowance`).
      - `kind` 'call' | 'plate' | 'allowance': `allowance`: a plan approval's automatic-fix allowance (see `autoFixAllowance`).
      - `idempotencyKey` string
      - `label` string
      - `credits` number
      - `callId` string, nullable
      - `plateKey` string, nullable
      - `seconds` number, nullable
    - `calls` union: Quote: the priced calls `[{ chapterIndex, callId, take, seconds, audioMode, credits }]`. Confirmed: how many calls were queued.
      - object[]
        - `chapterIndex` integer
        - `callId` string
        - `take` integer
        - `seconds` number
        - `audioMode` 'dialogue' | 'ambient' | 'silent'
        - `credits` number
      - integer
    - `warnings` string[]: Quote only. Compile warnings worth showing (advisory; nothing blocks on them).
    - `balanceAfter` integer: Quote only. Studio Credits the writer would have left once this is spent: the balance available now (as Billing shows it) minus the price. Negative means they are that many short.
    - `sufficient` boolean: Quote only. True when the available balance covers the price. False means the writer must top up first (the note names the Billing link) — do not confirm; the reserve would refuse.
    - `note` string: The price in the writer's own words, what it would leave (or how short they are, with the top-up link), and how to proceed (quote only).
    - `message` string: Confirmed call only: what was queued.
    - `previewUrl` string: A chapter render only: a shareable link to watch the chapter's current cut.
    - `autoFixAllowance` object, nullable: A plan approval's quote only: the AUTOMATIC-FIX ALLOWANCE, part of the one approval and included in `credits`. A ceiling the engine draws on to retake the shots the review of the 480p preview flags (and render flagged on-screen text again), reserved with the render; only what the fixes draw is charged and the rest comes back when the loop stops. 0 when the cut has nothing a paid new take could redo (app captures, title cards) or the balance holds only the render; free fixes (re-timing an app capture's narration to its picture) run whatever it holds, and `note` says plainly what is and is not fixed by itself. The 720p finish is a separate approval.
      - `credits` integer, required
      - `note` string, required: The allowance in the writer's words.
    - `planCoverageOverride` PlanCoverageOverride: A render over a failed plan gate on the writer's word: the report it overrode, the plan revision it graded, its score, the bar, and the reasons it gave. A recorded override (on the confirmed approval and on the coverage read) also carries `at` and `by`.
      - `reason` string, required
      - `reportId` string, required
      - `planRevision` integer, required
      - `overallScore` number, nullable, required
      - `gateThreshold` number, required
      - `gateReasons` string[], required
      - `at` string, date-time: Recorded overrides only: when the confirmed approval recorded it.
      - `by` object: Recorded overrides only: who approved it — `api` (the Story API: MCP and the CLI) with the API key or `oauth:…` connection, or `app` (the Writers Lab).
        - `userId` string, required
        - `surface` 'api' | 'app', required
        - `keyId` string, nullable, required
  - RenderQuotePreparing: A plan approval whose price is still being prepared in the background (every chapter or beat voiced and planned): no price yet, and nothing was reserved — confirmed or not. Ask again with the same request after `checkAgainSeconds`; the price comes back once it is ready.
    - `quotedOnly` true, required
    - `status` 'preparing', required
    - `jobId` string, required
    - `progress` string, nullable, required: What is being prepared now, e.g. "Preparing chapters 9 and 10 of 17; 8 of 17 done…".
    - `checkAgainSeconds` integer, required
    - `note` string, required: One line to tell the writer.

## Other responses

- `400`: Request payload failed validation. The error message names the offending field.
- `401`: Missing, malformed, revoked, or expired API key.
- `402`: Not enough Studio Credits to reserve this job (`insufficient_credits`), and nothing was reserved. `details` is InsufficientCreditsDetails: `required` and `available`, and `autoTopup` when auto top-up is on but did not cover it — `pending` (retry in a moment), `over_cap` (raise the monthly spending cap), `too_small` (buy a bigger pack) or `failed`.
- `403`: API key is valid but lacks the scope, is blocked by IP allowlist, or the account is disabled.
- `404`: The named project or source does not exist or is owned by another account.
- `409`: The pitch deck exists and the request is well-formed, but the deck, its chapter, its cast or its plates are not in a state that allows it (`pitch_deck_state_invalid`), and nothing was reserved: a plan approved outside draft, a take chosen, a chapter edited or rendered outside review/complete, an export before the finish, a finish while a call has no selected take or a footage chapter has not rendered, a text-only chapter asked for footage, a changed chapter asked for one call, a delivery class changed mid-pass, a render the compile blocked, or a finish while the master failed its video coverage (`pitch_deck_video_coverage_failed`, with `details.videoCoverage`). `details.refusal` is `{ code, action, fix }` (see PitchDeckRefusalDetails): `action` is what unblocks it — `recast_figure`, `add_signature_items`, `recast_voice` or `remake_location_plate` — and `fix` names the MCP tool and CLI command that take it; `details.blocked[]` lists every chapter a blocked compile could not render. The identical request succeeds once the deck is put right.
- `429`: Rate limit for this API key + IP + route family was exceeded.
- `500`: The API failed unexpectedly. Safe to retry with the same Idempotency-Key.

---

[API](https://skmtc.dev/sleeperhit/apis/sleeper-hit-studio-story-api.md) · [All operations](https://skmtc.dev/sleeperhit/apis/sleeper-hit-studio-story-api/llms.txt) · [OpenAPI document](https://skmtc.dev/sleeperhit/apis/sleeper-hit-studio-story-api/revisions/4534f5d25540?raw)
