---
title: "Save one agreed episode brief while mapping a season."
method: PATCH
path: "/season-runs/{runId}/episodes/{episodeId}"
tags: ["Seasons"]
---

# Save one agreed episode brief while mapping a season.

`PATCH /season-runs/{runId}/episodes/{episodeId}`

## Request

- Base URL: `https://docs.sleeperhit.studio/api/v1`
- URL: `https://docs.sleeperhit.studio/api/v1/season-runs/{runId}/episodes/{episodeId}`
- Auth: HTTP bearer

Partial update available only while the run is mapping. Set confirmed=true only after the user has discussed and agreed to the title, summary, objective, audience/tone, duration, and notes. expectedMapVersion is required on every PATCH so two agent conversations cannot silently overwrite each other. Map edits are locked permanently after approval.

## Request body

- SeasonRunEpisodeUpdateRequest
  - `expectedMapVersion` integer, required: Required optimistic-concurrency version from the latest SeasonRun response. A mismatch returns season_run_state_invalid; reload before retrying.
  - `patch` SeasonRunEpisodeMapPatch, required
    - `title` string
    - `summary` string
    - `audience` string, nullable
    - `tone` string, nullable
    - `targetDurationMinutes` integer
    - `objective` string: What the episode has to achieve. Its plan reads the first 400 characters.
    - `notes` string, nullable: What the writer must know before this episode is written, one point per line. Each line becomes one of the plan's must-know items (220 characters each, 11 lines at most beside the objective).
    - `episodeNumber` integer
    - `confirmed` boolean: Set true only after the user agrees to this complete episode brief.
  - `userConfirmed` boolean: Required and true when patch.confirmed=true, after the user explicitly agrees to this episode brief.

## Response `200`

Updated 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.
- `409`: The season run or episode is not at the required human-approved lifecycle state. Reload the run and follow nextAction.
- `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)
