---
title: "Resume planning under the same durable StoryPlan id."
method: POST
path: "/story-plans/{planId}/resume"
tags: ["Plans"]
---

# Resume planning under the same durable StoryPlan id.

`POST /story-plans/{planId}/resume`

## Request

- Base URL: `https://docs.sleeperhit.studio/api/v1`
- URL: `https://docs.sleeperhit.studio/api/v1/story-plans/{planId}/resume`
- Auth: HTTP bearer

State-aware and replay-safe. Ordinary PENDING plans repair a missed queue dispatch, stale RUNNING plans reclaim a dead worker, and FAILED plans without generated output re-enter generation under the same id after clearing their failure. A PENDING plan with `failureCode: provider_capacity_backoff` already has a Sleeper Hit-managed delayed retry and is returned unchanged. Active, review-ready, and approved plans are also unchanged. REJECTED and SUPERSEDED plans remain human-terminal and cannot be resumed. Requires `Idempotency-Key`.

## Headers

- `Idempotency-Key` string, required

## Response `200`

Resumed plan envelope.

- StoryPlanEnvelopeResponse
  - `plan` StoryPlan, required
    - `id` string, required
    - `projectId` string, required
    - `status` 'PENDING' | 'RUNNING' | 'REQUIRES_APPROVAL' | 'READY' | 'APPROVED' | 'REJECTED' | 'FAILED' | 'SUPERSEDED', required
    - `title` string, nullable, required
    - `target` StoryPlanTarget, required
      - `audience` string, required
      - `objective` string, required
      - `outcome` string, required
      - `tone` string
      - `industry` string
      - `distributionContext` string
    - `artifactRequests` StoryArtifactRequest[], required
      - `type` 'pitch_deck' | 'table_read' | 'trailer', required
      - `mode` string
      - `deckMode` 'pitch' | 'showcase': For pitch_deck: what the deck is for. "pitch" (default) tells the approved story to a buyer and is judged as a pitch. "showcase" is an epic sizzle of the show's world, shown as its story: built on its intent (what the show is about, the one question it leaves open, an arc that builds to a turn), carried by a narrator and the characters' own lines, with world chapters (vistas, crowds, life, scale) that carry the theme interleaved with chapters of the characters in that world doing things; it is judged as a showcase, not as a pitch of the approved story. Every deck, in either mode, is planned from its intent. Set when the deck is planned; it never changes after.
      - `showcaseCharacters` string[]: For a showcase deck only: the characters it must feature, by their exact canon names, in the order they first appear (at most 12). Each is on camera in at least one chapter, among the world chapters. Omit to let the planner choose from the cast.
      - `shotDensity` 'held' | 'standard' | 'dense': For pitch_deck and trailer: how many angles (cuts) each rendered shot of a chapter or beat carries. Cuts inside a rendered shot are free, so density never changes the price, the plan or a line. "held" lets one long camera move carry a shot (1-2 angles); "standard" (default) cuts 2-3 angles into each shot where its seconds allow; "dense" cuts 3-5. No angle is planned under about 1.5 seconds, and a spoken line keeps its own seconds. Set when the deck or trailer is planned.
      - `durationSeconds` integer: Target duration in seconds. Video artifacts (trailer, pitch_deck) are planned at CUT density, not minimum-call density: round(target / mean) units of 4-30s each, with a ~5s mean trailer beat and a ~9s mean pitch-deck chapter (one render call, at most 10s) — so a 90s trailer plans about 18 beats and a 180s deck about 20 chapters. Each type carries its own ceiling: trailer 90s, pitch_deck 180s. Audio runtimes (table_read) are not unit-rendered and may run longer.
      - `aspectRatio` string: For a trailer, the shape the whole cut renders at: 16:9 (default), 9:16 or 1:1. Its shots, text composites and stitched master all follow it.
      - `videoModelResolution` '480p' | '720p' | '1080p': For a trailer, the class the finished cut is DELIVERED at: 720p or 1080p (default 720p; 480p delivers the default). Every shot renders at 480p first; the takes you keep are upscaled to this class when the trailer is finished, never re-rendered. Every class renders its first pass at 480p, finished by upscaling: 1 Studio Credit per second to render, plus 1 Studio Credit per second to finish at 720p or 4 Studio Credits per second at 1080p. For 90 seconds of generated footage, about 180 Studio Credits at 720p or 450 Studio Credits at 1080p. The exact per-shot quote, including location plates and on-screen text, is shown before anything is spent.
      - `includePdf` boolean: The deck's companion PDF, laid out from poster frames of the finished takes. Off unless true: decks ship without a PDF by default; render_pitch_deck_pdf renders one afterwards.
      - `narrationPolicy` 'auto' | 'include' | 'suppress': For table_read: auto lets the planner decide; include requests a narrator; suppress makes the read speaker-only. This is a user/plan choice, not tied to mode.
      - `deferMusic` boolean: For table_read: skip automatic soundtrack planning/rendering (SFX detection is unaffected). Use when you supply your own beds — attach them through POST /artifacts/{id}/music after the artifact exists. The manifest reports audio.adaptiveMusic=false with audio.musicFailure=null, since no music was requested rather than attempted and failed.
      - `deferSfx` boolean: For table_read: never SYNTHESISE a sound effect. Cues are still detected and placed, but each may only resolve to an effect the SFX bank already holds; anything with no match is left silent for you to fill via POST /artifacts/{id}/sfx. Use when the show has a fixed sound palette, so a one-off effect is never rendered, banked, or billed.
      - `deferAudioRender` boolean: For table_read: skip the automatic durable-MP3 render when the read reaches READY. Use when you run your own post-production — without it the whole read is synthesised once on ready, for a mix you then invalidate, and again on your finalize. You must POST /artifacts/{artifactId}/finalize yourself; until then `audio.recordingUrl` stays null and share players show a preparing state.
      - `coverageTarget` number: For table_read: minimum development-coverage score (out of 10) the screenplay must reach BEFORE the read is performed. Defaults to 8. Below the bar the script is rewritten against the coverage notes and re-scored, so parse/casting/voices/soundtrack/mix are never spent on a draft that did not clear it. Progress is reported as stage `coverage_gate`.
      - `coverageMaxRewrites` integer: For table_read: how many coverage-driven rewrites to attempt before performing the best draft. Defaults to 3. Set 0 to score without rewriting. Bounded because coverage scores are not monotonic — a rewrite can come back lower — so the best draft is kept rather than the last.
      - `punchUp` boolean: For table_read on a job that writes its screenplay: true runs a punch-up pass over the finished draft before it is scored or performed, one scene at a time. Every exchange is made to land, bits are topped, the register is brought up to what the notes and the Series Bible ask for (swearing included when they ask for it), and speeches, explained jokes and dead air are cut, at the same length or shorter. A scene keeps its punch-up only if every verbatim quote survives, it adds no neverSay term, it keeps its slugline, it adds no speaker and it grows by no more than 15%. A scene that only ran long is asked once to cut back to its length; any other failure, or a second miss, and the written scene stands. Default false. Not combined with scriptUploadId.
      - `neverSay` string[]: For table_read with punchUp: up to 20 words or phrases (each up to 40 characters) the punch-up must never introduce, matched as whole words, ignoring case. These are a show's hard lines, e.g. ["goddamn", "Jesus", "Christ"]. A punched scene that adds one keeps its written version instead. Requires punchUp.
      - `firstLineClean` boolean: For table_read with punchUp: true keeps the episode's first spoken line clean. If the punched opening scene's first spoken line swears and the written one did not, that scene keeps its written version (no retry). The writer's own first line is never changed. Requires punchUp.
      - `scopeKind` 'episode' | 'season' | 'project': For pitch_deck and trailer: "episode" (default, one installment), "season" (a whole season), or "project" (the whole project across all episodes).
      - `projectId` string: For season/project pitch decks and trailers: the StoryProject id (defaults to the plan's project).
      - `seasonNumber` integer: For season pitch decks and trailers: which season number.
      - `voiceMap` object: For table_read: a complete character-to-voice map applied before generation to skip automatic voice assignment. Use saved voice IDs from GET /voices. Each value requires voiceId; voiceName, gender, and provider are optional hints and authoritative saved-voice metadata wins.
      - `scriptUploadId` string: For trailer on a job: the script this episode cut is of (a ScriptUpload id in the plan's project; any version names its script). The cut is planned from that script's own newest completed table read or, for a story film with no read yet, from the film's own script, and never from another script's read. Without it an episode trailer is cut from the plan's own read, else a product project's one written story film (several are refused by name), else a story project's newest read. Not on a season or project trailer. For table_read on a job: perform this finalized, cast-ready screenplay version (a ScriptUpload id in the plan's project) as written, instead of writing a new one from the plan. Its episode cast must be complete — every voice and, unless the project is audio-only, every headshot, body shot and sheet — and its own screenplay coverage must be open (a report at or above the bar, or the writer's acknowledgement of it), or the job is refused before anything is created with 409 `cast_precondition_failed` (`details.reason`: `script_not_finalized`, `screenplay_coverage_before_cast` or `script_not_cast_ready`); a screenplay outside the project is 404 `script_not_found`. No coverage rewrite runs and no second coverage report is queued. Priced by its length the way Prepare Table Read prices a read (the table-read rate for 60 pages or fewer, or for over 60); the job's quote shows which. Charged once per screenplay version, both ways: a screenplay whose read is already paid for (prepared in the Writing Lab with POST /scripts/{scriptUploadId}/table-read/prepare, or held by another job) is quoted at 0 for the read and nothing is reserved, and a read the job holds is one a later prepare charges nothing for. Not combined with voiceMap, notes, durationSeconds, coverageTarget, coverageMaxRewrites, punchUp or neverSay.
      - `notes` string
    - `styleConstraints` StoryPlanStyleConstraints, required
      - `preferredVisualStyle` string
      - `forbiddenVisuals` string[]
      - `voicePreference` string
      - `musicPolicy` string
      - `brandSafety` string[]
    - `creativeBrief` StoryPlanCreativeBrief, required: Optional Storyteller/script brief used before table-read planning. For table_read plans, capture the audience, genre, page target, cast, music, and SFX basics here so the generated scriptBlueprint can be reviewed before generation.
      - `projectFormat` 'standalone' | 'feature' | 'tv_series' | 'limited_series' | 'short_form_series' | 'audio_series' | 'other'
      - `installmentLabel` string
      - `installmentNumber` integer
      - `seriesContext` string
      - `genre` string
      - `audience` string
      - `writingStyle` string
      - `comps` string[]
      - `pageTarget` integer
      - `castNotes` string
      - `musicStyle` string
      - `sfxPolicy` string
      - `replanInstruction` string: Exact correction brief carried forward from a terminal prior planning attempt.
      - `mustKnowBeforeWriting` string[]
    - `sourceIds` string[], required
    - `autoApprove` boolean, required
    - `plan` StoryPlanEnvelope, required: Canonical source-grounded plan. Returned inside `plan.plan` on a StoryPlanEnvelopeResponse.
      - `version` 1, required
      - `projectId` string, required
      - `title` string, required
      - `target` StoryPlanTarget, required
        - `audience` string, required
        - `objective` string, required
        - `outcome` string, required
        - `tone` string
        - `industry` string
        - `distributionContext` string
      - `sourceDigest` object, required
        - `summary` string, required
        - `sourceIds` string[], required
        - `evidence` object[], required
          - `sourceId` string, required
          - `claim` string, required
          - `support` string, required
          - `confidence` 'low' | 'medium' | 'high', required
        - `gaps` string[], required
        - `rightsNotes` string[], required
      - `thesis` object, required
        - `oneLine` string, required
        - `supportingClaims` string[], required
        - `avoidClaims` string[], required
      - `journey` object, required
        - `beats` StoryPlanBeat[], required: Requested-work-driven journey beats; long-form audio plans are not truncated to a fixed beat count.
          - `id` string, required
          - `role` 'hook' | 'context' | 'conflict' | 'proof' | 'turn' | 'payoff' | 'cta', required
          - `title` string, required
          - `narration` string
          - `visualIntent` string
          - `evidenceIds` string[]
          - `estimatedSeconds` integer: Planned beat duration. Long-form audio beats may exceed 120 seconds; table-read beats must sum to the requested table-read duration.
      - `visualSystem` object, required
        - `style` string, required
        - `palette` string[]
        - `typography` string
        - `aspectRatios` string[]
        - `continuityNotes` string
        - `forbiddenVisuals` string[]
      - `audioSystem` object
        - `voice` string
        - `music` string
        - `sfxPolicy` string
      - `scriptBlueprint` StoryPlanScriptBlueprint: Generated for table_read plans and intended for review before approving the plan. The job runner requires this field for table_read generation.
        - `projectFormat` 'standalone' | 'feature' | 'tv_series' | 'limited_series' | 'short_form_series' | 'audio_series' | 'other'
        - `installmentLabel` string
        - `pageTarget` integer, required
        - `genre` string, required
        - `audience` string, required
        - `writingStyle` string, required
        - `narrationPolicy` 'auto' | 'include' | 'suppress': Narration posture for this read. `suppress` means no NARRATOR voice/cue; `include` means a narrator is expected; `auto` lets the plan infer from the format and cast.
        - `outline` object[], required
          - `sceneId` string, required
          - `heading` string, required
          - `pageBudget` number: Pages allocated to the scene, in quarter pages. The budgets always sum to `pageTarget`: the planner's proportions are rescaled onto it, so an outline with more scenes than pages gets fractional scenes rather than a refusal. Long-form audio scenes are not capped at 30 pages.
          - `dramaticPurpose` string, required
          - `storyAction` string, required
          - `sourceEvidenceIds` string[]
        - `cast` object[], required
          - `name` string, required
          - `role` string, required
          - `function` string, required
          - `voiceDirection` string
        - `audio` object
          - `musicStyle` string
          - `sfxMoments` object[]
            - `sceneId` string, required
            - `label` string, required
            - `description` string, required
        - `plannedNextInstallments` object[]
          - `label` string, required
          - `premise` string, required
        - `approvalSummary` string, required
      - `artifacts` object[], required
        - `type` 'pitch_deck' | 'table_read' | 'trailer', required
        - `mode` string
        - `durationSeconds` integer: Target duration in seconds. Video artifacts (trailer, pitch_deck) are planned at CUT density, not minimum-call density: round(target / mean) units of 4-30s each, with a ~5s mean trailer beat and a ~9s mean pitch-deck chapter (one render call, at most 10s) — so a 90s trailer plans about 18 beats and a 180s deck about 20 chapters. Each type carries its own ceiling: trailer 90s, pitch_deck 180s. Audio runtimes (table_read) are not unit-rendered and may run longer.
        - `aspectRatio` string
        - `renderPlan` object, required
    - `quote` StoryPlanQuote, required
      - `currency` 'studio_credits', required
      - `total` integer, required
      - `lineItems` StoryPlanQuoteLineItem[], required
        - `kind` 'planning' | 'source_digest' | 'artifact' | 'addon', required
        - `label` string, required
        - `artifactType` 'pitch_deck' | 'table_read' | 'trailer'
        - `opKey` string, nullable
        - `credits` integer, required
        - `note` string
      - `notes` string[], required: When `balanceAfter` is present and the price is above zero, the first note is the price and what it would leave (or how short the writer is, with the top-up link).
      - `computedAt` string, date-time, required
      - `balanceAfter` integer: StoryPlan reads only, and only while the plan is still a price before a spend: status READY or REQUIRES_APPROVAL, or APPROVED with no live StoryJob (none, or only FAILED/CANCELED ones), read by a caller holding `credits:read`. Absent otherwise: once create_job exists it has reserved `total`, so the plan is paid for; PENDING/RUNNING quotes are preliminary; REJECTED/SUPERSEDED/FAILED plans will not be bought; and never on a StoryJob's recorded quote. A live balance, never stored. Studio Credits the writer would have left once `total` is spent: the balance available now (as Billing shows it) minus `total`. Negative means they are that many short.
      - `sufficient` boolean: Present exactly when `balanceAfter` is. True when the available balance covers `total`. False means the writer must top up before creating the job; say so.
    - `modelUsed` string, nullable, required
    - `providerUsed` string, nullable, required
    - `failureCode` string, nullable, required
    - `failureMessage` string, nullable, required
    - `approvedAt` string, date-time, nullable, required
    - `approvedVia` 'user_confirmed' | 'standing_approval', nullable: How the plan was approved: a person's `userConfirmed`, or a show's standing approval standing in for them. Null for a plan an app flow approved by construction. A job from a `standing_approval` plan may request only `table_read`.
    - `approvedUnderSeriesId` string, nullable: The show whose standing approval covered the approval (only when `approvedVia` is `standing_approval`).
    - `rejectedAt` string, date-time, nullable, required
    - `rejectionReason` string, nullable, required
    - `generatedAt` string, date-time, nullable, required
    - `currentVersion` integer, required
    - `createdAt` string, date-time, required
    - `updatedAt` string, date-time, required

## Other responses

- `401`: Missing, malformed, revoked, or expired API key.
- `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 plan is in a state that does not allow the requested operation.
- `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)
