---
title: "Start a conversational season run from the Series Bible episode map."
method: POST
path: "/story-projects/{projectId}/season-runs"
tags: ["Seasons"]
---

# Start a conversational season run from the Series Bible episode map.

`POST /story-projects/{projectId}/season-runs`

## Request

- Base URL: `https://docs.sleeperhit.studio/api/v1`
- URL: `https://docs.sleeperhit.studio/api/v1/story-projects/{projectId}/season-runs`
- Auth: HTTP bearer

Copies the current Series Bible episodes into a durable, editable season map and attaches an audio publishing series. Before calling, inspect document.readiness.development and complete an audio-appropriate Bible; runtime only requires a saved episode map so podcast creation is not blocked by visual/camera fields in the full development profile. Requires story:write and publishing:write because the route may create the audio series and RSS destination. Creation never approves or generates an episode: an agent must discuss each imported brief with the user, PATCH it with confirmed=true, then ask for whole-map approval. Requires Idempotency-Key for HTTP replay safety. Durable identity defaults to project + season number + Series Bible version, or an explicit runKey; a replay with different immutable create inputs is rejected. A product project's run makes one video per live (shipped or beta) feature of the season by itself: creating it maps the features (each brief agreed already) and prices what it may spend alone in `spendQuote` (the recordings it books, the most each finish costs); approving the map with the user's go starts it, and it stops only when an episode is `blocked`, saying what fixes it.

## Headers

- `Idempotency-Key` string, required

## Request body

- SeasonRunCreateRequest: Create a persistent season run from the current Series Bible episode map. Agents should first inspect document.readiness.development and complete an audio-appropriate Bible; the runtime minimum is intentionally the saved episode map because the full development profile includes visual/camera requirements. The imported episodes remain unconfirmed until discussed and saved one by one. Durable identity defaults to project + season number + Series Bible version; runKey selects an explicit domain identity. Every replay must use the same immutable create inputs. HTTP Idempotency-Key remains transport replay protection.
  - `publishingSeriesId` string: Existing audio publishing series in this project. Omit to reuse the first audio series or create one automatically.
  - `title` string
  - `seasonNumber` integer
  - `runKey` string: Optional durable domain identity. Reusing a runKey with different title, season, Bible version, policy, audience intent, audience policy, or explicit series is rejected.
  - `coveragePolicy` SeasonCoveragePolicy: Strict, deterministic coverage gate evaluated before durable MP3 finalization. Score and recommendation defaults are hard safety floors: callers may demand higher scores or fewer allowed recommendations, but may not weaken overall below 8/10, any required category below 7/10, or admit PASS. Revision attempts default to three, while callers may choose any positive integer when they want to keep spending credits on approved revisions.
    - `minimumOverallScore` integer, required
    - `minimumCategoryScore` integer, required
    - `allowedRecommendations` SeasonCoverageRecommendation[], required
    - `maxRevisionAttempts` integer, required
  - `audienceIntent` 'INTERNAL' | 'EXTERNAL': Who this season's finished work is for. INTERNAL stays inside the company; EXTERNAL leaves it. Fixed when the season is created and never editable afterwards — escalating a season after material is already in it would retroactively re-license work nobody reviewed for the outside. To take a season public, create a new EXTERNAL season and re-curate its sources.
  - `audiencePolicy` SeasonAudiencePolicy: Per-season narrowing on top of the audience intent. Deny rules apply to both intents and always beat allow rules. Items marked confidential are blocked on every season by a built-in list a policy may add to but never subtract from. Nothing that passes this becomes part of the season on its own — a person still has to keep it.
    - `version` 1
    - `integrations` SeasonAudienceOriginRule[]: At most one rule per connected product; a duplicate is rejected rather than merged.
      - `providerSlug` string, required: Connected product slug, e.g. slack, notion, github.
      - `allowOrigins` string[]: Stable upstream origin ids — channel id, owner/repo, space key. Required and non-empty for EXTERNAL seasons.
      - `denyOrigins` string[]: Never pulled, whatever the intent or the allow list says.
      - `disabled` boolean: Switch the whole product off for this season without discarding its lists.
    - `content` object
      - `denyKeywords` string[]: Blocked anywhere in an item's title, text, link, or labels. Matched on word boundaries.
      - `denyPatterns` string[]: Glob patterns, not regular expressions: * matches any run, ? matches one character, at most ten wildcards.
      - `extraConfidentialMarkers` string[]: Added on top of the always-on confidentiality markers. There is no way to remove a built-in marker.
    - `note` string: Free text echoed back in the plain-language summary the reviewer reads.

