This reference is generated from the live OpenAPI builder, schema registry, capabilities manifest, examples manifest, and UI/API/CLI/MCP surface registry. Do not hand-edit endpoint lists; change the contract code and regenerate.
Machine-readable surfaces
| Surface | Path |
|---|---|
| Capabilities | /api/v1/capabilities |
| OpenAPI JSON | /api/v1/openapi.json |
| Examples | /api/v1/examples |
| Agent index | /agent-index.json |
| Four-surface contract | /docs/surface-contract.json |
| CLI command map | /docs/cli-commands.json |
| MCP tool map | /docs/mcp-tools.json |
Surface coverage
- Operations: 333
- OpenAPI operations: 331
- Complete UI/API/CLI/MCP operations: 333 / 333
| Surface | Bound operations |
|---|---|
| UI | 333 / 333 |
| API | 333 / 333 |
| CLI | 333 / 333 |
| MCP | 333 / 333 |
Development flow warnings
Complete the Series Bible before screenplay pages
- Stage:
before_screenplay_generation - Blocking: no
- Surfaces:
ui,api,cli,mcp - Guidance: Before planning or generating a screenplay-backed table_read artifact, read, save, or generate the project Series Bible and confirm the format, audience, season arc, episode map, character roster, canon/world rules, visual style, audio style, and open questions are usable. The planner and screenplay adapter auto-load the saved Series Bible as project canon; if the user proceeds anyway, carry the missing areas into the StoryPlan review instead of silently ignoring them.
Finish the project stage a refusal names — do not retry it
- Stage:
before_planning_or_generation - Blocking: yes
- Surfaces:
ui,api,cli,mcp - Guidance: Planning, drafting episodes, casting, keeping or rendering mood board frames, writing the episode map, and generating project media are refused until the project reaches the stage each one needs. The refusal is a 409
project_precondition_failedwhoseerror.details.stagenames the stage the project has not finished (premise, premise_coverage, planning, full_coverage) withmissingFields,canPlanandcanStartEpisode. It is state, not a transient fault: nothing was created or charged, and the identical call refuses the same way until the stage is done — a recurring caller must stop and finish the stage (or tell the writer) rather than retry on a schedule.
A table read performs the project cast — complete it first
- Stage:
before_planning_or_generation - Blocking: yes
- Surfaces:
ui,api,cli,mcp - Guidance: A table_read job never casts. From a plan it starts only when the project cast canon, plus the request's own voiceMap, covers every canon character: a voice for everyone and, unless the project is audio-only (Series Bible productionMedium "audio"), a headshot, body shot and character sheet; a narrated read also needs a narrator voice. A job that adopts a finalized screenplay (scriptUploadId) needs that screenplay's episode cast complete. The refusal is a 409
cast_precondition_failedbefore anything is created or charged, withdetails.reasonanddetails.missing. Once the job has written its screenplay, a speaker the cast does not cover fails the job (failureCodecast_precondition_failed, a contract failure) before any audio, and its credits are released.
Complete the Series Bible Mood board before rendering
- Stage:
before_storyboard_rendering - Blocking: yes
- Surfaces:
ui,api,cli,mcp - Guidance: Image and video rendering are locked until the Series Bible has explicit production medium, visual style, character design, visual truths, production design, color palette, lighting rules, camera language, and location truths. Read document.readiness.moodBoard on the shared Series Bible response and resolve every missing field before rendering.
Run one coverage round after the screenplay exists
- Stage:
after_screenplay_exists - Blocking: no
- Surfaces:
ui,api,cli,mcp - Guidance: Once a screenplay-backed table_read artifact exists, read or poll the first coverage report before treating the script as ready for finalization, pitch packaging, or later installment generation. Table-read jobs queue the initial coverage pass when pages are created; request a new report only if none exists or the user wants another pass. Use the report, especially top fixes, to drive one revision pass. This is guidance, not an enforcement gate.
Account
| Endpoint | Operation ID | Scope | Idempotent | Schemas |
|---|---|---|---|---|
DELETE /api/v1/account/deletion | cancelAccountDeletion | account:write | no | response:AccountDeletionStatus |
GET /api/v1/account/deletion | getAccountDeletion | account:read | no | response:AccountDeletionStatus |
POST /api/v1/account/deletion | requestAccountDeletion | account:write | yes | request:AccountDeletionRequest<br>response:AccountDeletionStatus |
GET /api/v1/account/export | getAccountExport | account:read | no | response:AccountExportStatus |
POST /api/v1/account/export | requestAccountExport | account:read | yes | response:AccountExportStatus |
Artifacts
| Endpoint | Operation ID | Scope | Idempotent | Schemas |
|---|---|---|---|---|
GET /api/v1/artifacts/{artifactId} | getStoryArtifact | artifact:read | no | response:StoryArtifactEnvelope |
PATCH /api/v1/artifacts/{artifactId} | renameStoryArtifact | story:write | no | request:ArtifactRenameRequest<br>response:RenamedArtifact |
POST /api/v1/artifacts/{artifactId}/avatar-video | animateArtifactAvatar | artifact:publish | yes | request:TableReadAvatarVideoRequest<br>response:TableReadAvatarVideoResponse |
POST /api/v1/artifacts/{artifactId}/avatar | recastArtifactAvatar | artifact:publish | yes | request:AvatarRecastRequest |
GET /api/v1/artifacts/{artifactId}/cast | getArtifactCast | artifact:read | no | response:CastEnvelope |
POST /api/v1/artifacts/{artifactId}/cast | updateArtifactCast | artifact:publish | yes | request:CastBatchRequest |
GET /api/v1/artifacts/{artifactId}/coverage | getArtifactCoverage | artifact:read | no | response:CoverageReportEnvelope |
POST /api/v1/artifacts/{artifactId}/coverage | generateArtifactCoverage | story:write | yes | response:CoverageDispatchAck |
GET /api/v1/artifacts/{artifactId}/coverage/blacklist | getBlackListAnalysis | artifact:read | no | response:BlackListAnalysisResponse |
GET /api/v1/artifacts/{artifactId}/coverage/versions | listCoverageVersions | artifact:read | no | response:CoverageVersionsResponse |
POST /api/v1/artifacts/{artifactId}/figure | recastArtifactFigure | artifact:publish | yes | request:FigureRecastRequest |
POST /api/v1/artifacts/{artifactId}/finalize | finalizeArtifact | artifact:publish | yes | |
POST /api/v1/artifacts/{artifactId}/finalize/retry | retryArtifactFinalize | artifact:publish | yes | response:ArtifactFinalizeAck |
GET /api/v1/artifacts/{artifactId}/location-plates | getArtifactLocationPlates | artifact:read | no | response:LocationPlatesEnvelope |
POST /api/v1/artifacts/{artifactId}/location-plates/{plateKey}/remake | remakeArtifactLocationPlate | artifact:publish | yes | request:LocationPlateRemakeRequest<br>response:LocationPlateRemakeEnvelope |
GET /api/v1/artifacts/{artifactId}/music | getArtifactMusic | artifact:read | no | response:MusicStatusEnvelope |
POST /api/v1/artifacts/{artifactId}/music | generateArtifactMusic | artifact:publish | yes | request:MusicGenerateRequest<br>response:MusicGenerateResponse |
GET /api/v1/artifacts/{artifactId}/noun-imagery | getArtifactNounImagery | artifact:read | no | response:TableReadNounImageryResponse |
POST /api/v1/artifacts/{artifactId}/noun-imagery | generateArtifactNounImagery | artifact:publish | yes | request:TableReadNounImageryRequest<br>response:TableReadNounImageryQueued |
POST /api/v1/artifacts/{artifactId}/performance | updateArtifactPerformance | artifact:publish | yes | request:PerformanceDirectiveRequest<br>response:PerformanceDirectiveResponse |
GET /api/v1/artifacts/{artifactId}/publish-state | getArtifactPublishState | artifact:read | no | response:ArtifactPublishState |
POST /api/v1/artifacts/{artifactId}/publish-state | setArtifactPublishState | artifact:publish | no | request:ArtifactPublishStateRequest<br>response:ArtifactPublishStateResult |
POST /api/v1/artifacts/{artifactId}/refine | refineArtifact | artifact:publish | yes | request:RefineArtifactRequest<br>response:RefineArtifactEnvelope |
GET /api/v1/artifacts/{artifactId}/render-video | getArtifactRenderStatus | artifact:read | no | response:ArtifactRenderStatus |
POST /api/v1/artifacts/{artifactId}/render-video | renderArtifactVideo | artifact:publish | yes | response:ArtifactRenderAck |
GET /api/v1/artifacts/{artifactId}/review-items | listLineReviews | artifact:read | no | response:LineReviewList |
POST /api/v1/artifacts/{artifactId}/review-items | flagLineForReview | artifact:publish | no | request:LineReviewFlag<br>response:LineReviewFlagResult |
DELETE /api/v1/artifacts/{artifactId}/review-items/{entryIndex} | unflagLineForReview | artifact:publish | no | |
POST /api/v1/artifacts/{artifactId}/revoice-line | revoiceArtifactLine | artifact:publish | yes | request:RevoiceLineRequest<br>response:RevoiceLineEnvelope |
POST /api/v1/artifacts/{artifactId}/revoice | revoiceArtifactCharacter | artifact:publish | yes | request:RevoiceRequest<br>response:RevoiceEnvelope |
GET /api/v1/artifacts/{artifactId}/script | getArtifactScript | artifact:read | no | response:ArtifactScriptContentEnvelope |
POST /api/v1/artifacts/{artifactId}/script | replaceArtifactScript | artifact:publish | yes | request:ArtifactScriptReplacementRequest<br>response:StoryJobEnvelope |
GET /api/v1/artifacts/{artifactId}/sfx | listArtifactSfx | artifact:read | no | response:SfxListResponse |
POST /api/v1/artifacts/{artifactId}/sfx | manageArtifactSfx | artifact:publish | yes | request:SfxManageRequest<br>response:SfxMutationResponse |
GET /api/v1/artifacts/{artifactId}/soundscapes | listArtifactSoundscapes | artifact:read | no | response:TableReadSoundscapesResponse |
POST /api/v1/artifacts/{artifactId}/soundscapes | manageArtifactSoundscapes | artifact:publish | yes | request:TableReadSoundscapeRequest<br>response:TableReadSoundscapeResponse |
POST /api/v1/artifacts/{artifactId}/theater-backdrop-prompts | writeArtifactTheaterBackdropPrompts | artifact:publish | yes | request:TableReadBackdropPromptsRequest<br>response:TableReadBackdropPromptsResponse |
POST /api/v1/artifacts/{artifactId}/theater-canvas | applyTheaterCanvas | artifact:publish | yes | request:TheaterCanvasRequest<br>response:TheaterMotionResult |
POST /api/v1/artifacts/{artifactId}/theater-fx-cues | generateArtifactTheaterFxCues | artifact:publish | yes | request:TableReadFxCuesRequest<br>response:TableReadFxCuesResponse |
POST /api/v1/artifacts/{artifactId}/theater-motion-clip | generateTheaterMotionClip | artifact:publish | yes | request:TheaterMotionClipRequest<br>response:TheaterMotionResult |
POST /api/v1/artifacts/{artifactId}/theater-motion | directTheaterMotionBeat | artifact:publish | yes | request:TheaterMotionBeatRequest<br>response:TheaterMotionResult |
POST /api/v1/artifacts/{artifactId}/theater-scene | updateArtifactTheaterScene | artifact:publish | yes | request:TableReadTheaterSceneRequest<br>response:TableReadTheaterSceneResponse |
GET /api/v1/artifacts/{artifactId}/theater | getArtifactTheater | artifact:read | no | response:TableReadTheaterResponse |
POST /api/v1/artifacts/{artifactId}/theater | updateArtifactTheater | artifact:publish | yes | request:TableReadTheaterUpdateRequest<br>response:TableReadTheaterUpdateResponse |
POST /api/v1/artifacts/{artifactId}/update-recording | updateArtifactRecording | artifact:publish | yes | request:UpdateRecordingRequest<br>response:UpdateRecordingEnvelope |
POST /api/v1/artifacts/{artifactId}/voice-modification | modifyArtifactVoice | artifact:publish | yes | request:VoiceModificationRequest<br>response:VoiceModificationResponse |
POST /api/v1/artifacts/{artifactId}/voice | recastArtifactVoice | artifact:publish | yes | |
POST /api/v1/artifacts/{artifactId}/voice/restore | restoreArtifactVoice | artifact:publish | yes | request:VoiceRestoreRequest<br>response:VoiceRestoreResponse |
GET /api/v1/story-jobs/{jobId}/artifacts | listStoryJobArtifacts | artifact:read | no | response:StoryArtifactListResponse |
Brand
| Endpoint | Operation ID | Scope | Idempotent | Schemas |
|---|---|---|---|---|
POST /api/v1/brand-coverage | analyzeBrandCoverage | story:write | yes | request:BrandCoverageRequest<br>response:BrandCoverageAck |
GET /api/v1/brand-coverage/{reportId} | getBrandCoverage | story:read | no | response:BrandCoverageReport |
POST /api/v1/brand-narrative | generateBrandNarrative | story:write | yes | request:BrandNarrativeRequest<br>response:BrandNarrativeResponse |
Credits
| Endpoint | Operation ID | Scope | Idempotent | Schemas |
|---|---|---|---|---|
GET /api/v1/credits | getCredits | credits:read | no | response:CreditsSummary |
GET /api/v1/generation-health | getGenerationHealth | story:read | no | response:GenerationHealthEnvelope |
Discovery
| Endpoint | Operation ID | Scope | Idempotent | Schemas |
|---|---|---|---|---|
GET /api/v1/agent-guidance | getAgentGuidance | story:read | no | response:AgentGuidance |
GET /api/v1/capabilities | getCapabilities | story:read | no | response:Capabilities |
DELETE /api/v1/device/credential | revokeDeviceCredential | no | ||
POST /api/v1/device/credential | exchangeDeviceCode | no | ||
GET /api/v1/examples | getApiExamples | no |
Jobs
| Endpoint | Operation ID | Scope | Idempotent | Schemas |
|---|---|---|---|---|
GET /api/v1/background-activity | getBackgroundActivity | story:read | no | |
GET /api/v1/screenplay-drafts/{scriptUploadId} | getScreenplayDraft | story:read | no | response:ScreenplayDraftStatus |
POST /api/v1/screenplay-drafts/{scriptUploadId} | startScreenplayDraft | story:write | yes | response:ScreenplayDraftStatus |
GET /api/v1/story-jobs | listStoryJobs | story:read | no | response:StoryJobList |
POST /api/v1/story-jobs | createStoryJob | story:write | yes | request:StoryJobCreate<br>response:StoryJobEnvelope |
GET /api/v1/story-jobs/{jobId} | getStoryJob | story:read | no | response:StoryJobEnvelope |
POST /api/v1/story-jobs/{jobId}/cancel | cancelStoryJob | story:write | yes | response:StoryJobEnvelope |
POST /api/v1/story-jobs/{jobId}/resume | resumeStoryJob | story:write | yes | response:StoryJobResumeResponse |
Library
| Endpoint | Operation ID | Scope | Idempotent | Schemas |
|---|---|---|---|---|
GET /api/v1/library/audio | listMyLibraryAudio | story:read | no | response:MyLibraryAudio |
POST /api/v1/library/audio | importMyLibraryAudio | story:write | yes | request:MyLibraryAudioImportRequest<br>response:MyLibraryAudioImportResponse |
PATCH /api/v1/library/audio/{assetId} | renameMyLibraryAudio | story:write | no | request:MyLibraryAudioRenameRequest<br>response:MyLibraryAudioRenameResponse |
GET /api/v1/library/images | listMyLibraryImages | story:read | no | response:MyLibraryImages |
Pitch Deck
| Endpoint | Operation ID | Scope | Idempotent | Schemas |
|---|---|---|---|---|
DELETE /api/v1/pitch-decks/{jobId} | deletePitchDeck | story:write | no | response:VideoDeleteResult |
PATCH /api/v1/pitch-decks/{jobId} | renamePitchDeck | story:write | no | request:ArtifactRenameRequest<br>response:RenamedVideo |
GET /api/v1/pitch-decks/{jobId}/bell-lane | getPitchDeckBellLane | story:read | no | |
POST /api/v1/pitch-decks/{jobId}/bell-lane | strikePitchDeckBellOnTheBeat | story:write | yes | |
POST /api/v1/pitch-decks/{jobId}/bell-lane/remove | removePitchDeckBellLane | story:write | yes | |
GET /api/v1/pitch-decks/{jobId}/camera | getPitchDeckCamera | story:read | no | response:CameraView |
POST /api/v1/pitch-decks/{jobId}/camera | setPitchDeckCamera | story:write | yes | request:CameraSetRequest<br>response:CameraSetAck |
POST /api/v1/pitch-decks/{jobId}/cancel-render | cancelPitchDeckRender | artifact:publish | yes | response:RenderCancelResult |
GET /api/v1/pitch-decks/{jobId}/chapters | getPitchDeckChapters | story:read | no | response:PitchDeckChapters |
POST /api/v1/pitch-decks/{jobId}/chapters/{chapterIndex}/abandon-take | abandonPitchDeckTake | artifact:publish | yes | request:TakeAbandonRequest<br>response:TakeAbandonResult |
POST /api/v1/pitch-decks/{jobId}/chapters/{chapterIndex}/drop-line | dropPitchDeckChapterLine | story:write | yes | request:PitchDeckDropLineRequest<br>response:PitchDeckDropLineAnswer |
POST /api/v1/pitch-decks/{jobId}/chapters/{chapterIndex}/repair-take | repairPitchDeckTake | artifact:publish | yes | request:TakeRepairRequest<br>response:TakeRepairAnswer |
POST /api/v1/pitch-decks/{jobId}/chapters/{chapterIndex}/select-take | selectPitchDeckTake | story:write | yes | request:PitchDeckSelectTakeRequest<br>response:PitchDeckSelectTakeAck |
POST /api/v1/pitch-decks/{jobId}/chapters/{chapterIndex}/stage-voice-over | stageVoiceOverPitchDeckTake | artifact:publish | yes | request:StageVoiceOverRequest<br>response:StageVoiceOverAnswer |
POST /api/v1/pitch-decks/{jobId}/chapters/{chapterIndex}/take-repair-override | overridePitchDeckTakeRepair | story:write | yes | request:TakeRepairOverrideRequest<br>response:TakeRepairOverrideAck |
POST /api/v1/pitch-decks/{jobId}/chapters/{chapterIndex}/trim-take | trimPitchDeckTake | story:write | yes | request:TakeTrimRequest<br>response:TakeTrimQueued |
POST /api/v1/pitch-decks/{jobId}/chapters/insert | insertPitchDeckChapter | story:write | yes | request:PitchDeckInsertChapterRequest<br>response:PitchDeckInsertChapterAck |
POST /api/v1/pitch-decks/{jobId}/chapters/refine | refinePitchDeckChapter | story:write | yes | request:PitchDeckChapterRefineRequest<br>response:PitchDeckChapterRefineAck |
POST /api/v1/pitch-decks/{jobId}/chapters/remove | removePitchDeckChapter | story:write | yes | request:PitchDeckRemoveChapterRequest<br>response:PitchDeckRemoveChapterAck |
POST /api/v1/pitch-decks/{jobId}/chapters/render-clip | renderPitchDeckChapterClip | artifact:publish | yes | request:PitchDeckRenderClipRequest<br>response:PitchDeckRenderAnswer |
POST /api/v1/pitch-decks/{jobId}/chapters/reorder | reorderPitchDeckChapters | story:write | yes | request:PitchDeckReorderChaptersRequest<br>response:PitchDeckReorderChaptersAck |
POST /api/v1/pitch-decks/{jobId}/closing-sequence | updatePitchDeckClosingSequence | story:write | yes | request:PitchDeckClosingSequenceUpdateRequest<br>response:PitchDeckClosingSequenceUpdateAck |
POST /api/v1/pitch-decks/{jobId}/duplicate | duplicatePitchDeck | story:write | yes | request:PitchDeckDuplicateRequest<br>response:PitchDeckDuplicate |
POST /api/v1/pitch-decks/{jobId}/finish | finishPitchDeck | artifact:publish | yes | request:PitchDeckFinishRequest<br>response:PitchDeckFinishAnswer |
GET /api/v1/pitch-decks/{jobId}/intent | getPitchDeckIntent | story:read | no | response:VideoIntent |
POST /api/v1/pitch-decks/{jobId}/intent | updatePitchDeckIntent | story:write | yes | request:VideoIntentEditRequest<br>response:VideoIntentEditAck |
POST /api/v1/pitch-decks/{jobId}/interludes | updatePitchDeckInterludes | story:write | yes | request:PitchDeckInterludesUpdateRequest<br>response:PitchDeckInterludesUpdateAck |
GET /api/v1/pitch-decks/{jobId}/look | getPitchDeckLook | story:read | no | response:FinishView |
POST /api/v1/pitch-decks/{jobId}/look | setPitchDeckLook | story:write | yes | request:FinishSetRequest<br>response:FinishSetAck |
POST /api/v1/pitch-decks/{jobId}/more-fixes | allowMorePitchDeckFixes | artifact:publish | yes | request:MoreFixesRequest<br>response:MoreFixesAnswer |
GET /api/v1/pitch-decks/{jobId}/motion-blur | getPitchDeckMotionBlur | story:read | no | response:MotionBlurView |
POST /api/v1/pitch-decks/{jobId}/motion-blur | setPitchDeckMotionBlur | story:write | yes | request:MotionBlurSetRequest<br>response:MotionBlurSetAck |
GET /api/v1/pitch-decks/{jobId}/narrator | getPitchDeckNarrator | story:read | no | |
POST /api/v1/pitch-decks/{jobId}/narrator | setPitchDeckNarrator | story:write | yes | |
POST /api/v1/pitch-decks/{jobId}/pdf | requestPitchDeckPdf | story:write | yes | response:PitchDeckPdf |
POST /api/v1/pitch-decks/{jobId}/plan/approve | approvePitchDeckPlan | story:write | yes | request:PitchDeckPlanApproveRequest<br>response:PitchDeckRenderAnswer |
POST /api/v1/pitch-decks/{jobId}/publish | publishPitchDeck | story:write | yes | response:PitchDeckPublish |
GET /api/v1/pitch-decks/{jobId}/quiet-track | getPitchDeckQuietTrack | story:read | no | response:QuietTrackView |
POST /api/v1/pitch-decks/{jobId}/quiet-track | setPitchDeckQuietTrack | story:write | yes | request:QuietTrackSetRequest<br>response:QuietTrackSetAck |
POST /api/v1/pitch-decks/{jobId}/reassemble | reassemblePitchDeck | story:write | yes | response:RenderReassembleAck |
GET /api/v1/pitch-decks/{jobId}/resolution | getPitchDeckResolution | story:read | no | |
POST /api/v1/pitch-decks/{jobId}/resolution | setPitchDeckResolution | story:write | yes | |
GET /api/v1/pitch-decks/{jobId}/score-to-picture | getPitchDeckScoreToPicture | story:read | no | |
POST /api/v1/pitch-decks/{jobId}/score-to-picture | scorePitchDeckToPicture | artifact:publish | yes | |
POST /api/v1/pitch-decks/{jobId}/score-to-picture/revert | revertPitchDeckScoreToPicture | story:write | yes | |
GET /api/v1/pitch-decks/{jobId}/storyboard/audio-timeline | getPitchDeckAudioTimeline | story:read | no | |
POST /api/v1/pitch-decks/{jobId}/storyboard/audio-timeline | editPitchDeckAudioTimeline | story:write | yes | |
GET /api/v1/pitch-decks/{jobId}/storyboard/audio | getPitchDeckSceneAudio | story:read | no | |
GET /api/v1/pitch-decks/{jobId}/storyboard/coverage | getPitchDeckStoryboardCoverage | story:read | no | |
POST /api/v1/pitch-decks/{jobId}/storyboard/coverage | generatePitchDeckStoryboardCoverage | story:write | yes | |
POST /api/v1/pitch-decks/{jobId}/storyboard/direct-text-motion | directPitchDeckTextMotion | story:write | yes | request:PitchDeckDirectTextMotionRequest<br>response:PitchDeckDirectTextMotionAck |
POST /api/v1/pitch-decks/{jobId}/storyboard/music | editPitchDeckSceneMusic | story:write | yes | |
POST /api/v1/pitch-decks/{jobId}/storyboard/regenerate-text | regeneratePitchDeckText | artifact:publish | yes | request:PitchDeckChapterIndexRequest<br>response:PitchDeckTextRenderAnswer |
POST /api/v1/pitch-decks/{jobId}/storyboard/sfx | editPitchDeckSceneSfx | story:write | yes | |
GET /api/v1/pitch-decks/{jobId}/storyboard/sound-map | getPitchDeckSoundMap | story:read | no | |
POST /api/v1/pitch-decks/{jobId}/title-sequence | updatePitchDeckTitleSequence | story:write | yes | request:PitchDeckTitleSequenceUpdateRequest<br>response:PitchDeckTitleSequenceUpdateAck |
POST /api/v1/pitch-decks/{jobId}/title-sequence/motion-closes | renderPitchDeckTitleMotionCloses | artifact:publish | yes | request:PitchDeckTitleMotionClosesRequest<br>response:PitchDeckTitleMotionClosesAnswer |
POST /api/v1/pitch-decks/{jobId}/unpublish | unpublishPitchDeck | story:write | yes | response:PitchDeckUnpublish |
GET /api/v1/pitch-decks/{jobId}/versions | listPitchDeckVersions | story:read | no | response:PitchDeckVersions |
GET /api/v1/pitch-decks/{jobId}/versions/{number} | getPitchDeckVersion | story:read | no | response:PitchDeckVersionRead |
POST /api/v1/pitch-decks/{jobId}/versions/{number}/zip | requestPitchDeckVersionZip | story:write | yes | response:VideoVersionZipAck |
GET /api/v1/pitch-decks/{jobId}/video-coverage | getPitchDeckVideoCoverage | story:read | no | response:VideoCoverageEnvelope |
POST /api/v1/pitch-decks/{jobId}/video-coverage | generatePitchDeckVideoCoverage | story:write | yes | request:VideoCoverageGenerateRequest<br>response:VideoCoverageEnvelope |
POST /api/v1/pitch-decks/{jobId}/video-export | exportPitchDeckVideo | story:write | yes | response:PitchDeckExportAck |
GET /api/v1/pitch-decks/{jobId}/video-model | getPitchDeckVideoModel | story:read | no | response:PitchDeckVideoModelView |
POST /api/v1/pitch-decks/{jobId}/video-model | setPitchDeckVideoModel | story:write | yes | request:PitchDeckVideoModelSetRequest<br>response:PitchDeckVideoModelSetAck |
POST /api/v1/pitch-decks/{jobId}/visual-grammar | updatePitchDeckVisualGrammar | story:write | yes | request:PitchDeckVisualGrammarUpdateRequest<br>response:PitchDeckVisualGrammarUpdateAck |
Plans
| Endpoint | Operation ID | Scope | Idempotent | Schemas |
|---|---|---|---|---|
GET /api/v1/story-plans/{planId} | getStoryPlan | story:read | no | response:StoryPlanEnvelopeResponse |
PATCH /api/v1/story-plans/{planId} | renameStoryPlan | story:write | no | request:ArtifactRenameRequest<br>response:StoryPlanEnvelopeResponse |
POST /api/v1/story-plans/{planId}/approve | approveStoryPlan | story:write | yes | request:StoryPlanApprovalRequest<br>response:StoryPlanEnvelopeResponse |
POST /api/v1/story-plans/{planId}/reject | rejectStoryPlan | story:write | yes | response:StoryPlanEnvelopeResponse |
POST /api/v1/story-plans/{planId}/resume | resumeStoryPlan | story:write | yes | response:StoryPlanEnvelopeResponse |
GET /api/v1/story-projects/{projectId}/story-plans | listStoryPlans | story:read | no | response:StoryPlanList |
POST /api/v1/story-projects/{projectId}/story-plans | createStoryPlan | story:write | yes | request:StoryPlanCreate<br>response:StoryPlanEnvelopeResponse |
Projects
| Endpoint | Operation ID | Scope | Idempotent | Schemas |
|---|---|---|---|---|
GET /api/v1/story-projects | listStoryProjects | story:read | no | response:StoryProjectList |
POST /api/v1/story-projects | createStoryProject | story:write | yes | request:StoryProjectCreate<br>response:StoryProjectEnvelope |
DELETE /api/v1/story-projects/{projectId} | deleteStoryProject | story:write | no | response:ProjectDeletionResult |
GET /api/v1/story-projects/{projectId} | getStoryProject | story:read | no | response:StoryProjectDetailEnvelope |
PATCH /api/v1/story-projects/{projectId} | updateStoryProject | story:write | no | request:StoryProjectPatch<br>response:StoryProjectEnvelope |
GET /api/v1/story-projects/{projectId}/cast-canon | getProjectCastCanon | story:read | no | response:CastCanonEnvelope |
PATCH /api/v1/story-projects/{projectId}/cast-canon | saveProjectCastCanon | story:write | no | request:CastCanonPatch<br>response:CastCanonEnvelope |
GET /api/v1/story-projects/{projectId}/cast-look | getProjectCastLook | story:read | no | response:CastLookRead |
PATCH /api/v1/story-projects/{projectId}/cast-look | setProjectCastLook | story:write | no | request:CastLookPatch<br>response:CastLookSaved |
POST /api/v1/story-projects/{projectId}/cast-portraits/choose | chooseProjectCastPortrait | story:write | yes | request:CastPortraitChooseRequest<br>response:CastPortraitChooseResponse |
POST /api/v1/story-projects/{projectId}/cast-portraits/design | designProjectCastPortraits | artifact:publish | yes | request:CastPortraitDesignRequest<br>response:CastPortraitDesignEnvelope |
GET /api/v1/story-projects/{projectId}/cast-portraits/designs | listProjectCastPortraitDesigns | story:read | no | response:CastPortraitDesignListResponse |
GET /api/v1/story-projects/{projectId}/cast-portraits/designs/{designId}/coverage | getProjectCastPortraitCoverage | story:read | no | response:CastingPortraitCoverageResponse |
POST /api/v1/story-projects/{projectId}/cast-portraits/designs/{designId}/coverage | generateProjectCastPortraitCoverage | story:write | yes | request:CastingCoverageGenerateRequest<br>response:CastingPortraitCoverageResponse |
PATCH /api/v1/story-projects/{projectId}/cast-voice | setProjectCastVoice | story:write | no | request:CastVoiceSetRequest<br>response:CastVoiceSetResponse |
POST /api/v1/story-projects/{projectId}/cast-voice/choose | chooseProjectCastVoice | story:write | yes | request:CastVoiceChooseRequest<br>response:CastVoiceChooseResponse |
POST /api/v1/story-projects/{projectId}/cast-voice/design | designProjectCastVoice | artifact:publish | yes | request:CastVoiceDesignRequest<br>response:CastVoiceDesignEnvelope |
GET /api/v1/story-projects/{projectId}/cast-voice/designs | listProjectCastVoiceDesigns | story:read | no | response:CastVoiceDesignListResponse |
GET /api/v1/story-projects/{projectId}/cast-voice/designs/{designId}/coverage | getProjectCastVoiceCoverage | story:read | no | response:CastingCoverageResponse |
POST /api/v1/story-projects/{projectId}/cast-voice/designs/{designId}/coverage | generateProjectCastVoiceCoverage | story:write | yes | request:CastingCoverageGenerateRequest<br>response:CastingCoverageResponse |
GET /api/v1/story-projects/{projectId}/cast | getProjectCast | story:read | no | response:ProjectCastReadEnvelope |
PATCH /api/v1/story-projects/{projectId}/cast | updateEpisodeCharacterDirection | story:write | no | request:EpisodeCharacterDirectionRequest<br>response:EpisodeCharacterDirectionResult |
POST /api/v1/story-projects/{projectId}/cast | generateProjectCast | artifact:publish | yes | request:GenerateProjectCastRequest<br>response:ProjectCastEnvelope |
GET /api/v1/story-projects/{projectId}/episodes | listProjectEpisodes | story:read | no | response:ProjectEpisodesResponse |
POST /api/v1/story-projects/{projectId}/episodes | startPlannedEpisode | story:write | yes | request:StartPlannedEpisodeRequest<br>response:StartPlannedEpisodeResponse |
GET /api/v1/story-projects/{projectId}/mood-board | getProjectMoodBoard | story:read | no | response:MoodBoardEnvelope |
POST /api/v1/story-projects/{projectId}/mood-board/frames | keepProjectMoodBoardFrame | story:write | yes | request:MoodBoardFrameKeepRequest<br>response:MoodBoardEnvelope |
DELETE /api/v1/story-projects/{projectId}/mood-board/frames/{frameId} | dropProjectMoodBoardFrame | story:write | no | response:MoodBoardEnvelope |
POST /api/v1/story-projects/{projectId}/mood-board/render-frames | renderProjectMoodBoardFrames | artifact:publish | yes | request:MoodBoardRenderRequest<br>response:MoodBoardRenderEnvelope |
GET /api/v1/story-projects/{projectId}/outline-map | getProjectOutlineMap | story:read | no | response:OutlineMapEnvelope |
PATCH /api/v1/story-projects/{projectId}/outline-map | saveProjectEpisodeOutline | story:write | no | request:SaveEpisodeOutlineRequest<br>response:OutlineMapEnvelope |
GET /api/v1/story-projects/{projectId}/outline-map/coverage | getProjectOutlineCoverage | story:read | no | response:OutlineCoverageEnvelope |
POST /api/v1/story-projects/{projectId}/outline-map/coverage | generateProjectOutlineCoverage | story:write | yes | request:OutlineCoverageGenerateRequest<br>response:OutlineCoverageEnvelope |
POST /api/v1/story-projects/{projectId}/restore | restoreStoryProject | story:write | yes | response:ProjectRestoreResult |
POST /api/v1/story-projects/{projectId}/seasons | createProjectSeason | story:write | yes | request:ProjectSeasonCreateRequest<br>response:ProjectSeasonCreateResponse |
GET /api/v1/story-projects/{projectId}/series-bible | getProjectSeriesBible | story:read | no | response:SeriesBibleEnvelope |
PATCH /api/v1/story-projects/{projectId}/series-bible | saveProjectSeriesBible | story:write | no | request:SeriesBiblePatch<br>response:SeriesBibleSaveResult |
POST /api/v1/story-projects/{projectId}/series-bible | generateProjectSeriesBible | story:write | yes | request:SeriesBibleGenerateRequest<br>response:SeriesBibleSaveResult |
GET /api/v1/story-projects/{projectId}/series-bible/characters | getSeriesBibleCharacter | story:read | no | response:SeriesBibleCharacterResult |
PATCH /api/v1/story-projects/{projectId}/series-bible/characters | updateSeriesBibleCharacter | story:write | no | request:SeriesBibleCharacterUpdateRequest<br>response:SeriesBibleCharacterResult |
GET /api/v1/story-projects/{projectId}/series-bible/coverage | getProjectSeriesBibleCoverage | story:read | no | response:SeriesBibleCoverageEnvelope |
POST /api/v1/story-projects/{projectId}/series-bible/coverage | generateProjectSeriesBibleCoverage | story:write | yes | request:SeriesBibleCoverageGenerateRequest<br>response:SeriesBibleCoverageEnvelope |
GET /api/v1/story-projects/{projectId}/sounds | listProjectSounds | story:read | no | |
GET /api/v1/story-projects/deleted | listDeletedStoryProjects | story:read | no | response:DeletedStoryProjectList |
POST /api/v1/story-projects/from-script | createStoryProjectFromScript | story:write | yes | request:StoryProjectFromScript<br>response:StoryProjectFromScriptEnvelope |
Publishing
| Endpoint | Operation ID | Scope | Idempotent | Schemas |
|---|---|---|---|---|
GET /api/v1/publishing-feeds/{seriesSlug}.xml | getPublishingRssFeed | no | ||
GET /api/v1/publishing-provider-configs | listPublishingProviderConfigs | publishing:connect | no | response:PublishingProviderConfigList |
POST /api/v1/publishing-provider-configs | upsertPublishingProviderConfig | publishing:connect | yes | request:PublishingProviderConfigUpsertRequest<br>response:PublishingProviderConfigEnvelope |
GET /api/v1/publishing-releases/{releaseId} | getPublishingRelease | publishing:read | no | response:PublishingReleaseEnvelope |
PATCH /api/v1/publishing-releases/{releaseId} | updatePublishingRelease | publishing:write | no | request:PublishingReleaseUpdateRequest<br>response:PublishingReleaseEnvelope |
POST /api/v1/publishing-releases/{releaseId}/cancel | cancelPublishingRelease | publishing:publish | yes | response:PublishingReleaseEnvelope |
POST /api/v1/publishing-releases/{releaseId}/description/generate | generatePublishingReleaseDescription | publishing:write | yes | response:PublishingReleaseEnvelope |
POST /api/v1/publishing-releases/{releaseId}/publish | publishPublishingRelease | publishing:publish | yes | response:PublishingReleaseEnvelope |
POST /api/v1/publishing-releases/{releaseId}/refresh-media | refreshPublishedReleaseMedia | publishing:publish | yes | request:PublishingReleaseRefreshMediaRequest<br>response:PublishingReleaseEnvelope |
POST /api/v1/publishing-releases/{releaseId}/schedule | schedulePublishingRelease | publishing:publish | yes | response:PublishingReleaseEnvelope |
GET /api/v1/publishing-releases/{releaseId}/targets | listPublishingReleaseTargets | publishing:read | no | response:PublishingTargetList |
POST /api/v1/publishing-releases/{releaseId}/unpublish | unpublishPublishingRelease | publishing:publish | yes | response:PublishingReleaseEnvelope |
GET /api/v1/publishing-series/{seriesId} | getPublishingSeries | publishing:read | no | response:PublishingSeriesEnvelope |
PATCH /api/v1/publishing-series/{seriesId} | updatePublishingSeries | publishing:write | no | request:PublishingSeriesUpdateRequest<br>response:PublishingSeriesEnvelope |
GET /api/v1/publishing-series/{seriesId}/analytics | listPublishingAnalyticsSnapshots | publishing:analytics | no | response:PublishingAnalyticsList |
GET /api/v1/publishing-series/{seriesId}/destinations | listPublishingDestinations | publishing:read | no | response:PublishingDestinationList |
POST /api/v1/publishing-series/{seriesId}/destinations | upsertPublishingDestination | publishing:connect | yes | request:PublishingDestinationUpsertRequest<br>response:PublishingDestinationEnvelope |
GET /api/v1/publishing-series/{seriesId}/episodes | listShowEpisodes | publishing:read | no | response:ShowEpisodeList |
POST /api/v1/publishing-series/{seriesId}/episodes | produceShowEpisode | publishing:write | yes | response:ShowEpisodeProduce |
POST /api/v1/publishing-series/{seriesId}/generate-cover-art | generatePublishingSeriesCoverArt | publishing:write | no | request:PublishingCoverArtGenerateRequest<br>response:PublishingCoverArtGenerateResponse |
GET /api/v1/publishing-series/{seriesId}/quote | getShowQuote | publishing:read | no | response:ShowQuote |
GET /api/v1/publishing-series/{seriesId}/releases | listPublishingReleases | publishing:read | no | response:PublishingReleaseList |
POST /api/v1/publishing-series/{seriesId}/releases | createPublishingRelease | publishing:write | yes | request:PublishingReleaseCreateRequest<br>response:PublishingReleaseEnvelope |
POST /api/v1/publishing-series/{seriesId}/source-preview | previewShowSources | publishing:read | no | response:ShowSourcePreview |
GET /api/v1/show-source-templates | listShowSourceTemplates | publishing:read | no | response:ShowSourceCatalog |
GET /api/v1/story-projects/{projectId}/publishing-series | listPublishingSeries | publishing:read | no | response:PublishingSeriesList |
POST /api/v1/story-projects/{projectId}/publishing-series | createPublishingSeries | publishing:write | yes | request:PublishingSeriesCreateRequest<br>response:PublishingSeriesEnvelope |
Scripts
| Endpoint | Operation ID | Scope | Idempotent | Schemas |
|---|---|---|---|---|
POST /api/v1/scripts | uploadScript | story:write | yes | request:ScriptUploadRequest<br>response:ScriptUploadResponse |
DELETE /api/v1/scripts/{scriptUploadId} | deleteScript | story:write | no | |
GET /api/v1/scripts/{scriptUploadId} | getScript | story:read | no | response:WriterScriptEnvelope |
PATCH /api/v1/scripts/{scriptUploadId} | updateScript | story:write | no | request:ScriptMetadataPatch<br>response:WriterScriptEnvelope |
PATCH /api/v1/scripts/{scriptUploadId}/content | writeScriptContent | story:write | no | request:ScriptContentWriteRequest<br>response:ScriptContentWriteResponse |
GET /api/v1/scripts/{scriptUploadId}/export | exportScript | story:read | no | response:ScriptExportResponse |
POST /api/v1/scripts/{scriptUploadId}/finish | finishDraft | story:write | yes | request:FinishDraftRequest<br>response:FinishDraftResponse |
GET /api/v1/scripts/{scriptUploadId}/lines/{entryIndex} | getScriptLineTuning | story:read | no | response:ScriptLineTuning |
PATCH /api/v1/scripts/{scriptUploadId}/lines/{entryIndex} | updateScriptLine | story:write | no | request:ScriptLineEdit<br>response:ScriptLineTuning |
GET /api/v1/scripts/{scriptUploadId}/overrides | getScriptOverrides | story:read | no | response:ScriptOverrides |
PATCH /api/v1/scripts/{scriptUploadId}/overrides | updateScriptOverrides | story:write | no | request:ScriptOverridesPatch<br>response:ScriptOverrides |
GET /api/v1/scripts/{scriptUploadId}/share | getScriptShare | story:read | no | response:ScriptShareState |
POST /api/v1/scripts/{scriptUploadId}/share | createScriptShare | artifact:publish | no | request:CreateScriptShareRequest<br>response:CreateScriptShareResult |
POST /api/v1/scripts/{scriptUploadId}/table-read/characters/remove | removeScriptTableReadCharacter | story:write | no | request:RemoveCharacterRequest<br>response:RemoveCharacterResult |
POST /api/v1/scripts/{scriptUploadId}/table-read/prepare | prepareScriptTableRead | artifact:publish | yes | request:PrepareTableReadRequest<br>response:TableReadPrepareEnvelope |
POST /api/v1/scripts/{scriptUploadId}/table-read/retry-analysis | retryScriptTableReadAnalysis | story:write | no | response:RetryCreativeAnalysisResult |
POST /api/v1/scripts/{scriptUploadId}/versions | uploadScriptVersion | story:write | yes | request:ScriptVersionUploadRequest |
DELETE /api/v1/scripts/{scriptUploadId}/versions/{versionId} | deleteScriptVersion | story:write | no | response:ScriptVersionDeleteResponse |
Seasons
| Endpoint | Operation ID | Scope | Idempotent | Schemas |
|---|---|---|---|---|
GET /api/v1/season-runs/{runId} | getStorySeasonRun | story:read | no | response:SeasonRunEnvelope |
POST /api/v1/season-runs/{runId}/advance | advanceStorySeasonRun | story:write, artifact:publish, publishing:write | yes | request:SeasonRunAdvanceRequest<br>response:SeasonRunEnvelope |
POST /api/v1/season-runs/{runId}/approve | approveStorySeasonMap | story:write | yes | request:SeasonRunApprovalRequest<br>response:SeasonRunEnvelope |
PATCH /api/v1/season-runs/{runId}/episodes/{episodeId} | updateStorySeasonEpisode | story:write | no | request:SeasonRunEpisodeUpdateRequest<br>response:SeasonRunEnvelope |
GET /api/v1/story-projects/{projectId}/season-runs | listStorySeasonRuns | story:read | no | response:SeasonRunList |
POST /api/v1/story-projects/{projectId}/season-runs | createStorySeasonRun | story:write, publishing:write | yes | request:SeasonRunCreateRequest<br>response:SeasonRunEnvelope |
Sources
| Endpoint | Operation ID | Scope | Idempotent | Schemas |
|---|---|---|---|---|
GET /api/v1/story-projects/{projectId}/sources | listStorySources | source:read | no | response:StorySourceList |
POST /api/v1/story-projects/{projectId}/sources | createStorySource | source:write | yes | request:StorySourceCreate<br>response:StorySourceCreateResult |
DELETE /api/v1/story-projects/{projectId}/sources/{sourceId} | deleteStorySource | source:write | no | response:DeletionAck |
GET /api/v1/story-projects/{projectId}/sources/{sourceId} | getStorySource | source:read | no | response:StorySourceEnvelope |
Table Read
| Endpoint | Operation ID | Scope | Idempotent | Schemas |
|---|---|---|---|---|
GET /api/v1/table-read/performance-booth | getPerformanceBoothStatus | story:read | no | |
POST /api/v1/table-read/performance-booth | startPerformanceBoothSession | story:write | yes |
Trailer
| Endpoint | Operation ID | Scope | Idempotent | Schemas |
|---|---|---|---|---|
DELETE /api/v1/trailers/{jobId} | deleteTrailer | story:write | no | response:VideoDeleteResult |
PATCH /api/v1/trailers/{jobId} | renameTrailer | story:write | no | request:ArtifactRenameRequest<br>response:RenamedVideo |
GET /api/v1/trailers/{jobId}/aspect | getTrailerAspect | story:read | no | |
POST /api/v1/trailers/{jobId}/aspect | setTrailerAspect | story:write | yes | |
POST /api/v1/trailers/{jobId}/audio-policy | setTrailerAudioPolicy | story:write | yes | |
GET /api/v1/trailers/{jobId}/beats | getTrailerBeats | story:read | no | response:TrailerBeats |
POST /api/v1/trailers/{jobId}/beats/{beatIndex}/abandon-take | abandonTrailerTake | artifact:publish | yes | request:TakeAbandonRequest<br>response:TakeAbandonResult |
POST /api/v1/trailers/{jobId}/beats/{beatIndex}/drop-line | dropTrailerBeatLine | story:write | yes | request:TrailerDropLineRequest<br>response:TrailerDropLineAnswer |
POST /api/v1/trailers/{jobId}/beats/{beatIndex}/repair-take | repairTrailerTake | artifact:publish | yes | request:TakeRepairRequest<br>response:TakeRepairAnswer |
POST /api/v1/trailers/{jobId}/beats/{beatIndex}/select-take | selectTrailerTake | story:write | yes | request:TrailerSelectTakeRequest<br>response:TrailerSelectTakeAck |
POST /api/v1/trailers/{jobId}/beats/{beatIndex}/stage-voice-over | stageVoiceOverTrailerTake | artifact:publish | yes | request:StageVoiceOverRequest<br>response:StageVoiceOverAnswer |
POST /api/v1/trailers/{jobId}/beats/{beatIndex}/take-repair-override | overrideTrailerTakeRepair | story:write | yes | request:TakeRepairOverrideRequest<br>response:TakeRepairOverrideAck |
POST /api/v1/trailers/{jobId}/beats/{beatIndex}/trim-take | trimTrailerTake | story:write | yes | request:TakeTrimRequest<br>response:TakeTrimQueued |
POST /api/v1/trailers/{jobId}/beats/refine | refineTrailerBeat | story:write | yes | request:TrailerBeatRefineRequest<br>response:TrailerBeatPreview |
POST /api/v1/trailers/{jobId}/beats/render-clip | renderTrailerBeatClip | artifact:publish | yes | request:TrailerRenderClipRequest<br>response:TrailerRenderAnswer |
GET /api/v1/trailers/{jobId}/camera | getTrailerCamera | story:read | no | response:CameraView |
POST /api/v1/trailers/{jobId}/camera | setTrailerCamera | story:write | yes | request:CameraSetRequest<br>response:CameraSetAck |
POST /api/v1/trailers/{jobId}/cancel-render | cancelTrailerRender | artifact:publish | yes | response:RenderCancelResult |
POST /api/v1/trailers/{jobId}/closing-sequence | updateTrailerClosingSequence | story:write | yes | request:TrailerClosingSequenceUpdateRequest<br>response:TrailerClosingSequenceUpdateAck |
POST /api/v1/trailers/{jobId}/duplicate | duplicateTrailer | story:write | yes | request:TrailerDuplicateRequest<br>response:TrailerDuplicate |
POST /api/v1/trailers/{jobId}/finish | finishTrailer | artifact:publish | yes | request:TrailerFinishRequest<br>response:TrailerFinishAnswer |
GET /api/v1/trailers/{jobId}/intent | getTrailerIntent | story:read | no | response:VideoIntent |
POST /api/v1/trailers/{jobId}/intent | updateTrailerIntent | story:write | yes | request:VideoIntentEditRequest<br>response:VideoIntentEditAck |
POST /api/v1/trailers/{jobId}/interludes | updateTrailerInterludes | story:write | yes | request:TrailerInterludesUpdateRequest<br>response:TrailerInterludesUpdateAck |
GET /api/v1/trailers/{jobId}/look | getTrailerLook | story:read | no | response:FinishView |
POST /api/v1/trailers/{jobId}/look | setTrailerLook | story:write | yes | request:FinishSetRequest<br>response:FinishSetAck |
POST /api/v1/trailers/{jobId}/more-fixes | allowMoreTrailerFixes | artifact:publish | yes | request:MoreFixesRequest<br>response:MoreFixesAnswer |
GET /api/v1/trailers/{jobId}/motion-blur | getTrailerMotionBlur | story:read | no | response:MotionBlurView |
POST /api/v1/trailers/{jobId}/motion-blur | setTrailerMotionBlur | story:write | yes | request:MotionBlurSetRequest<br>response:MotionBlurSetAck |
GET /api/v1/trailers/{jobId}/narrator | getTrailerNarrator | story:read | no | |
POST /api/v1/trailers/{jobId}/narrator | setTrailerNarrator | story:write | yes | |
POST /api/v1/trailers/{jobId}/plan/approve | approveTrailerPlan | story:write | yes | request:TrailerPlanApproveRequest<br>response:TrailerRenderAnswer |
GET /api/v1/trailers/{jobId}/quiet-track | getTrailerQuietTrack | story:read | no | response:QuietTrackView |
POST /api/v1/trailers/{jobId}/quiet-track | setTrailerQuietTrack | story:write | yes | request:QuietTrackSetRequest<br>response:QuietTrackSetAck |
POST /api/v1/trailers/{jobId}/reassemble | reassembleTrailer | story:write | yes | response:RenderReassembleAck |
GET /api/v1/trailers/{jobId}/render-states | getTrailerRenderStates | story:read | no | response:TrailerBeatRenderStates |
GET /api/v1/trailers/{jobId}/resolution | getTrailerResolution | story:read | no | |
POST /api/v1/trailers/{jobId}/resolution | setTrailerResolution | story:write | yes | |
GET /api/v1/trailers/{jobId}/score-to-picture | getTrailerScoreToPicture | story:read | no | |
POST /api/v1/trailers/{jobId}/score-to-picture | scoreTrailerToPicture | artifact:publish | yes | |
POST /api/v1/trailers/{jobId}/score-to-picture/revert | revertTrailerScoreToPicture | story:write | yes | |
GET /api/v1/trailers/{jobId}/storyboard/audio-timeline | getTrailerAudioTimeline | story:read | no | |
POST /api/v1/trailers/{jobId}/storyboard/audio-timeline | editTrailerAudioTimeline | story:write | yes | |
GET /api/v1/trailers/{jobId}/storyboard/audio | getTrailerSceneAudio | story:read | no | |
GET /api/v1/trailers/{jobId}/storyboard/coverage | getTrailerStoryboardCoverage | story:read | no | |
POST /api/v1/trailers/{jobId}/storyboard/coverage | generateTrailerStoryboardCoverage | story:write | yes | |
POST /api/v1/trailers/{jobId}/storyboard/direct-text-motion | directTrailerTextMotion | story:write | yes | request:TrailerDirectTextMotionRequest<br>response:TrailerDirectTextMotionAck |
POST /api/v1/trailers/{jobId}/storyboard/insert-beat | insertTrailerBeat | story:write | yes | request:TrailerInsertBeatRequest<br>response:TrailerBeatOrderAck |
POST /api/v1/trailers/{jobId}/storyboard/move-beat | moveTrailerBeat | story:write | yes | request:TrailerMoveBeatRequest<br>response:TrailerBeatOrderAck |
POST /api/v1/trailers/{jobId}/storyboard/music | editTrailerSceneMusic | story:write | yes | |
POST /api/v1/trailers/{jobId}/storyboard/regenerate-text | regenerateTrailerText | artifact:publish | yes | request:TrailerBeatIndexRequest<br>response:TrailerTextRenderAnswer |
POST /api/v1/trailers/{jobId}/storyboard/remove-beat | removeTrailerBeat | story:write | yes | request:TrailerRemoveBeatRequest<br>response:TrailerBeatOrderAck |
POST /api/v1/trailers/{jobId}/storyboard/restore-take | restoreTrailerTake | story:write | yes | request:TrailerRestoreTakeRequest<br>response:TrailerRestoreTakeAck |
POST /api/v1/trailers/{jobId}/storyboard/sfx | editTrailerSceneSfx | story:write | yes | |
GET /api/v1/trailers/{jobId}/storyboard/sound-map | getTrailerSoundMap | story:read | no | |
GET /api/v1/trailers/{jobId}/storyboard/takes | getTrailerBeatTakes | story:read | no | response:TrailerBeatTakes |
GET /api/v1/trailers/{jobId}/versions | listTrailerVersions | story:read | no | response:TrailerVersions |
GET /api/v1/trailers/{jobId}/versions/{number} | getTrailerVersion | story:read | no | response:TrailerVersionRead |
POST /api/v1/trailers/{jobId}/versions/{number}/zip | requestTrailerVersionZip | story:write | yes | response:VideoVersionZipAck |
GET /api/v1/trailers/{jobId}/video-coverage | getTrailerVideoCoverage | story:read | no | response:VideoCoverageEnvelope |
POST /api/v1/trailers/{jobId}/video-coverage | generateTrailerVideoCoverage | story:write | yes | request:VideoCoverageGenerateRequest<br>response:VideoCoverageEnvelope |
POST /api/v1/trailers/{jobId}/video-export | exportTrailerVideo | story:write | yes | response:TrailerExportAck |
Voices
| Endpoint | Operation ID | Scope | Idempotent | Schemas |
|---|---|---|---|---|
GET /api/v1/likeness-consents | listLikenessConsents | story:read | no | response:LikenessConsentList |
POST /api/v1/likeness-consents/{consentId}/revoke | revokeLikenessConsent | story:write | yes | response:StudioLikenessConsent |
POST /api/v1/likeness-consents/links | createLikenessConsentLink | story:write | yes | response:LikenessConsentLinkResponse |
GET /api/v1/voice-migrations | listVoiceMigrations | story:read | no | response:VoiceMigrationListResponse |
POST /api/v1/voice-migrations | migrateVoice | story:write | yes | request:VoiceMigrationRequest<br>response:VoiceMigrationEnvelope |
POST /api/v1/voice-migrations/{migrationId}/undo | undoVoiceMigration | story:write | yes | response:VoiceMigrationUndoResponse |
GET /api/v1/voices | listStudioVoices | story:read | no | response:StudioVoiceListResponse |
DELETE /api/v1/voices/{voiceId} | deleteStudioVoice | story:write | no | response:StudioVoiceDeleteResponse |
PATCH /api/v1/voices/{voiceId} | renameStudioVoice | story:write | no | request:StudioVoiceRenameRequest<br>response:StudioVoiceRenameResponse |
POST /api/v1/voices/{voiceId}/consent-link | createVoiceConsentLink | story:write | yes | response:VoiceConsentLinkResponse |
POST /api/v1/voices/{voiceId}/revoke-consent | revokeVoiceConsent | story:write | yes | response:VoiceConsentRevokeResponse |
POST /api/v1/voices/clone-link | createVoiceCloneLink | story:write | yes | response:VoiceCloneLinkResponse |
POST /api/v1/voices/preview | previewStudioVoice | story:write | yes | request:VoicePreviewRequest<br>response:VoicePreviewResponse |
GET /api/v1/voices/pro/{jobId} | getProVoiceClone | story:read | no | response:ProVoiceCloneStatus |
GET /api/v1/voices/pro/script | getProVoiceCloneScript | story:read | no | response:ProVoiceCloneScript |
POST /api/v1/voices/pro/start | startProVoiceClone | story:write | yes | request:ProVoiceCloneStartRequest<br>response:ProVoiceCloneStartResult |
POST /api/v1/voices/pro/train | startProVoiceCloneTraining | story:write | yes | request:ProVoiceCloneTrainRequest<br>response:ProVoiceCloneTrainResult |
Error codes
Every error response has the same shape: { "error": { "code", "message", "requestId" } }. Match on code; the message text can change.
| Code | Meaning |
|---|---|
authentication_required | No bearer API key was supplied in the Authorization header. |
invalid_api_key | The API key is malformed, unknown, or does not match the stored secret. |
api_key_revoked | The API key has been revoked by an account owner. |
api_key_expired | The API key has passed its configured expiration timestamp. |
account_disabled | The account that owns this API key has been disabled. |
ip_not_allowed | The request originated from an IP that is not in the key allowlist. |
insufficient_scope | The credential does not include the scope required for this endpoint. The endpoint exists — error.details.missingScopes names what is missing. On an OAuth connector, disconnect and sign in again approving that access; on an sh_ key, mint a new key with those scopes. |
rate_limited | The per-key + per-IP rate limit for this route family was exceeded. |
idempotency_key_required | The endpoint requires an Idempotency-Key header for safe retries. |
idempotency_conflict | A request with the supplied Idempotency-Key is already processing or used incompatible parameters. |
validation_failed | The request payload failed validation. The error message names the offending field. |
project_not_found | The story project does not exist, was deleted, or is owned by another account. |
source_not_found | The story source does not exist, was deleted, or is owned by another account. |
source_type_unsupported | The requested source type is not yet supported by this API phase. Check /api/v1/capabilities. |
source_too_large | The source payload exceeds the documented size limit. See details.maxBytes. |
source_incomplete | A source declared as full context failed its producer-specific completeness proof and was rejected before generation. |
source_fetch_failed | A URL or PDF source could not be safely fetched (SSRF block, redirect limit, size limit, content-type mismatch, or upstream error). |
story_plan_not_found | The story plan does not exist, was deleted, or is owned by another account. |
story_plan_state_invalid | The story plan is in a state that does not allow the requested operation. See details.status and details.allowed. |
story_plan_failed | StoryPlan generation or refinement failed. See details for the underlying reason when available. |
season_run_not_found | The season run does not exist, was deleted, or is owned by another account. |
season_run_state_invalid | The season run or episode is not at the required lifecycle state. Reload it and follow nextAction before retrying. |
story_job_not_found | The story job does not exist, was deleted, or is owned by another account. |
story_job_state_invalid | The story job is in a state that does not allow the requested operation (e.g. canceling a job that has already reached a terminal state). See details.status and details.allowed. |
artifact_not_found | The story artifact does not exist or is owned by another account. |
artifact_not_ready | The artifact is not in a state where this operation is valid yet (e.g. requesting an MP4 render before the share link exists). See details.status. |
artifact_generation_failed | The artifact adapter failed to produce a shareable output. The job and artifact rows stay inspectable; see the artifact failureCode / failureMessage. |
publishing_series_not_found | The publishing series does not exist, was deleted, or is owned by another account. |
publishing_destination_not_found | The publishing destination does not exist, was deleted, or is owned by another account. |
publishing_release_not_found | The publishing release does not exist, was deleted, or is owned by another account. |
publishing_release_state_invalid | The publishing release is in a state that does not allow the requested operation. See details.status and details.allowed. |
publishing_asset_not_ready | The release does not have a finalized, ready asset for the requested publish operation. |
publishing_asset_stale | The source artifact changed after the release asset was promoted. Finalize the current source before publishing. |
project_precondition_failed | The project has not reached the development stage this call needs, so nothing was created, queued or charged. details.stage names the stage it has not finished (premise, premise_coverage, planning, full_coverage; ready when the stage is passed and a narrower precondition refused, such as a mood-board render with no image model bound), details.missingFields what that stage still lacks, and details.canPlan / details.canStartEpisode what the project can do now. Finish the named stage — do not retry: the identical call refuses the same way until it is done. GET /story-projects/{projectId} reports the same position as project.workspaceGate without attempting anything — read it first. |
spend_subject_unresolved | The project gate could not find the artifact, screenplay version, episode or project this paid call is for under your account, so it refused instead of guessing — nothing was created, queued or charged. details.subject is the kind it looked for (artifact, script_upload, script, project) and details.id the id. Check the id; retrying the same one refuses the same way. |
insufficient_credits | The account does not have enough Studio Credits to reserve this job. See details.required and details.available. A story job's refusal also carries details.jobId: null when the job was refused before it was created, or the id of the one job the Idempotency-Key is bound to — retrying that key after topping up resumes that job, it never creates another. |
internal_error | The Sleeper Hit Story API failed unexpectedly. Retrying with the same Idempotency-Key is safe. |
trailer_not_found | No trailer with that id, or it belongs to another account. |
trailer_state_invalid | The trailer exists and the request is well-formed, but the trailer, its beat, its cast or its plates are not in a state that allows it — a plan approved outside draft, a take chosen or a beat rendered outside review/complete, an export before the trailer is finished, a finish while a call has no selected take, a narrated cut with no narrator, a re-voiced take selected (it is never played), a beat with no caption or footage-only when text was asked for, or a render the compile blocked. Nothing was reserved. A render refusal carries details.refusal = { code, action, fix } — 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 — plus details.blocked[], one entry per beat the compile could not render. The identical request succeeds once the trailer is put right. |
pitch_deck_not_found | No pitch deck with that id, or it belongs to another account. |
pitch_deck_state_invalid | The 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; details.refusal names the fix. State, not payload. |
pitch_deck_version_not_found | The deck has no version with that number. Read its versions (GET /pitch-decks/{jobId}/versions) for the numbers it has; a version is kept each time the deck's master is published. |
trailer_version_not_found | The trailer has no version with that number. Read its versions (GET /trailers/{jobId}/versions) for the numbers it has; a version is kept each time the trailer's master is published. |
story_job_not_resumable | The job exists but has no resumable checkpoint — it either finished or failed past the point a resume can start from. |
script_not_found | No screenplay with that id, or it belongs to another account. |
cast_precondition_failed | A cast is not ready, so nothing was created, quoted or charged. On the casting step: nobody castable, nobody with a description, or no image model bound. On a table_read job: the project cast canon (+ the request's voiceMap) does not cover every character (details.reason: no_cast_canon, cast_canon_collision, cast_canon_incomplete), an adopted scriptUploadId is a draft or not cast (script_not_finalized, script_not_cast_ready), or a resumed job's own screenplay voices someone uncast (episode_cast_incomplete); details.missing lists each member and what it lacks (voice, and unless details.audioOnly, face, body, sheet). Complete the cast — do not retry: the identical call refuses the same way until it is done. GET /story-projects/{projectId} reports the canon half as project.tableReadReadiness. |
table_read_not_prepared | The read's table read is not paid for yet, so nothing was started, rendered or charged. A live read, a durable audio render (finalize) and the read's sound effects and music wait until it is PREPARED — charged once per screenplay version: POST /scripts/{scriptUploadId}/table-read/prepare (MCP prepare_table_read, CLI script table-read prepare), or Continue to Table Read in the Writing Lab. A read made by a Story API job is paid by that job. Prepare it, then retry. |
table_read_not_failed | The read's creative analysis is not in a FAILED state, so there is nothing to retry. Only a read whose analysis failed can be re-run (POST /scripts/{scriptUploadId}/table-read/retry-analysis, MCP retry_creative_analysis, CLI script table-read retry-analysis, or the Writing Lab failed card's Try Again). Nothing was changed or charged. |
voice_clone_not_found | No voice clone with that id, or it belongs to another account. |
mood_board_full | The mood board already holds the maximum number of frames that can bind a render. Drop one before keeping another. |
fal_account_locked | The upstream generation provider has locked the studio account, usually for billing. Nothing was charged. |
location_plate_not_found | The script stores no location plate at that key. Read the location plates (GET /artifacts/{artifactId}/location-plates) for the keys it does store; a plate the script has not made yet is made when a video renders. |
location_plate_precondition_failed | The plate cannot be remade yet, and nothing was priced or charged: it is not ready, a remake is already running for it, the plate it is cropped from is missing, or no image model is bound. The message says which; the identical call succeeds once the plate is put right. |
voice_not_found | No voice with that id in your library (GET /voices lists the ones you own), or a voice you may not use: another writer's cloned or designed voice is never previewed, cast, pinned or migrated onto. A catalog voice can be previewed and assigned, but only your own voices can be renamed or deleted. |
voice_design_not_found | No cast voice design with that id in this project. designId comes from the answer of POST /story-projects/{projectId}/cast-voice/design for the same project. |
voice_in_use | The voice is still pinned in a cast canon or spoken in a read, so it was not deleted. details.uses names where; repoint those first, or send the delete again with confirmed: true to delete it anyway (those characters will have no voice until they are recast). |
cast_voice_precondition_failed | The read or project is not ready for this cast voice operation, and nothing was priced or charged: the character has no lines to speak, there is no earlier voice to restore, or the read has no finished recording to re-voice. The message says which and what to do first. |
script_in_use | The script was not deleted: something live still depends on it. details.uses names each (a published or scheduled release, a running job, a season-run slot, a pitch deck or a trailer); unpublish, cancel or delete those first, then delete the script again. |
script_state_invalid | The script's state refuses this change: the most common is deleting a script's only version, which leaves no script — delete the whole script instead (DELETE /scripts/{scriptId}). The message says which. |
plan_limit_reached | Your plan does not allow this: an upload that would take you past the plan's Writers Lab page allowance (the message gives the pages in use and the pages this upload adds) or a second version of a script on the free plan, or a second screenplay coverage report on the free plan (it includes one). details carries the plan and the numbers. Upgrade the plan, or delete scripts you no longer need, then try again. |
usage_limit_reached | This month's cap on your coverage reports (screenplay, Series Bible premise and full, outline) or table-read generations (scene music, scene atmosphere, backdrop prompts) is used up, so nothing was queued or charged. details carries feature (COVERAGE or TABLE_READ), limit, used and resetsAt (the first of next month, 00:00 UTC). It is state, not a rate: do not retry before resetsAt — the same call refuses the same way until then. |
cast_voice_casting_coverage_failed | The candidate voice you chose FAILED its casting coverage (a model listened to the candidates and scored each against the brief), so nothing was cloned or pinned. details.castingCoverage carries its score, the bar, why (flags, gateReasons), the candidate it recommends instead and, when every candidate failed, a revised brief to design again from. Choose the recommendation, design again, or send acknowledgeCastingCoverage: true only once the writer has heard the failure and chosen it anyway. |
cast_portrait_casting_coverage_failed | The headshot you chose FAILED its casting coverage (a model looked at the candidates and scored each against the look and the Series Bible), so nothing was pinned. details.castingCoverage carries its score, the bar, why (flags, gateReasons), the headshot it recommends instead and, when every one failed, a revised look to draw again from. Choose the recommendation, draw again, or send acknowledgeCastingCoverage: true only once the writer has seen the failure and chosen it anyway. |
voice_retired | A voice its provider has retired (it can no longer speak) is in the way, and nothing was queued, charged or changed. A finalize, export, render, live read, re-voice or preview that would speak it refuses before anything is synthesized; a cast, pin or choose refuses to add it; a migration refuses a replacement whose provider is retiring or retired. details.voices names each voice (character, voiceId, voiceName, provider, retiresAt). Migrate those voices to a new voice (POST /voice-migrations; MCP migrate_voice; CLI voices migrate), then retry. |
voice_consent_required | A CLONED voice has no consent on record from the person whose voice it is (it was made before consent records began), so nothing was cast, pinned, previewed, spoken or charged. details.voices names each (voiceId, name). Mint a consent link (POST /voices/{voiceId}/consent-link; MCP create_voice_consent_link; CLI voices consent <voiceId>) and give it to that person; once they confirm, retry. A Pro clone with no consent from any recording session cannot be trained. |
voice_consent_revoked | The speaker of a cloned voice revoked their consent, so the voice is never used for anything new (work already published stays up). Nothing was cast, pinned, previewed, spoken, recorded or charged. details.voices names each (voiceId, name, revokedAt). Choose another voice; revoking is final. |
account_email_mismatch | confirmEmail is not this account's email address, so no deletion was scheduled. Ask the account owner for the address on the account. |
account_deletion_refused | This account cannot delete itself (message says why): it is an admin account, it hosts workshop cohorts other members belong to, or it owns a live organization (transfer or delete it first). Also returned when cancelling a deletion whose purge has already started. Nothing was changed. |
project_not_restorable | The project cannot be restored: it is not deleted, its 30-day restore window has passed, or its purge has started (message says which). Its episodes are already in the library as private scripts. |
content_refused | The request asks for, or what would be published or shared contains, content our rules never allow (sexual content involving anyone under 18, or a real person, however long ago they lived, shown in a sexual or intimate way), so nothing was made, saved, published, shared or charged. It is not retried. Rephrase or remove that content. |
safety_hold | Publishing and sharing are paused on this account while a review is open, so nothing was published or shared. Writing is not affected. Write to help@sleeperhit.studio if you think this is a mistake. |
safety_check_unavailable | The content safety check that runs before anything is published or shared could not run, so nothing was published or shared. Retry in a few minutes; a queued publish retries on its own. |
likeness_consent_required | The picture or video would show a real, identifiable person who is living or died within the last 70 years (details.people names them). A real person's likeness needs their approval, or their estate's, so nothing was made or charged. Mint a link for them to approve (POST /likeness-consents/links; the account holder approves their own likeness in the dashboard), then retry; or describe a fictional character instead. Fictional characters, including ones who share a name with real people, and long-dead historical figures are not affected, and nobody real is ever shown in a sexual or intimate way. |
reference_image_not_owned | A face or likeness reference (a portrait or body reference, a cast canon picture, a roster portrait) must be a picture stored in your own account: one you uploaded, or one made in your own projects. A link to anywhere else, or to another writer's picture, is refused (details.field names it), and nothing was changed or charged. Use a picture from your library or one of your cast's portraits. |
likeness_consent_not_found | No likeness approval with that id belongs to this account. List them with GET /likeness-consents. |
character_not_found | Nobody in the project's cast canon or Series Bible answers to that name (any name or alias counts). Read GET /story-projects/{projectId}/cast-look for the people the project has, or add the character to the Series Bible first. Nothing was changed or charged. |
portrait_design_not_found | No portrait design with that id in this project. designId comes from POST /story-projects/{projectId}/cast-portraits/design (confirmed) or GET .../cast-portraits/designs for the same project. |
series_bible_version_conflict | The Series Bible changed since the version this edit was based on (details.expectedVersion vs details.currentVersion), so nothing was saved. Read the character again and send the edit with the current version, or omit expectedVersion to edit whatever is saved now. |
show_cap_reached | A recurring show's next paid step would cross one of its spending caps, so it was not taken: details.scope is episode (the episode cap), month (the monthly cap, in the series' timezone) or unset (the show has no caps, so it spends nothing). details.cap, details.credits (the step's quote) and details.wouldSpend give the numbers. Raise the cap with the creator (PATCH /publishing-series/{seriesId} episodeCreditCap / monthlyCreditCap) or wait for the next month; do not retry — the same step refuses the same way. |
not_a_sign_in_key | A sign-out (DELETE /device/credential) was made with a credential no browser sign-in minted — a key made on the API page, or a connector's OAuth grant — so nothing was revoked and the credential keeps working. Revoke a key, or disconnect a connector, on the API page (/dashboard/api). |
not_found | No endpoint at that path. This is a routing problem, not an authentication or method one — check the path against the API reference. |
method_not_allowed | The path exists but does not accept that HTTP method. The Allow response header lists the methods it does accept. |