Sign inStart creating

API surface

Two mechanisms. Choose by shape, not habit.

  • Server actions (src/app/**/actions.ts) for mutations driven by the UI. Type-safe end to end, no serialization boilerplate, no route to keep in sync.
  • Route handlers (src/app/api/**/route.ts) for anything needing a URL: file downloads, OAuth redirects, <form method="post"> targets, and fetch from a client component.

Server actions

src/app/studio/actions.ts

Every mutation calls assertOwns(projectId) first — ownership check, admin override, throws otherwise.

ActionNotes
createProjectAlso creates the Universe and Timeline. A project without a Universe is not a thing.
saveScriptEvery save is a new ScriptVersion
promoteScriptToProductionParses Fountain → scenes, characters, locations, cues, notes, graph edges
buildShotList / buildAllShotListsShot Engine coverage grammar
generateStoryboardActionCheap still, Economy tier
generateShotActionThrows StoryboardGateError if the gate is unmet
generateArenaAction / pickWinnerActionArena + win-rate training
improveActionRegenerates only weak scoring dimensions
freeGenerate / tryStyleCreator Studio surfaces
applyStyleActionProject / scene / shot scope
approveCascadeAction / rejectCascadeActionThe confirmation gate
addStateEventAction / changeWardrobeActionReturn a priced plan, not a mutation
directorSend / directorApprove / directorRejectVibe directing
assembleTimeline / proposeRuntimeEditAction / applyEditProposalActionNLE
runAuditAction / fixAllActionFilm Audit
submitToTheaterCreates the Publication, cast cards, runs Stage 0

src/app/admin/actions.ts

decideAction, assessQualityAction, publishAction — all behind requireRole('admin', 'moderator').

Route handlers

RoutePurpose
GET /api/style-preview/[styleId]Canonical reference scene as SVG. ?w &h &seed &still &modifiers. Deterministic → immutable cache.
GET /api/projects/[id]/export`?format=edl \srt \edl-json \<delivery format>`
GET /api/projects/[id]/script/export`?format=fountain \fdx, ?annotations=1`
POST /api/theater/[id]/progressJSON. Called by the player every ~5s of watched time.
`POST /api/theater/[id]/rate \comment \mylist`Form posts, 303 redirect back
POST /api/theater/follow/[userId]Toggle
POST /api/festival/vote/[entryId]One vote per account per category
GET /api/publishing/connect/[platform]OAuth start; CSRF nonce in an httpOnly cookie
POST /api/publishing/disconnect/[id]Destroys the token
POST /api/auth/signout

Conventions

  • AuthcurrentUser() returns null; requireUser() / requireRole() throw AuthError with a status.
  • Errors — server actions return { ok, message } for expected failures (insufficient credits, storyboard gate) and throw only for programming errors. Expected failures are UI states, not exceptions.
  • Redirects — form-post handlers return 303 so a refresh does not resubmit.
  • RevalidationrevalidatePath(path, 'layout') after anything that changes project-wide state.

Not built

No public REST API for third parties. Part 7 lists an API for the Studio tier; when it exists it should wrap the same service functions rather than reimplementing them — the services are already the API, they just lack an HTTP surface and key auth.