## Response `200`

Created or replayed season run.

- SeasonRunEnvelope
  - `run` SeasonRun, required
    - `id` string, required
    - `projectId` string, required
    - `publishingSeriesId` string, nullable, required
    - `publishingSeries` object, nullable, required
      - `id` string, required
      - `title` string, required
      - `slug` string, required
      - `status` string, required
    - `title` string, required
    - `seasonNumber` integer, required
    - `status` 'mapping' | 'approved' | 'running' | 'blocked' | 'complete' | 'ended', required: Durable season-run lifecycle. `ended` is terminal: the user ended the run (POST /season-runs/{runId}/end) and nothing it held can spend; only a new run makes the season again. `mapping` is editable and unapproved; `approved`/`running` have a locked map; `blocked` requires human intervention; `complete` means every episode published.
    - `projectKind` 'story' | 'product', required: `story`: the run writes, reads and publishes episodes. `product`: one video per live feature of the season, made by itself once the map and its price are approved.
    - `spendQuote` ProductSeasonSpendQuote, required: What approving a product season's map lets it spend by itself: the recordings it books (each once, at no more than its tour's price here), and the most each video's finish may cost (its title cards). Cutting a video from recordings is free. Anything dearer, and any second recording, is held for the writer.
      - `recordings` object[], required
        - `featureId` string, required
        - `featureName` string, required
        - `tourId` string, required
        - `credits` integer, required
      - `recordingCredits` integer, required
      - `finishCreditsMaxPerVideo` integer, required
      - `videos` integer, required
      - `totalCreditsMax` integer, required
      - `gaps` object[], required: Features of the season the run cannot record by itself; they wait for the writer.
        - `featureId` string, required
        - `featureName` string, required
        - `code` string
        - `reason` string, required
      - `onTheWay` object[], required: Features whose recording is already booked or running (by the writer or an earlier run): the run waits for it and never books another.
        - `featureId` string, required
        - `featureName` string, required
        - `reason` string, required
      - `leftOut` object[], required: Features of the season left out of the map: not available yet, or availability not set.
        - `featureId` string, required
        - `featureName` string, required
        - `reason` string, required
      - `sentence` string, required: The price in one sentence, to read to the writer before approving.
    - `coveragePolicy` SeasonCoveragePolicy, required: Strict, deterministic coverage gate evaluated before durable MP3 finalization. Score and recommendation defaults are hard safety floors: callers may demand higher scores or fewer allowed recommendations, but may not weaken overall below 8/10, any required category below 7/10, or admit PASS. Revision attempts default to three, while callers may choose any positive integer when they want to keep spending credits on approved revisions.
      - `minimumOverallScore` integer, required
      - `minimumCategoryScore` integer, required
      - `allowedRecommendations` SeasonCoverageRecommendation[], required
      - `maxRevisionAttempts` integer, required
    - `audienceIntent` 'INTERNAL' | 'EXTERNAL', required: Who this season's finished work is for. INTERNAL stays inside the company; EXTERNAL leaves it. Fixed when the season is created and never editable afterwards — escalating a season after material is already in it would retroactively re-license work nobody reviewed for the outside. To take a season public, create a new EXTERNAL season and re-curate its sources.
    - `audiencePolicy` SeasonAudiencePolicy, required: Per-season narrowing on top of the audience intent. Deny rules apply to both intents and always beat allow rules. Items marked confidential are blocked on every season by a built-in list a policy may add to but never subtract from. Nothing that passes this becomes part of the season on its own — a person still has to keep it.
      - `version` 1
      - `integrations` SeasonAudienceOriginRule[]: At most one rule per connected product; a duplicate is rejected rather than merged.
        - `providerSlug` string, required: Connected product slug, e.g. slack, notion, github.
        - `allowOrigins` string[]: Stable upstream origin ids — channel id, owner/repo, space key. Required and non-empty for EXTERNAL seasons.
        - `denyOrigins` string[]: Never pulled, whatever the intent or the allow list says.
        - `disabled` boolean: Switch the whole product off for this season without discarding its lists.
      - `content` object
        - `denyKeywords` string[]: Blocked anywhere in an item's title, text, link, or labels. Matched on word boundaries.
        - `denyPatterns` string[]: Glob patterns, not regular expressions: * matches any run, ? matches one character, at most ten wildcards.
        - `extraConfidentialMarkers` string[]: Added on top of the always-on confidentiality markers. There is no way to remove a built-in marker.
      - `note` string: Free text echoed back in the plain-language summary the reviewer reads.
    - `mapVersion` integer, required: Optimistic concurrency version for conversational map edits.
    - `seriesBibleVersion` integer, nullable, required
    - `currentVersion` integer, required
    - `approvedAt` string, date-time, nullable, required
    - `completedAt` string, date-time, nullable, required
    - `endedAt` string, date-time, nullable, required: When the user ended the run; null unless status is `ended`.
    - `lastError` string, nullable, required: Why this run stopped, as one plain sentence for the writer — never a provider's or the database's raw wording. The raw text stays in the row and the server log.
    - `progress` object, required
      - `total` integer, required
      - `coveragePassed` integer, required
      - `published` integer, required
    - `episodes` SeasonRunEpisode[], required
      - `id` string, required
      - `bibleEpisodeId` string, required
      - `position` integer, required
      - `episodeNumber` integer, required
      - `title` string, required
      - `summary` string, required
      - `objective` string, required
      - `audience` string, nullable, required
      - `tone` string, nullable, required
      - `targetDurationMinutes` integer, required
      - `notes` string, nullable, required: What the writer must know before this episode is written, one point per line.
      - `confirmed` boolean, required: True only after the episode brief was discussed and explicitly confirmed during season mapping.
      - `briefVersion` integer, required
      - `status` 'mapping' | 'ready_to_plan' | 'recording' | 'planning' | 'plan_review' | 'generating' | 'coverage' | 'needs_revision' | 'coverage_passed' | 'finalizing' | 'ready_to_publish' | 'publishing' | 'published' | 'blocked', required: Durable state for one mapped episode. A story run deliberately stops at `plan_review`, `needs_revision`, and `ready_to_publish` for a human decision. A product run (one video per live feature) goes through `recording` (only when the feature has no recording yet), `planning`, `plan_review`, `generating`, `coverage`, `coverage_passed` and `finalizing` by itself and ends at `ready_to_publish`, its video waiting in Publishing; it stops only at `blocked`, with `lastError` saying what fixes it.
      - `currentPlanId` string, nullable, required
      - `planAttemptCount` integer, required: Durable count of planning attempts for this episode, including replaced failed, rejected, or superseded plans.
      - `currentJobId` string, nullable, required
      - `currentArtifactId` string, nullable, required
      - `currentArtifactVersion` integer, nullable, required
      - `currentCoverageReportId` string, nullable, required
      - `currentReleaseId` string, nullable, required
      - `destinationKey` string, nullable, required: A product run only: the video destination the feature's cut is made for, chosen from its footage. Null on a story run.
      - `currentCaptureJobId` string, nullable, required: A product run only: the hosted recording the run booked for the feature (only one the approved price named). Null on a story run.
      - `currentVideoPlanRunId` string, nullable, required: A product run only: the product video plan run of the feature's cut. Null on a story run.
      - `currentTrailerJobId` string, nullable, required: A product run only: the feature's video (a capture-only trailer). Null on a story run.
      - `coverageAttemptCount` integer, required
      - `pendingReplanInstruction` string, nullable, required: One-shot correction brief waiting to be copied into the next replacement StoryPlan. It is cleared only after that plan is durably attached.
      - `revisionAttemptCount` integer, required
      - `coverageSnapshot` SeasonCoverageScoreSnapshot, required
        - `overallScore` integer, nullable, required
        - `recommendation` string, nullable, required
        - `categories` object, required
          - `premise` integer, nullable, required
          - `structure` integer, nullable, required
          - `characters` integer, nullable, required
          - `dialogue` integer, nullable, required
          - `pacing` integer, nullable, required
          - `marketFit` integer, nullable, required
      - `gateResult` SeasonCoverageGateResult, required
        - `passed` boolean, required
        - `failureReasons` string[], required
      - `productionReadiness` object, nullable, required: Durable retryable production blockers. Present across reconnects until music, SFX, and voice effects are ready and advance re-evaluates the gate.
        - `status` 'blocked', required
        - `retryable` true, required
        - `seasonRunEpisodeId` string, required
        - `artifactId` string, required
        - `artifactVersion` integer, required
        - `requirements` object, required
          - `musicRequired` boolean, required
          - `endMusicRequired` boolean, required
          - `sfxRequired` integer, required
          - `voiceEffectsRequired` integer, required
        - `blockers` object[], required
          - `code` string, required
          - `message` string, required
          - `sceneIndex` integer
          - `label` string
          - `modificationId` string
      - `blockerCode` string, nullable, required: Durable blocker classification used to distinguish guarded retryable stages from terminal human intervention.
      - `lastError` string, nullable, required: Why this episode stopped, as one plain sentence for the writer — never a provider's or the database's raw wording. The raw text stays in the row and the server log.
      - `coverageAttempts` SeasonCoverageAttempt[], required
        - `id` string, required
        - `attemptNumber` integer, required
        - `artifactId` string, required
        - `artifactVersion` integer, required
        - `coverageReportId` string, required
        - `coveragePolicy` SeasonCoveragePolicy, required: Strict, deterministic coverage gate evaluated before durable MP3 finalization. Score and recommendation defaults are hard safety floors: callers may demand higher scores or fewer allowed recommendations, but may not weaken overall below 8/10, any required category below 7/10, or admit PASS. Revision attempts default to three, while callers may choose any positive integer when they want to keep spending credits on approved revisions.
          - `minimumOverallScore` integer, required
          - `minimumCategoryScore` integer, required
          - `allowedRecommendations` SeasonCoverageRecommendation[], required
          - `maxRevisionAttempts` integer, required
        - `scoreSnapshot` SeasonCoverageScoreSnapshot, required
          - `overallScore` integer, nullable, required
          - `recommendation` string, nullable, required
          - `categories` object, required
            - `premise` integer, nullable, required
            - `structure` integer, nullable, required
            - `characters` integer, nullable, required
            - `dialogue` integer, nullable, required
            - `pacing` integer, nullable, required
            - `marketFit` integer, nullable, required
        - `passed` boolean, required
        - `failureReasons` string[], required
        - `revisionInstruction` string, nullable, required
        - `revisionApprovedAt` string, date-time, nullable, required
        - `createdAt` string, date-time, required
      - `createdAt` string, date-time, required
      - `updatedAt` string, date-time, required
    - `nextAction` SeasonRunNextAction, required: Agent-oriented next durable action. Treat `map_episode`, `approve_map`, `approve_plan`, `replan`, `retry_coverage`, `revise`, `repair_production`, and `publish` as human conversation stops, never as permission to act automatically.
      - `type` 'map_episode' | 'approve_map' | 'approve_plan' | 'replan' | 'retry_coverage' | 'revise' | 'repair_production' | 'publish' | 'resolve_blocker' | 'advance' | 'running' | 'complete' | 'ended', required
      - `episodeId` string
      - `message` string, required
      - `reportId` string
      - `planId` string
      - `jobId` string
      - `artifactId` string
      - `releaseId` string
      - `proposedRevisionInstruction` string
      - `requirements` object
      - `blockers` object[]
      - `blockerCode` string
    - `createdAt` string, date-time, required
    - `updatedAt` string, date-time, required

## Other responses

- `400`: Request payload failed validation. The error message names the offending field.
- `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 season run does not exist, was deleted, or is owned by another account.
- `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)
