{"openapi":"3.1.0","info":{"title":"Pouchy World API","version":"1.58.1","description":"Build an interactive world — a short-drama story, a game’s NPCs, an interactive novel — without an official app. Two lanes: the OWNER lane (a signed-in project admin) authors and reads; the MACHINE lane (a project Secret Key plus a POUCHY-SOURCE-V1 provider signature over the exact raw body) drives sessions, turns and events from your backend. Secret keys and signing secrets never belong in a browser or a mobile app.\n\nIdempotency is explicit everywhere it matters: a turnId is the turn’s identity (re-send it to retry, mint a new one for a new beat), an eventId is the event’s, and a story or world publish is idempotent on its content bytes. Delivery is at-least-once with consumer-side de-duplication; GET /turns/{turnId} is the authoritative recovery path when a response is lost."},"servers":[{"url":"https://pouchy.ai/v1"}],"components":{"schemas":{"StoryPackageContent":{"type":"object","additionalProperties":true,"required":["name","summary","roles"],"description":"A Story Package v1 body. Array ORDER is narrative order and therefore content — reordering roles or scenes mints a different revision. Referential and uniqueness rules are enforced by the server and named in the field descriptions below. SIZE: besides each list's maxItems, the whole package is at most 900000 bytes, measured as the UTF-8 JSON of the normalized content (a published revision is one stored document; CJK text is 3 bytes a character). Over it is a 400 naming the measured size.","x-pouchy-max-bytes":900000,"properties":{"name":{"type":"string","maxLength":80,"description":"Required."},"summary":{"type":"string","maxLength":1000,"description":"Required."},"source":{"type":"object","additionalProperties":true,"required":["title","version"],"description":"A REFERENCE to the original screenplay — never its bytes. Both fields are required when `source` is present at all.","properties":{"title":{"type":"string","maxLength":200,"description":""},"version":{"type":"string","maxLength":60,"description":""}}},"canon":{"type":"array","items":{"type":"string","maxLength":500,"description":""},"maxItems":100,"description":"World-shared truth every role sees. Entries are trimmed; empty strings are refused."},"constraints":{"type":"array","items":{"type":"string","maxLength":500,"description":""},"maxItems":50,"description":"What the story must never do. Same entry rules as canon."},"roles":{"type":"array","items":{"type":"object","additionalProperties":true,"required":["storyRoleId","worldRoleId"],"properties":{"storyRoleId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{1,63}$","description":"Lowercase machine id, 2–64 chars."},"worldRoleId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{1,63}$","description":"The WORLD role this story role plays. Must exist in the world revision that pins this package — checked at PIN time, not here, because a package is project-scoped and may be pinned by many worlds. At most one story role per worldRoleId."},"description":{"type":"string","maxLength":500,"description":"Authoring metadata and a dashboard label. It reaches NO prompt at runtime, not even this role’s own — a role’s persona is the bound agent’s `systemPrompt`."},"secrets":{"type":"array","items":{"type":"string","maxLength":500,"description":""},"maxItems":20,"description":"Role-PRIVATE canon. Never rendered to another role, in any projection."},"goals":{"type":"array","items":{"type":"string","maxLength":500,"description":""},"maxItems":20,"description":"Rendered into THIS role’s own per-beat envelope, and nowhere else — never into another role’s. It does NOT drive role selection: selection is declaration order intersected with bound actors and with focusRoles / event subscriptions, minus roles that have left the worldline (1.53.0), capped at 3, and reads no goals at all. (The previous wording said otherwise and described a design that was never built.)"},"effectsAllow":{"type":"array","maxItems":8,"items":{"type":"string","enum":["set_flag","increment_flag","reveal_fact","update_relationship","update_entity","advance_clock","request_scene_transition","complete_objective"]},"description":"What this role may PROPOSE. DEFAULT DENY: absent means the role proposes nothing and the effect tool is not offered to it at all. An unknown kind is an error, never a silent drop."}}},"maxItems":24,"description":"At least one role is required. storyRoleId is unique; worldRoleId is bound at most once."},"scenes":{"type":"array","items":{"type":"object","additionalProperties":true,"required":["sceneId","title"],"properties":{"sceneId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Lowercase machine id; dots allowed for namespacing (fact.ferry)."},"title":{"type":"string","maxLength":120,"description":"Required."},"description":{"type":"string","maxLength":500,"description":""},"location":{"type":"string","maxLength":120,"description":""}}},"maxItems":100,"description":"sceneId is unique within the package."},"nodes":{"type":"array","items":{"type":"object","additionalProperties":true,"required":["nodeId","sceneId","title","objective"],"properties":{"nodeId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Lowercase machine id; dots allowed for namespacing (fact.ferry)."},"sceneId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Must name a scene declared in `scenes`."},"title":{"type":"string","maxLength":120,"description":""},"objective":{"type":"string","maxLength":500,"description":""},"prerequisites":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Lowercase machine id; dots allowed for namespacing (fact.ferry)."},"maxItems":10,"description":"Node ids that must be completed first. Each must be a declared node, and never this node itself."}}},"maxItems":200,"description":"A plot node is an objective inside a scene. nodeId is unique."},"establishedFacts":{"type":"array","items":{"type":"object","additionalProperties":true,"required":["factId","text"],"properties":{"factId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Lowercase machine id; dots allowed for namespacing (fact.ferry)."},"text":{"type":"string","maxLength":500,"description":""}}},"maxItems":200,"description":"factId is unique."},"branches":{"type":"array","items":{"type":"object","additionalProperties":true,"required":["branchId","fromNodeId","condition","toNodeId"],"properties":{"branchId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Lowercase machine id; dots allowed for namespacing (fact.ferry)."},"fromNodeId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Must be a declared node."},"toNodeId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Must be a declared node."},"condition":{"type":"string","maxLength":500,"description":"Descriptive text in v1 — the runtime does not evaluate it. It is what a turn offers as a next option."}}},"maxItems":200,"description":"branchId is unique."},"endings":{"type":"array","items":{"type":"object","additionalProperties":true,"required":["endingId","title","condition"],"properties":{"endingId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Lowercase machine id; dots allowed for namespacing (fact.ferry)."},"title":{"type":"string","maxLength":120,"description":""},"condition":{"type":"string","maxLength":500,"description":""}}},"maxItems":60,"description":"endingId is unique."},"stateSchema":{"type":"object","additionalProperties":true,"description":"The declared world-state flags. Absent means a package with no flags.","properties":{"flags":{"type":"array","items":{"type":"object","additionalProperties":true,"required":["key","kind"],"properties":{"key":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Lowercase machine id; dots allowed for namespacing (fact.ferry)."},"kind":{"type":"string","enum":["boolean","number","string"]},"initial":{"description":"Optional starting value. Its JSON type must match `kind` exactly — a number initial on a string flag is refused."},"public":{"type":"boolean","enum":[true],"description":"Contract v4. `true` publishes this flag — its NAME and its VALUE — on the progress checkpoint’s `publicFlags` for any client holding the instance. Absent keeps it out of the checkpoint (a flag’s name can itself be a spoiler). `false` is refused: the only two states are published and not. Declaring any public flag is what makes a package contract v4; the server decides the version and refuses a client-supplied `contractVersion`."},"range":{"type":"object","required":["min","max"],"properties":{"min":{"type":"number"},"max":{"type":"number"}},"description":"PRESENTATION ONLY — the full scale this number runs on, so a client can draw a meter instead of printing a bare integer. Valid only on `kind: \"number\"`; refused on the others, because `{min:0,max:1}` on a boolean reads as \"it can be 0.5\". `min` must be STRICTLY less than `max`: equal bounds divide by zero in every client that draws a meter. Absent means the author did not say, and a client must not invent one — assuming /100 renders a 0–10 score as \"6% of the way there\". Nothing keys off this: not the coordinator, not ending adjudication, not the ledger, and it is NOT a contract version (an older reader ignoring it prints the number without a meter, which is what every client did before it existed)."},"label":{"type":"string","description":"PRESENTATION ONLY — the human name for this flag. Absent means a client derives one from `key` (`trust_amara` → `TRUST · AMARA`). It exists because `key` also has to serve as the identifier a rule condition names, and the two jobs have conflicting naming constraints. NEVER translated by a client: these strings are authored inside the package, in the language of the work. Absent or a non-empty string — `\"\"` is refused, because an empty label cannot be told from an unfilled form field and one meaning with two byte spellings is what `public` already refuses."}}},"maxItems":50,"description":"key is unique."}}},"effectRules":{"type":"object","additionalProperties":true,"required":["conflict"],"description":"How the coordinator settles TWO ROLES proposing incompatible effects on the SAME target in one turn. Absent means `reject`. There is deliberately no \"the model decides\" option. It does NOT govern the caller: a `trigger.proposedPatches` op always beats a role effect on the same axis, whatever this says (world API 1.46.0) — see that field. Making that author-configurable would make the deterministic entrance conditional on an author’s dramatic taste, which is the one property it exists to have.","properties":{"conflict":{"type":"string","enum":["reject","first_wins"]}}},"initialRelations":{"type":"array","maxItems":50,"description":"Contract v2. The relations the story opens with, seeded into a NEW world instance only. A SET, not a list: [a,b] and [b,a] are one relation, a pair declared twice is REFUSED rather than merged (differing descriptors are a contradiction; identical ones are still two rows for one key), and the whole array is sorted before hashing — so authoring order cannot change the content hash. Every other array in this contract keeps declaration order. EVERY relation here is PUBLIC: relations live in the shared block of world state, so a relation that must stay hidden belongs in the authorised role’s `secrets` instead. Supplying this field is what makes a package contract v2; the server decides the version and refuses a client-supplied `contractVersion`.","items":{"type":"object","additionalProperties":true,"required":["between","descriptor"],"properties":{"between":{"type":"array","minItems":2,"maxItems":2,"items":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{1,63}$","description":"Lowercase machine id, 2–64 chars."},"description":"Two DISTINCT declared storyRoleIds. Normalized to ascending order."},"descriptor":{"type":"string","maxLength":120}}}},"series":{"type":"object","additionalProperties":true,"required":["seriesId","title","episodes"],"description":"Contract v3. Episodes, and the deterministic rules that decide when one is over. Supplying this field is what makes a package contract v3; the server decides the version and refuses a client-supplied `contractVersion`. Every id a rule names — node, fact, scene, flag, ending — must be DECLARED in the same package: a rule that can never hold would make the episode end only by fallback, silently. Ending rules are evaluated SERVER-SIDE over committed state after a beat commits; no model, client or prompt supplies an endingId.","properties":{"seriesId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Lowercase machine id; dots allowed for namespacing (fact.ferry)."},"title":{"type":"string","maxLength":120},"episodes":{"type":"array","maxItems":24,"description":"DECLARATION order is broadcast order. At least one; there is no empty-series form, because absent already means \"not a serial\".","items":{"type":"object","additionalProperties":true,"required":["episodeId","title","turnBudget","fallbackEndingId"],"properties":{"episodeId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Lowercase machine id; dots allowed for namespacing (fact.ferry)."},"title":{"type":"string","maxLength":120},"turnBudget":{"type":"integer","minimum":1,"maximum":200,"description":"Committed beats this episode may run before the fallback ending fires. Capped at the evidence window: an episode longer than one evidence read can never be turned into a script."},"fallbackEndingId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"A DECLARED ending, used when no rule ever holds. Required — the alternative is an episode that cannot end."},"endingRules":{"type":"array","maxItems":20,"description":"Order-independent: priority decides (lower wins) and `ruleId` breaks a tie. Reordering this array changes nothing, unlike every other array in this contract.","items":{"type":"object","additionalProperties":true,"required":["ruleId","endingId","priority","all"],"properties":{"ruleId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Lowercase machine id; dots allowed for namespacing (fact.ferry)."},"endingId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Lowercase machine id; dots allowed for namespacing (fact.ferry)."},"priority":{"type":"integer"},"all":{"type":"array","minItems":1,"maxItems":10,"description":"A CONJUNCTION over a closed condition vocabulary — `node_completed`, `fact_revealed`, `flag_equals`, `scene_is`, `clock_at_least`. An OR is two rules. An empty list would be true of every state and is refused.","items":{"type":"object","additionalProperties":true}}}}}}}}}},"visibility":{"type":"object","additionalProperties":true,"required":["factDefault"],"description":"Role visibility over established facts and (Story Contract v5, world API 1.55.0) story flags. Canon is world-shared by definition and role `secrets` are private by definition; facts and flags sit between. Absent means { factDefault: \"shared\" } and every role sees every flag.","properties":{"factDefault":{"type":"string","enum":["shared","role-scoped"]},"factAllow":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Lowercase machine id; dots allowed for namespacing (fact.ferry)."}},"description":"worldRoleId → factId[]. Every key must be a declared worldRoleId and every value a declared factId; both are refused by name rather than ignored."},"flagDefault":{"type":"string","enum":["shared","role-scoped"],"description":"Story Contract v5 (world API 1.55.0). `role-scoped`: a role sees a flag only when `flagAllow` names it for that role, or the flag is `public` (public is world-public — every client and every role). Hidden means hidden wherever a ROLE reads: its envelope (key and value), its call brief, a deliberation, and its effect brief; a role `set_flag` / `increment_flag` on a flag it cannot see is refused exactly like an undeclared flag (`schema_rejection`). Everything that is not a role sees every flag: your `proposedPatches` may write a hidden flag, ending rules read it, and `getWorldState` returns it. A flag in no role’s list is hidden from every role. Absent or `shared` is the behaviour before v5 (and `shared` is dropped, so it never changes the package’s bytes or version). Using `role-scoped` makes the package contract v5; an environment that has not enabled v5 writes answers 409 `story_contract_v5_write_disabled` with the field intact, and a v5 package cannot be read by a pre-1.55.0 server. The switch is DEPLOYMENT-wide — it covers every project at once and is never set per project; `GET https://pouchy.ai/api/version` (public, no auth) reports whether the serving deployment writes v5 as `storyContractWrites.v5`, so check that before publishing rather than learning it from a 409."},"flagAllow":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Lowercase machine id; dots allowed for namespacing (fact.ferry)."}},"description":"Story Contract v5. worldRoleId → flag keys, only with `flagDefault: \"role-scoped\"` (refused otherwise, never ignored). Every key must be a declared worldRoleId and every value a declared flag; a `public` flag may not be listed."}}}}},"WorldCarryoverV1":{"type":"object","properties":{"contractVersion":{"type":"integer","enum":[1]},"fromEpisodeId":{"type":"string"},"fromEndingId":{"type":"string"},"toEpisodeId":{"type":"string"},"canonFacts":{"type":"array","items":{"type":"object","properties":{"factId":{"type":"string"},"text":{"type":"string"}},"required":["factId","text"]},"description":"Declared facts the worldline has REVEALED, with the author's text. A reveal is world-level, not per role: once a fact is revealed every role and the reader view that `GET …/state` (`getWorldState`) returns include it, whatever `visibility.factAllow` granted before. So this list is exactly what a reader may already know — safe to recite as a recap. Sorted by factId; at most 100."},"canonFactsTruncated":{"type":"boolean"},"relations":{"type":"array","items":{"type":"object","properties":{"between":{"type":"array","items":{"type":"string"},"minItems":2,"maxItems":2},"descriptor":{"type":"string"}},"required":["between","descriptor"]},"description":"Where the characters stand — the SHARED relations block of world state. A relation that must stay hidden belongs in a role's `secrets`, which never appear here. At most 50."},"relationsTruncated":{"type":"boolean"},"unresolvedThreads":{"type":"array","items":{"type":"object","properties":{"nodeId":{"type":"string"},"objective":{"type":"string"}},"required":["nodeId","objective"]},"description":"Declared nodes never completed, with their objectives. In a branching story most of these are paths this worldline did not take, not open questions — do not present them to a reader as \"unresolved\". At most 50."},"unresolvedThreadsTruncated":{"type":"boolean"},"unfulfilledSetups":{"type":"array","items":{"type":"object","properties":{"factId":{"type":"string"},"text":{"type":"string"}},"required":["factId","text"]},"description":"EVERY declared fact not yet revealed, with the author's text — NOT filtered by `visibility.factDefault` / `factAllow`: a role-scoped fact granted to one role, or to none, is here. These are setups the story has not paid off, i.e. spoilers by definition, under `shared` visibility too. Backend data for planning the next episode; never recite it to a reader. At most 50."},"unfulfilledSetupsTruncated":{"type":"boolean"}},"required":["contractVersion","fromEpisodeId","fromEndingId","toEpisodeId","canonFacts","canonFactsTruncated","relations","relationsTruncated","unresolvedThreads","unresolvedThreadsTruncated","unfulfilledSetups","unfulfilledSetupsTruncated"],"description":"What the next episode gets to stand on — a projection of the worldline, nothing copied or deleted. Returned only to the owner lane and the signed machine lane, never to a player token, and not stored. Role-private layers (`secrets`, role notes) are never in it. Of its lists, only `canonFacts` and `relations` are reader-safe as they stand."}},"securitySchemes":{"OwnerToken":{"type":"http","scheme":"bearer","description":"A signed-in project admin’s Firebase ID token."},"SecretKey":{"type":"http","scheme":"bearer","description":"A project Secret Key (pchy_sk_…). Carries the test/live axis; must be paired with X-Pouchy-Source-Signature."},"AdminKey":{"type":"http","scheme":"bearer","description":"A project ADMIN key (pchy_admin_…), minted once from the dashboard. Long-lived and machine-held: this is the credential an unattended backend uses for the /admin mirror of the world reads, because an OwnerToken is a browser-minted Firebase ID token that expires within the hour. It proves the PROJECT — never the Provider — so it cannot drive a turn."},"SourceSignature":{"type":"apiKey","in":"header","name":"X-Pouchy-Source-Signature","description":"POUCHY-SOURCE-V1: t=<unix seconds>,kid=<keyId>,v1=<hex hmac>. The canonical string is five newline-joined lines — POUCHY-SOURCE-V1, t, the declared source, the ID SLOT, and the sha256 of the exact raw body (or \"-\" when empty). The id slot is per-door and there are three: turnId on /environments/{envId}/instances/{id}/turns, eventId on /events/ingest, and world.request_id (the value from the body you are signing) on /sessions — a session mint has no turn and no event, and signing an empty or invented id there is the first thing an integrator gets wrong. Sign at send time, every attempt."}}},"paths":{"/admin/environments/{envId}/overview":{"parameters":[{"name":"envId","in":"path","required":true,"schema":{"type":"string"},"description":"The world (environment) id — env_…."}],"get":{"summary":"Ready / not-ready and the next step for each blocker, on the admin key. The machine-lane twin of /projects/{projectId}/environments/{envId}/overview.","security":[{"AdminKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/admin/environments/{envId}/cost":{"parameters":[{"name":"envId","in":"path","required":true,"schema":{"type":"string"},"description":"The world (environment) id — env_…."},{"name":"months","in":"query","required":false,"schema":{"type":"integer"},"description":"Window length; clamped server-side."}],"get":{"summary":"Per-experience cost on the admin key — the same numbers as the owner route. A backend that drives an experience is the party that wants its cost in its own dashboard, so this read belongs on a credential a server can hold.","security":[{"AdminKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/admin/environments/{envId}/instances/{worldInstanceId}/state":{"parameters":[{"name":"envId","in":"path","required":true,"schema":{"type":"string"},"description":"The world (environment) id — env_…."},{"name":"worldInstanceId","in":"path","required":true,"schema":{"type":"string"},"description":"The world instance — one user’s story."}],"get":{"summary":"The world’s current state in the USER projection, mirroring the owner-plane route byte for byte — same projection, same shape. `flags` is EVERY flag, not only those an author marked `public: true` (that filter is the progress checkpoint’s); `completedNodes` is the plot progress the checkpoint reports only as a COUNT; `factsVisible` is the world-revealed set. Never the role-private layer.","security":[{"AdminKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"worldInstanceId":{"type":"string"},"environmentId":{"type":"string"},"environmentRevision":{"type":"integer"},"storyPackageRef":{"type":"string"},"state":{"type":"object","properties":{"stateRevision":{"type":"integer"},"clock":{"type":"integer"},"sceneId":{"type":"string"},"location":{"type":"string"},"flags":{"type":"object","additionalProperties":true},"completedNodes":{"type":"array","items":{"type":"string"}},"factsVisible":{"type":"array","items":{"type":"string"}},"relations":{"type":"object","additionalProperties":true},"entities":{"type":"object","additionalProperties":true},"departedRoles":{"type":"array","items":{"type":"string"},"description":"World API 1.53.0. Roles that have left this worldline (`set_role_presence` with `present: false`), sorted; `[]` when nobody has. Always present from 1.53.0 — absent means an older server (unknown), never \"nobody\". A role listed here is not cast on this instance, on any lane, until a `present: true`."},"lastEntries":{"type":"array","items":{"type":"object","properties":{"entryId":{"type":"string"},"kind":{"type":"string"},"at":{"type":"integer"}}}}}}}}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/admin/environments/{envId}/instances/{worldInstanceId}/consistency":{"parameters":[{"name":"envId","in":"path","required":true,"schema":{"type":"string"},"description":"The world (environment) id — env_…."},{"name":"worldInstanceId","in":"path","required":true,"schema":{"type":"string"},"description":"The world instance — one user’s story."},{"name":"cursor","in":"query","required":false,"schema":{"type":"integer"},"description":"Resume an `incomplete` scan from its cursor."},{"name":"layers","in":"query","required":false,"schema":{"type":"string"},"description":"Comma-separated. Omitted (the default) means deterministic only, with zero model calls and a body byte-identical to world API 1.19. `model_assisted` additionally requests the judgement layer, which also needs a server flag and a declaration on the published world revision — three gates, all required."}],"get":{"summary":"What this world’s own record proves about itself — the DETERMINISTIC layer. Every finding is a comparison between committed facts and the story package this instance pins; nothing is a judgement about writing, and nothing is repaired. Rate limited on two axes (project and IP, 30/min each) and never cached. Answers 200 with `status: \"unavailable\"` when the world cannot be read, and 429 with Retry-After when a ceiling is hit.","security":[{"AdminKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"status":{"type":"string","enum":["complete","incomplete"]},"contractVersion":{"type":"integer"},"findings":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","enum":["revision_mismatch","ledger_drift","ledger_missing_entries","unrecorded_beat_claim","orphan_branch_reference","node_prereq_violation","effect_without_grant","flag_schema_violation","relation_change_without_cause","verbatim_secret_disclosure"],"description":"A CLOSED set. Members may be added in a later minor; a published member is never removed, renamed, or widened in meaning — a criterion found to be wrong is deprecated and replaced beside it. Treat an unknown code as informational rather than failing on it."},"layer":{"type":"string","enum":["deterministic"]},"status":{"type":"string","enum":["proven"]},"severity":{"type":"string","enum":["blocking","warning","info"]},"evidence":{"type":"array","description":"Never empty. Identifiers and already-committed facts; `quote` appears only on verbatim matches and carries the matched fragment, capped — never a whole line.","items":{"type":"object","properties":{"turnId":{"type":"string"},"seq":{"type":"integer"},"roleId":{"type":"string"},"sceneId":{"type":"string"},"nodeId":{"type":"string"},"branchId":{"type":"string"},"flagKey":{"type":"string"},"effectKind":{"type":"string"},"quote":{"type":"string"},"ledgerRejectionCode":{"type":"string"}}}}},"required":["code","layer","status","severity","evidence"]}},"coverage":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"skipped":{"type":"integer","description":"Entries this check could not examine."},"reason":{"type":"string","enum":["evidence_field_absent","scan_incomplete"]}},"required":["code","skipped","reason"]},"description":"What could NOT be examined, and why. A non-empty coverage list on an otherwise clean report means the report is clean about less than the whole world — absence of a finding is not proof of absence."},"entriesScanned":{"type":"integer"},"findingsTruncated":{"type":"boolean"},"cursor":{"type":"integer","description":"Present only when `status` is `incomplete`. Pass it back to continue."},"modelAssistedFindings":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","enum":["suspected_canon_conflict","suspected_revival","suspected_persona_conflict","suspected_secret_leak","suspected_cross_ip_contamination"],"description":"A SEPARATE closed set from the deterministic one, which stays frozen. These are judgements, not proofs."},"layer":{"type":"string","enum":["model_assisted"]},"status":{"type":"string","enum":["potential","confirmed","dismissed"],"description":"`potential` until a person decides. Confirmation is an editorial judgement recorded against the finding — it changes nothing about the story, the canon, the ledger or the world state."},"severity":{"type":"string","enum":["warning","info"],"description":"Never `blocking`, at the type level as well as here. A suspicion must not be able to fail somebody’s build. `suspected_cross_ip_contamination` in particular is an editorial opinion even once confirmed, and is not evidence of isolation."},"findingId":{"type":"string","description":"Stable across scans for the same judgement: derived from the code and the server-normalised evidence refs, and from nothing else. A rephrased model note does not mint a new finding, which is what lets a dismissal stick."},"evidence":{"type":"array","items":{"type":"object","properties":{"turnId":{"type":"string"},"roleId":{"type":"string"}}},"description":"Assembled server-side from committed rows. The model selects which rows by index; it never writes evidence."},"suggestion":{"type":"object","properties":{"kind":{"type":"string","enum":["author_note"]},"text":{"type":"string"}}},"reviewedBy":{"type":"string"},"reviewedAt":{"type":"string"}},"required":["code","layer","status","severity","findingId","evidence"]},"description":"Present ONLY when the request asked for `model_assisted` in `layers`. A default request carries neither this key nor `modelAssistedNote`, byte-identically to world API 1.19. Suspicions never appear in `findings` — a consumer testing `severity === \"blocking\"` there must not begin failing on an opinion."},"modelAssistedNote":{"type":"string","enum":["disabled_server","not_declared","rate_limited","unavailable","incomplete"],"description":"Why the model layer produced less than a full pass, when it was asked for. `disabled_server` and `not_declared` mean a gate was shut; `incomplete` means the per-scan budget stopped it early and the suspicions listed are not the whole picture."}},"required":["status","contractVersion","findings","coverage","entriesScanned","findingsTruncated"]},{"type":"object","properties":{"status":{"type":"string","enum":["rate_limited"]},"retryAfterSec":{"type":"integer"}},"required":["status","retryAfterSec"]},{"type":"object","properties":{"status":{"type":"string","enum":["unavailable"]},"reason":{"type":"string","enum":["world_unavailable"]}},"required":["status","reason"]}]}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/admin/environments/{envId}/instances/{worldInstanceId}/script-drafts/{draftId}/editorial/{editorialId}/approved-export/{exportId}":{"parameters":[{"name":"envId","in":"path","required":true,"schema":{"type":"string"},"description":"The world (environment) id — env_…."},{"name":"worldInstanceId","in":"path","required":true,"schema":{"type":"string"},"description":"The world instance — one user’s story."},{"name":"draftId","in":"path","required":true,"schema":{"type":"string"},"description":"The evidence draft id."},{"name":"editorialId","in":"path","required":true,"schema":{"type":"string"},"description":"The editorial draft id."},{"name":"exportId","in":"path","required":true,"schema":{"type":"string"},"description":"The approved export id — a content digest."}],"get":{"summary":"Read ONE approved export, SCRIPT INCLUDED. The collection GET beside this lists exports without their bodies; this is the only read that returns one. Mirrored on the admin key at the matching `/v1/admin/...` path, so an unattended backend can fetch an approved script without a browser anywhere in its deployment — every field it needs is in the `world.script_approved` webhook (`exportId`, and `environmentId` / `worldInstanceId` / `evidenceDraftId` / `editorialDraftId` inside `lineage`). It READS only: minting an export is a reviewer signing off on a specific text and stays on the owner plane. The body cannot change — `exportId` is its content digest. Narrow `content.contractVersion` before reading version-specific fields. Any miss — wrong project, environment, instance, draft, editorial or id — answers the SAME 404, so refusals cannot be used to map which objects exist. Served `Cache-Control: no-store`.","security":[{"AdminKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/admin/environments/{envId}/instances/{worldInstanceId}/progress":{"parameters":[{"name":"envId","in":"path","required":true,"schema":{"type":"string"},"description":"The world (environment) id — env_…."},{"name":"worldInstanceId","in":"path","required":true,"schema":{"type":"string"},"description":"The world instance — one user’s story."}],"get":{"summary":"Where the story is, and whether it can go on — a DERIVED checkpoint over facts that already committed. Nothing is stored, no model is asked, and the story package is the one this instance pinned, at its exact revision and content hash. Answers 200 even with no checkpoint: \"this world cannot be resumed\" is an answer about the story, not an error about the request.","security":[{"AdminKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"status":{"type":"string","enum":["ready"]},"resumable":{"type":"boolean"},"checkpoint":{"type":"object","properties":{"contractVersion":{"type":"integer","description":"Checkpoint contract version. 1 today."},"environmentRevision":{"type":"integer","description":"The revision this INSTANCE is pinned to for life — never the environment head."},"storyPackageRef":{"type":"object","properties":{"packageId":{"type":"string"},"revision":{"type":"integer"},"contentHash":{"type":"string"}},"required":["packageId","revision","contentHash"]},"stateRevision":{"type":"integer","description":"The CAS counter the turns door expects."},"started":{"type":"boolean","description":"At least one beat has committed."},"currentScene":{"type":["object","null"],"properties":{"sceneId":{"type":"string"},"title":{"type":"string"}},"description":"Null when the world has not opened a scene. A sceneId the pinned story does not declare is NOT reported as null — the whole read answers `story_reference_invalid` instead."},"completedNodeCount":{"type":"integer"},"declaredNodeCount":{"type":"integer","description":"How many nodes the AUTHOR declared. Not a denominator: a branching story never visits all of them, so a ratio of these two numbers means nothing."},"publicRelations":{"type":"array","items":{"type":"object","properties":{"between":{"type":"array","items":{"type":"string"}},"descriptor":{"type":"string"}}},"description":"World-public relations, sorted. Role-private material never appears here."},"publicRelationsTruncated":{"type":"boolean"},"publicFlags":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"value":{"description":"boolean | number | string, or null when the flag was never set and declares no initial"}}},"description":"Contract v4. The flags the pinned story declared `public: true`, sorted by key, with the value the state THIS checkpoint reports (the declared initial when never set; null when there is neither). ALWAYS present — an empty array for a story that publishes nothing — so absent never reads as unknown. A flag the story did not mark public never appears here."},"publicFlagsTruncated":{"type":"boolean"},"recentProgressRecords":{"type":"array","items":{"type":"object","properties":{"seq":{"type":"integer"},"kind":{"type":"string","enum":["turn","event","system"]},"at":{"type":"integer"}}},"description":"The newest committed beats, newest first — that one happened, at a revision, at a time. Not a story summary: no patches, no lines, no actors."},"recentProgressTruncated":{"type":"boolean"},"nextOptions":{"type":"array","items":{"type":"object","properties":{"branchId":{"type":"string"},"condition":{"type":"string"}}},"description":"Branches reachable from the completed nodes, derived from the pinned story. Never model-generated, and capped server-side."},"rebuildable":{"type":"boolean","description":"Always true — nothing here is stored; it is a function of committed facts."}},"required":["contractVersion","environmentRevision","storyPackageRef","stateRevision","started","completedNodeCount","declaredNodeCount","publicFlags","publicFlagsTruncated","nextOptions"]}},"required":["status","resumable","checkpoint"]},{"type":"object","properties":{"status":{"type":"string","enum":["unavailable"]},"resumable":{"type":"boolean","enum":[false]},"reason":{"type":"string","enum":["world_unavailable","story_reference_invalid"],"description":"`world_unavailable` covers every \"no answer\" cause at once — no such instance, no access, a revoked or deleted package, a contract this build cannot read. They are one word on purpose: naming them apart would let a caller map which internal objects exist by reading the refusals."}},"required":["status","resumable","reason"]}]}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/admin/environments/{envId}/instances/{worldInstanceId}/turns":{"parameters":[{"name":"envId","in":"path","required":true,"schema":{"type":"string"},"description":"The world (environment) id — env_…."},{"name":"worldInstanceId","in":"path","required":true,"schema":{"type":"string"},"description":"The world instance — one user’s story."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer"},"description":"How many beats to return. Capped server-side."},{"name":"sinceSeq","in":"query","required":false,"schema":{"type":"integer"},"description":"Walk FORWARD from a revision already processed: beats with seq > sinceSeq, OLDEST first, plus `nextSinceSeq` to continue. The resume cursor."}],"get":{"summary":"The committed timeline, on the admin key. READ only — driving a turn stays on the machine lane at /projects/{projectId}/…/turns, which requires a Secret Key AND a provider signature. An admin key proves the project, not the Provider.","security":[{"AdminKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/admin/environments/{envId}/instances/{worldInstanceId}/turns/{turnId}":{"parameters":[{"name":"envId","in":"path","required":true,"schema":{"type":"string"},"description":"The world (environment) id — env_…."},{"name":"worldInstanceId","in":"path","required":true,"schema":{"type":"string"},"description":"The world instance — one user’s story."},{"name":"turnId","in":"path","required":true,"schema":{"type":"string"},"description":"The turn id."}],"get":{"summary":"Read back ONE committed turn — the recovery path for a dropped response, now reachable by the process that dropped it.","security":[{"AdminKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"properties":{"turnId":{"type":"string"},"worldInstanceId":{"type":"string"},"beforeStateRevision":{"type":"integer"},"afterStateRevision":{"type":"integer"},"roleMessages":{"type":"array","items":{"type":"object","properties":{"roleId":{"type":"string"},"message":{"type":"string"}}}},"committedStateDiff":{"type":"array","items":{"type":"object","additionalProperties":true}},"rejectedEffects":{"type":"array","items":{"type":"object","properties":{"index":{"type":"integer"},"reason":{"type":"string"},"roleId":{"type":"string"},"kind":{"type":"string"},"code":{"type":"string","enum":["narrative_conflict","policy_rejection","schema_rejection","canon_rejection","concurrency_rejection","repair_failure","provider_precedence"],"description":"WHY, as a code. `narrative_conflict` is the only one that describes story content; every other code is a defect in the story package, the prompt or the integration. Absent on a turn committed before the taxonomy — read that as unknown, never as narrative. The read-back (GET …/turns/{turnId}) carries it since 1.51.1."}}}},"selectedRoles":{"type":"array","items":{"type":"string"}},"skippedRoles":{"type":"array","description":"Roles that were in the cast but did not speak. `reason` is the HUMAN line and carries the sub-cause; it is free to be reworded, so never key off it. `code` is the machine half — a closed enum (see below) added 2026-09-09 after an integrator, given the prefixes in a letter because this document carried none, built a cost model on `reason.startsWith(...)`. To read a beat: every role in `selectedRoles` either has a line in `roleMessages` or is listed here — a role that produced no line, deliberately or not, is listed here as `no_message`, never left out. (Before 1.52.0 this sentence said a role in `selectedRoles` and absent here \"chose silence\"; that was never true.) A role that CHOSE silence under `trigger.allowSilence` additionally carries `silence: \"chosen\"`; `x-pouchy-skip-semantics` is per code, so branch on `silence` for that one distinction.","items":{"type":"object","properties":{"roleId":{"type":"string"},"reason":{"type":"string"},"silence":{"type":"string","enum":["chosen"],"description":"World API 1.52.0. Present only with `code: \"no_message\"`, and only when the role output the silence token `trigger.allowSilence` offered it: it CHOSE not to speak — not a fault, and it does not make the beat `partial`. Absent on every other `no_message` (a blank reply, a terminal that was not a message); read that as unknown, never as a choice — a blank reply is also what a failed final model call returns."},"code":{"type":"string","enum":["not_focused","role_cap_reached","no_actor_bound","role_departed","deadline_exhausted","not_admitted","actor_unavailable","session_busy","content_policy","turn_error","no_message","effect_refused","conflict_undelivered"],"x-pouchy-cost-model":{"version":"1.1.0","noModelCall":["not_focused","role_cap_reached","no_actor_bound","role_departed","deadline_exhausted","not_admitted","actor_unavailable","session_busy","content_policy"],"modelMayHaveRun":["turn_error","no_message","effect_refused","conflict_undelivered"],"digest":"1f836a3d5fe7c4db"},"x-pouchy-skip-semantics":{"version":"1.1.0","codes":{"not_focused":{"stage":"never_cast","fault":"none","meaning":"The caller narrowed this beat with focusRoles and did not name this role."},"role_cap_reached":{"stage":"never_cast","fault":"capacity","meaning":"The beat was already full at WORLD_TURN_MAX_ROLES. Seats fill in DECLARATION order, so the same role is skipped on every beat until the cast or the focus changes."},"no_actor_bound":{"stage":"never_cast","fault":"configuration","meaning":"No actor is bound to this role in this world. An operator fixes it by minting one."},"role_departed":{"stage":"never_cast","fault":"none","meaning":"The role has left this worldline (set_role_presence with present:false) and is not cast on this instance, on any lane, until a present:true brings it back. Other instances of the environment are unaffected."},"deadline_exhausted":{"stage":"cast_no_model","fault":"capacity","meaning":"The beat's time budget ran out before this role's turn; the roles ahead of them spent it."},"not_admitted":{"stage":"cast_no_model","fault":"policy","meaning":"Admission refused the run — metering, a suspended instance, or another gate. A decision, not a defect: the sub-cause is in `reason`, and retrying changes nothing until the underlying condition does."},"actor_unavailable":{"stage":"cast_no_model","fault":"defect","meaning":"The actor's registry row is missing or belongs to the other environment. Distinct from not_admitted on purpose: one is a policy answering, the other is a thing that should exist and does not."},"session_busy":{"stage":"cast_no_model","fault":"transient","meaning":"A beat for this role is already in flight and its turn lease refused. Transient by construction."},"content_policy":{"stage":"cast_no_model","fault":"policy","meaning":"The moderation gate silenced this role BEFORE any model ran — a classifier verdict or a blocked term."},"turn_error":{"stage":"model_may_have_run","fault":"defect","meaning":"The runtime failed. Deliberately does NOT promise whether a model call happened: context assembly failing and a provider dying mid-stream both land here, and no code can separate them after the fact."},"no_message":{"stage":"model_may_have_run","fault":"none","meaning":"The turn completed without a line — a terminal that was not a message, or a deliberately silent beat. The model ran."},"effect_refused":{"stage":"spoke_withheld","fault":"policy","meaning":"This role's effect was refused and the one correction pass did not save it, so their line was never published."},"conflict_undelivered":{"stage":"spoke_withheld","fault":"transient","meaning":"The world moved under the turn; nothing was published, by anyone, on this beat."}},"digest":"70c09e4e50a838f7"},"description":"WHY, machine-readable. Each code also carries a structured `stage` and `fault` in `x-pouchy-skip-semantics` — branch on those rather than on this paragraph. No model call was made for `not_focused` (the caller narrowed the cast), `role_cap_reached` (the three-seat staging limit, decided in declaration order — the same role is skipped every beat until the cast changes), `no_actor_bound` (setup incomplete), `deadline_exhausted` (the beat ran out of budget first), `not_admitted` (metering / suspension / a gate said no — a decision), `actor_unavailable` (the actor registry row is missing or belongs to the other env — a defect), `session_busy` (a beat for this role is already in flight) or `content_policy` (the moderation gate silenced them BEFORE any model ran). A model call was made, or may have been, for `turn_error` (the runtime failed — deliberately does NOT promise a call happened: assembly failing and a provider dying mid-stream both land here) and `no_message` (the turn completed with no line — a non-message terminal, a blank reply, or, with `silence: \"chosen\"`, a role that chose not to speak). The role DID speak and the line was withheld for `effect_refused` (their effect was refused and the one correction did not save it) and `conflict_undelivered` (the world moved under the turn; nothing was published to anyone). Absent only from a non-production caller; every path in the platform sets it."}}}},"repairs":{"type":"array","items":{"type":"object","properties":{"roleId":{"type":"string"},"outcome":{"type":"string","enum":["repaired","fallback"]},"reason":{"type":"string"}}}},"nextOptions":{"type":"array","items":{"type":"object","properties":{"branchId":{"type":"string"},"condition":{"type":"string"}}}},"committedAt":{"type":"integer","description":"Epoch milliseconds the single writer committed the beat."},"environmentRevision":{"type":"integer"},"eventId":{"type":"string"},"traceId":{"type":"string"},"completionStatus":{"type":"string","enum":["committed"],"description":"Always `committed`: a turn that did not commit has no entry and answers 404."},"executionStatus":{"type":"string","enum":["committed"]},"deliveryStatus":{"type":"string"}},"description":"The committed turn, read from its ledger entry in the live result’s shape. `selectedRoles`, `skippedRoles`, `repairs` and `nextOptions` are absent on an entry that predates their recording — read that as unknown, not \"none\"."}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/admin/environments/{envId}/instances/{worldInstanceId}/metrics":{"parameters":[{"name":"envId","in":"path","required":true,"schema":{"type":"string"},"description":"The world (environment) id — env_…."},{"name":"worldInstanceId","in":"path","required":true,"schema":{"type":"string"},"description":"The world instance — one user’s story."}],"get":{"summary":"Delivery health and turn quality, kept apart, on the admin key.","security":[{"AdminKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/admin/environments/{envId}/instances/{worldInstanceId}/deliveries":{"parameters":[{"name":"envId","in":"path","required":true,"schema":{"type":"string"},"description":"The world (environment) id — env_…."},{"name":"worldInstanceId","in":"path","required":true,"schema":{"type":"string"},"description":"The world instance — one user’s story."},{"name":"status","in":"query","required":false,"schema":{"type":"string"},"description":"pending | delivering | delivered | retry_wait | dead | rehydrating | unrecoverable | resolved_gap"},{"name":"sessionId","in":"query","required":false,"schema":{"type":"string"},"description":"Narrow to one delivery stream."},{"name":"turnId","in":"query","required":false,"schema":{"type":"string"},"description":"Narrow to one beat’s lines."},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"Continue a page."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer"},"description":"Rows per page. Capped server-side."}],"get":{"summary":"The delivery queue VIEW, on the admin key. Statuses, timings, attempts and error classes — never message bodies. The queue ACTIONS (requeue, rehydrate, resolve-gap, drain) are deliberately not mirrored: each decides what happens to a reader who is missing a beat.","security":[{"AdminKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/sessions":{"post":{"summary":"Mint a session. With a `world` block ({ environment, role, request_id, world_instance? }) this mints a WORLD session bound to one instance and one role — the token your own frontend then uses with the companion SDK. request_id makes the mint idempotent (same id + same body = same instance, no second metering).","security":[{"SecretKey":[],"SourceSignature":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"external_user_id":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"},"description":"Narrow-only: intersected with the non-sensitive default set. It cannot name a scope the AGENT TEMPLATE grants (ui.render, skills.execute, wallet.read, chat.instructions, data.activity) — those are not in the default set, so this field can neither add nor remove them. Use withhold_scopes for that."},"withhold_scopes":{"type":"array","items":{"type":"string"},"description":"Scopes this session must NOT carry, whatever the agent template grants. Subtracted LAST, so it can only ever REMOVE — never an escalation path, and an unknown or ungranted name is inert rather than an error. This is the only way to narrow a template-granted capability, and it is per SESSION on purpose: a per-agent setting cannot express a per-END-USER policy, because the same agent serves the users the policy covers and the users it does not. The case it was built for: closing the Instant-UI surface for a class of end user with [\"ui.render\"], so the model is never offered render_interface and cannot render a free-text TextInput panel around a host-side input restriction. NOTE for the world plane: Instant UI is a COMPANION-SESSION capability and does not reach a coordinated world beat at all. A beat runs on its own world-turn session, whose outbound stream no reader token opens and no world route reads, so the panel tools are withheld there whatever the agent template grants — `genui` changes nothing about a beat, and the scope is inert rather than an escalation. Panels for a world reader belong on the companion session that reader holds, not on the beat. AND NOTE WHAT THAT MEANS FOR THIS FIELD ON THE WORLD PLANE: withhold_scopes narrows the TOKEN it mints, and a coordinated beat does not run on that token. A beat resolves its own scopes from the pinned production agent plus the platform default set (scopesForAgent(prod.agent, DEFAULT_SCOPES)), so withholding a scope at mint time changes what THAT READER SESSION may do and changes nothing about what a character may do in a beat — and for `files` specifically the beat now subtracts it by default anyway (world API 1.45.0), so the opt-in that governs create_spreadsheet / create_document on a beat is the agent template's `worldFiles` flag, not this field. Re-reading `scopes` on the mint confirms the mint, never the beat. There is no per-reader scope narrowing for beats today; the narrowing that reaches a beat is the agent template a world pins."},"world":{"type":"object","properties":{"environment":{"type":"string"},"role":{"type":"string"},"request_id":{"type":"string"},"world_instance":{"type":"string"}},"required":["environment","role","request_id"]}},"required":["external_user_id"]}}}},"responses":{"200":{"description":"Session minted: { session_token, expires_in, agent, scopes, instance: { id, external_user_id, created }, world: { environment, environment_revision, instance, role } }. `scopes` is the EFFECTIVE set the token carries — after the narrow, after the template grants, after any withhold. When the body asked to withhold, `withheld` names what was actually removed and `withhold_ignored` names the requested scopes that removed nothing (unknown name, or one this agent never granted) — so a withhold that silently did nothing is not returned as a success.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/projects/{projectId}/events":{"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"},"description":"The project id."}],"post":{"summary":"Deliver a trusted event. With a `world: { environment, instance }` block it routes into that world: on a coordinated world it becomes ONE coordinator turn (turnId derived from eventId, so a re-delivery re-runs nothing); on an actor world it wakes each subscribed role. Roles are resolved server-side from the pinned definition — a payload can never grant one.","security":[{"SecretKey":[],"SourceSignature":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"eventId":{"type":"string"},"schemaVersion":{"type":"integer"},"data":{"type":"object","additionalProperties":true},"occurredAt":{"type":"integer"},"world":{"type":"object","properties":{"environment":{"type":"string"},"instance":{"type":"string"}},"required":["environment","instance"]}},"required":["name","eventId","data"]}}}},"responses":{"202":{"description":"Accepted (or a duplicate of an accepted event)","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/projects/{projectId}/story-packages":{"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"},"description":"The project id."}],"get":{"summary":"List story package heads — metadata only, never content bodies.","security":[{"OwnerToken":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"packages":{"type":"array","items":{"type":"object","properties":{"packageId":{"type":"string"},"name":{"type":"string"},"latestRevision":{"type":"integer"},"disabled":{"type":"boolean"},"pinnedRevisions":{"type":["integer","null"],"description":"How many ENVIRONMENT REVISIONS have ever pinned this package. Monotonic — a superseded revision still counts, because an instance stays pinned to the revision it was created on and re-reads the package every beat. `null` = UNKNOWN (written before the stamp existed); never conflated with 0."},"lastPinnedBy":{"type":["string","null"],"description":"The most recent environment that pinned it; null when none has, or when the count is unknown."},"deletable":{"type":["boolean","null"],"description":"What DELETE would answer, without calling it. true = never pinned, the delete succeeds. false = pinned, refused `has_pins`. null = UNKNOWN, refused `pins_unknown`. Three states because the refusal has three and their remedies differ."},"createdAt":{"type":"string"},"createdBy":{"type":"string"},"updatedAt":{"type":"string"},"updatedBy":{"type":"string"}},"required":["packageId","name","latestRevision","pinnedRevisions","lastPinnedBy","deletable"]}}}}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}},"post":{"summary":"Import a story package as revision 1: roles bound to world roles, scenes, plot nodes, established facts, constraints, branches, endings, a typed flag schema, per-role visibility and per-role effect grants. The original screenplay stays a REFERENCE ({title, version}), never stored bytes. Optionally IDEMPOTENT: supply `clientRequestId` and the package id is derived from it, so a creation whose response was lost can be retried with the same key AND the same body to get the ORIGINAL package back (`created: false`) instead of a second one. The same key with DIFFERENT content is refused 409 `idempotency_key_reused` — never reported as a success for content that was never written.","security":[{"OwnerToken":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"required":["name","summary","roles"],"description":"A Story Package v1 body. Array ORDER is narrative order and therefore content — reordering roles or scenes mints a different revision. Referential and uniqueness rules are enforced by the server and named in the field descriptions below. SIZE: besides each list's maxItems, the whole package is at most 900000 bytes, measured as the UTF-8 JSON of the normalized content (a published revision is one stored document; CJK text is 3 bytes a character). Over it is a 400 naming the measured size.","x-pouchy-max-bytes":900000,"properties":{"name":{"type":"string","maxLength":80,"description":"Required."},"summary":{"type":"string","maxLength":1000,"description":"Required."},"source":{"type":"object","additionalProperties":true,"required":["title","version"],"description":"A REFERENCE to the original screenplay — never its bytes. Both fields are required when `source` is present at all.","properties":{"title":{"type":"string","maxLength":200,"description":""},"version":{"type":"string","maxLength":60,"description":""}}},"canon":{"type":"array","items":{"type":"string","maxLength":500,"description":""},"maxItems":100,"description":"World-shared truth every role sees. Entries are trimmed; empty strings are refused."},"constraints":{"type":"array","items":{"type":"string","maxLength":500,"description":""},"maxItems":50,"description":"What the story must never do. Same entry rules as canon."},"roles":{"type":"array","items":{"type":"object","additionalProperties":true,"required":["storyRoleId","worldRoleId"],"properties":{"storyRoleId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{1,63}$","description":"Lowercase machine id, 2–64 chars."},"worldRoleId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{1,63}$","description":"The WORLD role this story role plays. Must exist in the world revision that pins this package — checked at PIN time, not here, because a package is project-scoped and may be pinned by many worlds. At most one story role per worldRoleId."},"description":{"type":"string","maxLength":500,"description":"Authoring metadata and a dashboard label. It reaches NO prompt at runtime, not even this role’s own — a role’s persona is the bound agent’s `systemPrompt`."},"secrets":{"type":"array","items":{"type":"string","maxLength":500,"description":""},"maxItems":20,"description":"Role-PRIVATE canon. Never rendered to another role, in any projection."},"goals":{"type":"array","items":{"type":"string","maxLength":500,"description":""},"maxItems":20,"description":"Rendered into THIS role’s own per-beat envelope, and nowhere else — never into another role’s. It does NOT drive role selection: selection is declaration order intersected with bound actors and with focusRoles / event subscriptions, minus roles that have left the worldline (1.53.0), capped at 3, and reads no goals at all. (The previous wording said otherwise and described a design that was never built.)"},"effectsAllow":{"type":"array","maxItems":8,"items":{"type":"string","enum":["set_flag","increment_flag","reveal_fact","update_relationship","update_entity","advance_clock","request_scene_transition","complete_objective"]},"description":"What this role may PROPOSE. DEFAULT DENY: absent means the role proposes nothing and the effect tool is not offered to it at all. An unknown kind is an error, never a silent drop."}}},"maxItems":24,"description":"At least one role is required. storyRoleId is unique; worldRoleId is bound at most once."},"scenes":{"type":"array","items":{"type":"object","additionalProperties":true,"required":["sceneId","title"],"properties":{"sceneId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Lowercase machine id; dots allowed for namespacing (fact.ferry)."},"title":{"type":"string","maxLength":120,"description":"Required."},"description":{"type":"string","maxLength":500,"description":""},"location":{"type":"string","maxLength":120,"description":""}}},"maxItems":100,"description":"sceneId is unique within the package."},"nodes":{"type":"array","items":{"type":"object","additionalProperties":true,"required":["nodeId","sceneId","title","objective"],"properties":{"nodeId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Lowercase machine id; dots allowed for namespacing (fact.ferry)."},"sceneId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Must name a scene declared in `scenes`."},"title":{"type":"string","maxLength":120,"description":""},"objective":{"type":"string","maxLength":500,"description":""},"prerequisites":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Lowercase machine id; dots allowed for namespacing (fact.ferry)."},"maxItems":10,"description":"Node ids that must be completed first. Each must be a declared node, and never this node itself."}}},"maxItems":200,"description":"A plot node is an objective inside a scene. nodeId is unique."},"establishedFacts":{"type":"array","items":{"type":"object","additionalProperties":true,"required":["factId","text"],"properties":{"factId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Lowercase machine id; dots allowed for namespacing (fact.ferry)."},"text":{"type":"string","maxLength":500,"description":""}}},"maxItems":200,"description":"factId is unique."},"branches":{"type":"array","items":{"type":"object","additionalProperties":true,"required":["branchId","fromNodeId","condition","toNodeId"],"properties":{"branchId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Lowercase machine id; dots allowed for namespacing (fact.ferry)."},"fromNodeId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Must be a declared node."},"toNodeId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Must be a declared node."},"condition":{"type":"string","maxLength":500,"description":"Descriptive text in v1 — the runtime does not evaluate it. It is what a turn offers as a next option."}}},"maxItems":200,"description":"branchId is unique."},"endings":{"type":"array","items":{"type":"object","additionalProperties":true,"required":["endingId","title","condition"],"properties":{"endingId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Lowercase machine id; dots allowed for namespacing (fact.ferry)."},"title":{"type":"string","maxLength":120,"description":""},"condition":{"type":"string","maxLength":500,"description":""}}},"maxItems":60,"description":"endingId is unique."},"stateSchema":{"type":"object","additionalProperties":true,"description":"The declared world-state flags. Absent means a package with no flags.","properties":{"flags":{"type":"array","items":{"type":"object","additionalProperties":true,"required":["key","kind"],"properties":{"key":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Lowercase machine id; dots allowed for namespacing (fact.ferry)."},"kind":{"type":"string","enum":["boolean","number","string"]},"initial":{"description":"Optional starting value. Its JSON type must match `kind` exactly — a number initial on a string flag is refused."},"public":{"type":"boolean","enum":[true],"description":"Contract v4. `true` publishes this flag — its NAME and its VALUE — on the progress checkpoint’s `publicFlags` for any client holding the instance. Absent keeps it out of the checkpoint (a flag’s name can itself be a spoiler). `false` is refused: the only two states are published and not. Declaring any public flag is what makes a package contract v4; the server decides the version and refuses a client-supplied `contractVersion`."},"range":{"type":"object","required":["min","max"],"properties":{"min":{"type":"number"},"max":{"type":"number"}},"description":"PRESENTATION ONLY — the full scale this number runs on, so a client can draw a meter instead of printing a bare integer. Valid only on `kind: \"number\"`; refused on the others, because `{min:0,max:1}` on a boolean reads as \"it can be 0.5\". `min` must be STRICTLY less than `max`: equal bounds divide by zero in every client that draws a meter. Absent means the author did not say, and a client must not invent one — assuming /100 renders a 0–10 score as \"6% of the way there\". Nothing keys off this: not the coordinator, not ending adjudication, not the ledger, and it is NOT a contract version (an older reader ignoring it prints the number without a meter, which is what every client did before it existed)."},"label":{"type":"string","description":"PRESENTATION ONLY — the human name for this flag. Absent means a client derives one from `key` (`trust_amara` → `TRUST · AMARA`). It exists because `key` also has to serve as the identifier a rule condition names, and the two jobs have conflicting naming constraints. NEVER translated by a client: these strings are authored inside the package, in the language of the work. Absent or a non-empty string — `\"\"` is refused, because an empty label cannot be told from an unfilled form field and one meaning with two byte spellings is what `public` already refuses."}}},"maxItems":50,"description":"key is unique."}}},"effectRules":{"type":"object","additionalProperties":true,"required":["conflict"],"description":"How the coordinator settles TWO ROLES proposing incompatible effects on the SAME target in one turn. Absent means `reject`. There is deliberately no \"the model decides\" option. It does NOT govern the caller: a `trigger.proposedPatches` op always beats a role effect on the same axis, whatever this says (world API 1.46.0) — see that field. Making that author-configurable would make the deterministic entrance conditional on an author’s dramatic taste, which is the one property it exists to have.","properties":{"conflict":{"type":"string","enum":["reject","first_wins"]}}},"initialRelations":{"type":"array","maxItems":50,"description":"Contract v2. The relations the story opens with, seeded into a NEW world instance only. A SET, not a list: [a,b] and [b,a] are one relation, a pair declared twice is REFUSED rather than merged (differing descriptors are a contradiction; identical ones are still two rows for one key), and the whole array is sorted before hashing — so authoring order cannot change the content hash. Every other array in this contract keeps declaration order. EVERY relation here is PUBLIC: relations live in the shared block of world state, so a relation that must stay hidden belongs in the authorised role’s `secrets` instead. Supplying this field is what makes a package contract v2; the server decides the version and refuses a client-supplied `contractVersion`.","items":{"type":"object","additionalProperties":true,"required":["between","descriptor"],"properties":{"between":{"type":"array","minItems":2,"maxItems":2,"items":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{1,63}$","description":"Lowercase machine id, 2–64 chars."},"description":"Two DISTINCT declared storyRoleIds. Normalized to ascending order."},"descriptor":{"type":"string","maxLength":120}}}},"series":{"type":"object","additionalProperties":true,"required":["seriesId","title","episodes"],"description":"Contract v3. Episodes, and the deterministic rules that decide when one is over. Supplying this field is what makes a package contract v3; the server decides the version and refuses a client-supplied `contractVersion`. Every id a rule names — node, fact, scene, flag, ending — must be DECLARED in the same package: a rule that can never hold would make the episode end only by fallback, silently. Ending rules are evaluated SERVER-SIDE over committed state after a beat commits; no model, client or prompt supplies an endingId.","properties":{"seriesId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Lowercase machine id; dots allowed for namespacing (fact.ferry)."},"title":{"type":"string","maxLength":120},"episodes":{"type":"array","maxItems":24,"description":"DECLARATION order is broadcast order. At least one; there is no empty-series form, because absent already means \"not a serial\".","items":{"type":"object","additionalProperties":true,"required":["episodeId","title","turnBudget","fallbackEndingId"],"properties":{"episodeId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Lowercase machine id; dots allowed for namespacing (fact.ferry)."},"title":{"type":"string","maxLength":120},"turnBudget":{"type":"integer","minimum":1,"maximum":200,"description":"Committed beats this episode may run before the fallback ending fires. Capped at the evidence window: an episode longer than one evidence read can never be turned into a script."},"fallbackEndingId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"A DECLARED ending, used when no rule ever holds. Required — the alternative is an episode that cannot end."},"endingRules":{"type":"array","maxItems":20,"description":"Order-independent: priority decides (lower wins) and `ruleId` breaks a tie. Reordering this array changes nothing, unlike every other array in this contract.","items":{"type":"object","additionalProperties":true,"required":["ruleId","endingId","priority","all"],"properties":{"ruleId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Lowercase machine id; dots allowed for namespacing (fact.ferry)."},"endingId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Lowercase machine id; dots allowed for namespacing (fact.ferry)."},"priority":{"type":"integer"},"all":{"type":"array","minItems":1,"maxItems":10,"description":"A CONJUNCTION over a closed condition vocabulary — `node_completed`, `fact_revealed`, `flag_equals`, `scene_is`, `clock_at_least`. An OR is two rules. An empty list would be true of every state and is refused.","items":{"type":"object","additionalProperties":true}}}}}}}}}},"visibility":{"type":"object","additionalProperties":true,"required":["factDefault"],"description":"Role visibility over established facts and (Story Contract v5, world API 1.55.0) story flags. Canon is world-shared by definition and role `secrets` are private by definition; facts and flags sit between. Absent means { factDefault: \"shared\" } and every role sees every flag.","properties":{"factDefault":{"type":"string","enum":["shared","role-scoped"]},"factAllow":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Lowercase machine id; dots allowed for namespacing (fact.ferry)."}},"description":"worldRoleId → factId[]. Every key must be a declared worldRoleId and every value a declared factId; both are refused by name rather than ignored."},"flagDefault":{"type":"string","enum":["shared","role-scoped"],"description":"Story Contract v5 (world API 1.55.0). `role-scoped`: a role sees a flag only when `flagAllow` names it for that role, or the flag is `public` (public is world-public — every client and every role). Hidden means hidden wherever a ROLE reads: its envelope (key and value), its call brief, a deliberation, and its effect brief; a role `set_flag` / `increment_flag` on a flag it cannot see is refused exactly like an undeclared flag (`schema_rejection`). Everything that is not a role sees every flag: your `proposedPatches` may write a hidden flag, ending rules read it, and `getWorldState` returns it. A flag in no role’s list is hidden from every role. Absent or `shared` is the behaviour before v5 (and `shared` is dropped, so it never changes the package’s bytes or version). Using `role-scoped` makes the package contract v5; an environment that has not enabled v5 writes answers 409 `story_contract_v5_write_disabled` with the field intact, and a v5 package cannot be read by a pre-1.55.0 server. The switch is DEPLOYMENT-wide — it covers every project at once and is never set per project; `GET https://pouchy.ai/api/version` (public, no auth) reports whether the serving deployment writes v5 as `storyContractWrites.v5`, so check that before publishing rather than learning it from a 409."},"flagAllow":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Lowercase machine id; dots allowed for namespacing (fact.ferry)."}},"description":"Story Contract v5. worldRoleId → flag keys, only with `flagDefault: \"role-scoped\"` (refused otherwise, never ignored). Every key must be a declared worldRoleId and every value a declared flag; a `public` flag may not be listed."}}},"clientRequestId":{"type":"string","pattern":"^[A-Za-z0-9_-]{16,128}$","description":"Optional idempotency token for CREATION. Transport, not story content: it is lifted out of the body before validation and is never stored, logged or echoed. Omit it and creation behaves exactly as it always has (a random package id)."}}}}}},"responses":{"201":{"description":"Created as revision 1","content":{"application/json":{"schema":{"type":"object","properties":{"packageId":{"type":"string"},"revision":{"type":"integer"},"contentHash":{"type":"string"}}}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/projects/{projectId}/story-packages/{packageId}":{"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"},"description":"The project id."},{"name":"packageId","in":"path","required":true,"schema":{"type":"string"},"description":"The story package id — sp_…."}],"get":{"summary":"Head plus the current story content.","security":[{"OwnerToken":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"head":{"type":"object","properties":{"packageId":{"type":"string"},"name":{"type":"string"},"latestRevision":{"type":"integer"},"disabled":{"type":"boolean"},"pinnedRevisions":{"type":["integer","null"],"description":"How many ENVIRONMENT REVISIONS have ever pinned this package. Monotonic — a superseded revision still counts, because an instance stays pinned to the revision it was created on and re-reads the package every beat. `null` = UNKNOWN (written before the stamp existed); never conflated with 0."},"lastPinnedBy":{"type":["string","null"],"description":"The most recent environment that pinned it; null when none has, or when the count is unknown."},"deletable":{"type":["boolean","null"],"description":"What DELETE would answer, without calling it. true = never pinned, the delete succeeds. false = pinned, refused `has_pins`. null = UNKNOWN, refused `pins_unknown`. Three states because the refusal has three and their remedies differ."},"createdAt":{"type":"string"},"createdBy":{"type":"string"},"updatedAt":{"type":"string"},"updatedBy":{"type":"string"}},"required":["packageId","name","latestRevision","pinnedRevisions","lastPinnedBy","deletable"]},"content":{"type":"object","additionalProperties":true,"required":["name","summary","roles"],"description":"A Story Package v1 body. Array ORDER is narrative order and therefore content — reordering roles or scenes mints a different revision. Referential and uniqueness rules are enforced by the server and named in the field descriptions below. SIZE: besides each list's maxItems, the whole package is at most 900000 bytes, measured as the UTF-8 JSON of the normalized content (a published revision is one stored document; CJK text is 3 bytes a character). Over it is a 400 naming the measured size.","x-pouchy-max-bytes":900000,"properties":{"name":{"type":"string","maxLength":80,"description":"Required."},"summary":{"type":"string","maxLength":1000,"description":"Required."},"source":{"type":"object","additionalProperties":true,"required":["title","version"],"description":"A REFERENCE to the original screenplay — never its bytes. Both fields are required when `source` is present at all.","properties":{"title":{"type":"string","maxLength":200,"description":""},"version":{"type":"string","maxLength":60,"description":""}}},"canon":{"type":"array","items":{"type":"string","maxLength":500,"description":""},"maxItems":100,"description":"World-shared truth every role sees. Entries are trimmed; empty strings are refused."},"constraints":{"type":"array","items":{"type":"string","maxLength":500,"description":""},"maxItems":50,"description":"What the story must never do. Same entry rules as canon."},"roles":{"type":"array","items":{"type":"object","additionalProperties":true,"required":["storyRoleId","worldRoleId"],"properties":{"storyRoleId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{1,63}$","description":"Lowercase machine id, 2–64 chars."},"worldRoleId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{1,63}$","description":"The WORLD role this story role plays. Must exist in the world revision that pins this package — checked at PIN time, not here, because a package is project-scoped and may be pinned by many worlds. At most one story role per worldRoleId."},"description":{"type":"string","maxLength":500,"description":"Authoring metadata and a dashboard label. It reaches NO prompt at runtime, not even this role’s own — a role’s persona is the bound agent’s `systemPrompt`."},"secrets":{"type":"array","items":{"type":"string","maxLength":500,"description":""},"maxItems":20,"description":"Role-PRIVATE canon. Never rendered to another role, in any projection."},"goals":{"type":"array","items":{"type":"string","maxLength":500,"description":""},"maxItems":20,"description":"Rendered into THIS role’s own per-beat envelope, and nowhere else — never into another role’s. It does NOT drive role selection: selection is declaration order intersected with bound actors and with focusRoles / event subscriptions, minus roles that have left the worldline (1.53.0), capped at 3, and reads no goals at all. (The previous wording said otherwise and described a design that was never built.)"},"effectsAllow":{"type":"array","maxItems":8,"items":{"type":"string","enum":["set_flag","increment_flag","reveal_fact","update_relationship","update_entity","advance_clock","request_scene_transition","complete_objective"]},"description":"What this role may PROPOSE. DEFAULT DENY: absent means the role proposes nothing and the effect tool is not offered to it at all. An unknown kind is an error, never a silent drop."}}},"maxItems":24,"description":"At least one role is required. storyRoleId is unique; worldRoleId is bound at most once."},"scenes":{"type":"array","items":{"type":"object","additionalProperties":true,"required":["sceneId","title"],"properties":{"sceneId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Lowercase machine id; dots allowed for namespacing (fact.ferry)."},"title":{"type":"string","maxLength":120,"description":"Required."},"description":{"type":"string","maxLength":500,"description":""},"location":{"type":"string","maxLength":120,"description":""}}},"maxItems":100,"description":"sceneId is unique within the package."},"nodes":{"type":"array","items":{"type":"object","additionalProperties":true,"required":["nodeId","sceneId","title","objective"],"properties":{"nodeId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Lowercase machine id; dots allowed for namespacing (fact.ferry)."},"sceneId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Must name a scene declared in `scenes`."},"title":{"type":"string","maxLength":120,"description":""},"objective":{"type":"string","maxLength":500,"description":""},"prerequisites":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Lowercase machine id; dots allowed for namespacing (fact.ferry)."},"maxItems":10,"description":"Node ids that must be completed first. Each must be a declared node, and never this node itself."}}},"maxItems":200,"description":"A plot node is an objective inside a scene. nodeId is unique."},"establishedFacts":{"type":"array","items":{"type":"object","additionalProperties":true,"required":["factId","text"],"properties":{"factId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Lowercase machine id; dots allowed for namespacing (fact.ferry)."},"text":{"type":"string","maxLength":500,"description":""}}},"maxItems":200,"description":"factId is unique."},"branches":{"type":"array","items":{"type":"object","additionalProperties":true,"required":["branchId","fromNodeId","condition","toNodeId"],"properties":{"branchId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Lowercase machine id; dots allowed for namespacing (fact.ferry)."},"fromNodeId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Must be a declared node."},"toNodeId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Must be a declared node."},"condition":{"type":"string","maxLength":500,"description":"Descriptive text in v1 — the runtime does not evaluate it. It is what a turn offers as a next option."}}},"maxItems":200,"description":"branchId is unique."},"endings":{"type":"array","items":{"type":"object","additionalProperties":true,"required":["endingId","title","condition"],"properties":{"endingId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Lowercase machine id; dots allowed for namespacing (fact.ferry)."},"title":{"type":"string","maxLength":120,"description":""},"condition":{"type":"string","maxLength":500,"description":""}}},"maxItems":60,"description":"endingId is unique."},"stateSchema":{"type":"object","additionalProperties":true,"description":"The declared world-state flags. Absent means a package with no flags.","properties":{"flags":{"type":"array","items":{"type":"object","additionalProperties":true,"required":["key","kind"],"properties":{"key":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Lowercase machine id; dots allowed for namespacing (fact.ferry)."},"kind":{"type":"string","enum":["boolean","number","string"]},"initial":{"description":"Optional starting value. Its JSON type must match `kind` exactly — a number initial on a string flag is refused."},"public":{"type":"boolean","enum":[true],"description":"Contract v4. `true` publishes this flag — its NAME and its VALUE — on the progress checkpoint’s `publicFlags` for any client holding the instance. Absent keeps it out of the checkpoint (a flag’s name can itself be a spoiler). `false` is refused: the only two states are published and not. Declaring any public flag is what makes a package contract v4; the server decides the version and refuses a client-supplied `contractVersion`."},"range":{"type":"object","required":["min","max"],"properties":{"min":{"type":"number"},"max":{"type":"number"}},"description":"PRESENTATION ONLY — the full scale this number runs on, so a client can draw a meter instead of printing a bare integer. Valid only on `kind: \"number\"`; refused on the others, because `{min:0,max:1}` on a boolean reads as \"it can be 0.5\". `min` must be STRICTLY less than `max`: equal bounds divide by zero in every client that draws a meter. Absent means the author did not say, and a client must not invent one — assuming /100 renders a 0–10 score as \"6% of the way there\". Nothing keys off this: not the coordinator, not ending adjudication, not the ledger, and it is NOT a contract version (an older reader ignoring it prints the number without a meter, which is what every client did before it existed)."},"label":{"type":"string","description":"PRESENTATION ONLY — the human name for this flag. Absent means a client derives one from `key` (`trust_amara` → `TRUST · AMARA`). It exists because `key` also has to serve as the identifier a rule condition names, and the two jobs have conflicting naming constraints. NEVER translated by a client: these strings are authored inside the package, in the language of the work. Absent or a non-empty string — `\"\"` is refused, because an empty label cannot be told from an unfilled form field and one meaning with two byte spellings is what `public` already refuses."}}},"maxItems":50,"description":"key is unique."}}},"effectRules":{"type":"object","additionalProperties":true,"required":["conflict"],"description":"How the coordinator settles TWO ROLES proposing incompatible effects on the SAME target in one turn. Absent means `reject`. There is deliberately no \"the model decides\" option. It does NOT govern the caller: a `trigger.proposedPatches` op always beats a role effect on the same axis, whatever this says (world API 1.46.0) — see that field. Making that author-configurable would make the deterministic entrance conditional on an author’s dramatic taste, which is the one property it exists to have.","properties":{"conflict":{"type":"string","enum":["reject","first_wins"]}}},"initialRelations":{"type":"array","maxItems":50,"description":"Contract v2. The relations the story opens with, seeded into a NEW world instance only. A SET, not a list: [a,b] and [b,a] are one relation, a pair declared twice is REFUSED rather than merged (differing descriptors are a contradiction; identical ones are still two rows for one key), and the whole array is sorted before hashing — so authoring order cannot change the content hash. Every other array in this contract keeps declaration order. EVERY relation here is PUBLIC: relations live in the shared block of world state, so a relation that must stay hidden belongs in the authorised role’s `secrets` instead. Supplying this field is what makes a package contract v2; the server decides the version and refuses a client-supplied `contractVersion`.","items":{"type":"object","additionalProperties":true,"required":["between","descriptor"],"properties":{"between":{"type":"array","minItems":2,"maxItems":2,"items":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{1,63}$","description":"Lowercase machine id, 2–64 chars."},"description":"Two DISTINCT declared storyRoleIds. Normalized to ascending order."},"descriptor":{"type":"string","maxLength":120}}}},"series":{"type":"object","additionalProperties":true,"required":["seriesId","title","episodes"],"description":"Contract v3. Episodes, and the deterministic rules that decide when one is over. Supplying this field is what makes a package contract v3; the server decides the version and refuses a client-supplied `contractVersion`. Every id a rule names — node, fact, scene, flag, ending — must be DECLARED in the same package: a rule that can never hold would make the episode end only by fallback, silently. Ending rules are evaluated SERVER-SIDE over committed state after a beat commits; no model, client or prompt supplies an endingId.","properties":{"seriesId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Lowercase machine id; dots allowed for namespacing (fact.ferry)."},"title":{"type":"string","maxLength":120},"episodes":{"type":"array","maxItems":24,"description":"DECLARATION order is broadcast order. At least one; there is no empty-series form, because absent already means \"not a serial\".","items":{"type":"object","additionalProperties":true,"required":["episodeId","title","turnBudget","fallbackEndingId"],"properties":{"episodeId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Lowercase machine id; dots allowed for namespacing (fact.ferry)."},"title":{"type":"string","maxLength":120},"turnBudget":{"type":"integer","minimum":1,"maximum":200,"description":"Committed beats this episode may run before the fallback ending fires. Capped at the evidence window: an episode longer than one evidence read can never be turned into a script."},"fallbackEndingId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"A DECLARED ending, used when no rule ever holds. Required — the alternative is an episode that cannot end."},"endingRules":{"type":"array","maxItems":20,"description":"Order-independent: priority decides (lower wins) and `ruleId` breaks a tie. Reordering this array changes nothing, unlike every other array in this contract.","items":{"type":"object","additionalProperties":true,"required":["ruleId","endingId","priority","all"],"properties":{"ruleId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Lowercase machine id; dots allowed for namespacing (fact.ferry)."},"endingId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Lowercase machine id; dots allowed for namespacing (fact.ferry)."},"priority":{"type":"integer"},"all":{"type":"array","minItems":1,"maxItems":10,"description":"A CONJUNCTION over a closed condition vocabulary — `node_completed`, `fact_revealed`, `flag_equals`, `scene_is`, `clock_at_least`. An OR is two rules. An empty list would be true of every state and is refused.","items":{"type":"object","additionalProperties":true}}}}}}}}}},"visibility":{"type":"object","additionalProperties":true,"required":["factDefault"],"description":"Role visibility over established facts and (Story Contract v5, world API 1.55.0) story flags. Canon is world-shared by definition and role `secrets` are private by definition; facts and flags sit between. Absent means { factDefault: \"shared\" } and every role sees every flag.","properties":{"factDefault":{"type":"string","enum":["shared","role-scoped"]},"factAllow":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Lowercase machine id; dots allowed for namespacing (fact.ferry)."}},"description":"worldRoleId → factId[]. Every key must be a declared worldRoleId and every value a declared factId; both are refused by name rather than ignored."},"flagDefault":{"type":"string","enum":["shared","role-scoped"],"description":"Story Contract v5 (world API 1.55.0). `role-scoped`: a role sees a flag only when `flagAllow` names it for that role, or the flag is `public` (public is world-public — every client and every role). Hidden means hidden wherever a ROLE reads: its envelope (key and value), its call brief, a deliberation, and its effect brief; a role `set_flag` / `increment_flag` on a flag it cannot see is refused exactly like an undeclared flag (`schema_rejection`). Everything that is not a role sees every flag: your `proposedPatches` may write a hidden flag, ending rules read it, and `getWorldState` returns it. A flag in no role’s list is hidden from every role. Absent or `shared` is the behaviour before v5 (and `shared` is dropped, so it never changes the package’s bytes or version). Using `role-scoped` makes the package contract v5; an environment that has not enabled v5 writes answers 409 `story_contract_v5_write_disabled` with the field intact, and a v5 package cannot be read by a pre-1.55.0 server. The switch is DEPLOYMENT-wide — it covers every project at once and is never set per project; `GET https://pouchy.ai/api/version` (public, no auth) reports whether the serving deployment writes v5 as `storyContractWrites.v5`, so check that before publishing rather than learning it from a 409."},"flagAllow":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Lowercase machine id; dots allowed for namespacing (fact.ferry)."}},"description":"Story Contract v5. worldRoleId → flag keys, only with `flagDefault: \"role-scoped\"` (refused otherwise, never ignored). Every key must be a declared worldRoleId and every value a declared flag; a `public` flag may not be listed."}}}}},"contentHash":{"type":"string"}},"required":["head","content","contentHash"]}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}},"patch":{"summary":"Publish the next IMMUTABLE story revision. Idempotent on content: unchanged bytes return the existing revision (created: false); different bytes mint the next one and never rewrite an old one. Re-publishing an old revision’s bytes IS the rollback.","security":[{"OwnerToken":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"required":["name","summary","roles"],"description":"A Story Package v1 body. Array ORDER is narrative order and therefore content — reordering roles or scenes mints a different revision. Referential and uniqueness rules are enforced by the server and named in the field descriptions below. SIZE: besides each list's maxItems, the whole package is at most 900000 bytes, measured as the UTF-8 JSON of the normalized content (a published revision is one stored document; CJK text is 3 bytes a character). Over it is a 400 naming the measured size.","x-pouchy-max-bytes":900000,"properties":{"name":{"type":"string","maxLength":80,"description":"Required."},"summary":{"type":"string","maxLength":1000,"description":"Required."},"source":{"type":"object","additionalProperties":true,"required":["title","version"],"description":"A REFERENCE to the original screenplay — never its bytes. Both fields are required when `source` is present at all.","properties":{"title":{"type":"string","maxLength":200,"description":""},"version":{"type":"string","maxLength":60,"description":""}}},"canon":{"type":"array","items":{"type":"string","maxLength":500,"description":""},"maxItems":100,"description":"World-shared truth every role sees. Entries are trimmed; empty strings are refused."},"constraints":{"type":"array","items":{"type":"string","maxLength":500,"description":""},"maxItems":50,"description":"What the story must never do. Same entry rules as canon."},"roles":{"type":"array","items":{"type":"object","additionalProperties":true,"required":["storyRoleId","worldRoleId"],"properties":{"storyRoleId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{1,63}$","description":"Lowercase machine id, 2–64 chars."},"worldRoleId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{1,63}$","description":"The WORLD role this story role plays. Must exist in the world revision that pins this package — checked at PIN time, not here, because a package is project-scoped and may be pinned by many worlds. At most one story role per worldRoleId."},"description":{"type":"string","maxLength":500,"description":"Authoring metadata and a dashboard label. It reaches NO prompt at runtime, not even this role’s own — a role’s persona is the bound agent’s `systemPrompt`."},"secrets":{"type":"array","items":{"type":"string","maxLength":500,"description":""},"maxItems":20,"description":"Role-PRIVATE canon. Never rendered to another role, in any projection."},"goals":{"type":"array","items":{"type":"string","maxLength":500,"description":""},"maxItems":20,"description":"Rendered into THIS role’s own per-beat envelope, and nowhere else — never into another role’s. It does NOT drive role selection: selection is declaration order intersected with bound actors and with focusRoles / event subscriptions, minus roles that have left the worldline (1.53.0), capped at 3, and reads no goals at all. (The previous wording said otherwise and described a design that was never built.)"},"effectsAllow":{"type":"array","maxItems":8,"items":{"type":"string","enum":["set_flag","increment_flag","reveal_fact","update_relationship","update_entity","advance_clock","request_scene_transition","complete_objective"]},"description":"What this role may PROPOSE. DEFAULT DENY: absent means the role proposes nothing and the effect tool is not offered to it at all. An unknown kind is an error, never a silent drop."}}},"maxItems":24,"description":"At least one role is required. storyRoleId is unique; worldRoleId is bound at most once."},"scenes":{"type":"array","items":{"type":"object","additionalProperties":true,"required":["sceneId","title"],"properties":{"sceneId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Lowercase machine id; dots allowed for namespacing (fact.ferry)."},"title":{"type":"string","maxLength":120,"description":"Required."},"description":{"type":"string","maxLength":500,"description":""},"location":{"type":"string","maxLength":120,"description":""}}},"maxItems":100,"description":"sceneId is unique within the package."},"nodes":{"type":"array","items":{"type":"object","additionalProperties":true,"required":["nodeId","sceneId","title","objective"],"properties":{"nodeId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Lowercase machine id; dots allowed for namespacing (fact.ferry)."},"sceneId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Must name a scene declared in `scenes`."},"title":{"type":"string","maxLength":120,"description":""},"objective":{"type":"string","maxLength":500,"description":""},"prerequisites":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Lowercase machine id; dots allowed for namespacing (fact.ferry)."},"maxItems":10,"description":"Node ids that must be completed first. Each must be a declared node, and never this node itself."}}},"maxItems":200,"description":"A plot node is an objective inside a scene. nodeId is unique."},"establishedFacts":{"type":"array","items":{"type":"object","additionalProperties":true,"required":["factId","text"],"properties":{"factId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Lowercase machine id; dots allowed for namespacing (fact.ferry)."},"text":{"type":"string","maxLength":500,"description":""}}},"maxItems":200,"description":"factId is unique."},"branches":{"type":"array","items":{"type":"object","additionalProperties":true,"required":["branchId","fromNodeId","condition","toNodeId"],"properties":{"branchId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Lowercase machine id; dots allowed for namespacing (fact.ferry)."},"fromNodeId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Must be a declared node."},"toNodeId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Must be a declared node."},"condition":{"type":"string","maxLength":500,"description":"Descriptive text in v1 — the runtime does not evaluate it. It is what a turn offers as a next option."}}},"maxItems":200,"description":"branchId is unique."},"endings":{"type":"array","items":{"type":"object","additionalProperties":true,"required":["endingId","title","condition"],"properties":{"endingId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Lowercase machine id; dots allowed for namespacing (fact.ferry)."},"title":{"type":"string","maxLength":120,"description":""},"condition":{"type":"string","maxLength":500,"description":""}}},"maxItems":60,"description":"endingId is unique."},"stateSchema":{"type":"object","additionalProperties":true,"description":"The declared world-state flags. Absent means a package with no flags.","properties":{"flags":{"type":"array","items":{"type":"object","additionalProperties":true,"required":["key","kind"],"properties":{"key":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Lowercase machine id; dots allowed for namespacing (fact.ferry)."},"kind":{"type":"string","enum":["boolean","number","string"]},"initial":{"description":"Optional starting value. Its JSON type must match `kind` exactly — a number initial on a string flag is refused."},"public":{"type":"boolean","enum":[true],"description":"Contract v4. `true` publishes this flag — its NAME and its VALUE — on the progress checkpoint’s `publicFlags` for any client holding the instance. Absent keeps it out of the checkpoint (a flag’s name can itself be a spoiler). `false` is refused: the only two states are published and not. Declaring any public flag is what makes a package contract v4; the server decides the version and refuses a client-supplied `contractVersion`."},"range":{"type":"object","required":["min","max"],"properties":{"min":{"type":"number"},"max":{"type":"number"}},"description":"PRESENTATION ONLY — the full scale this number runs on, so a client can draw a meter instead of printing a bare integer. Valid only on `kind: \"number\"`; refused on the others, because `{min:0,max:1}` on a boolean reads as \"it can be 0.5\". `min` must be STRICTLY less than `max`: equal bounds divide by zero in every client that draws a meter. Absent means the author did not say, and a client must not invent one — assuming /100 renders a 0–10 score as \"6% of the way there\". Nothing keys off this: not the coordinator, not ending adjudication, not the ledger, and it is NOT a contract version (an older reader ignoring it prints the number without a meter, which is what every client did before it existed)."},"label":{"type":"string","description":"PRESENTATION ONLY — the human name for this flag. Absent means a client derives one from `key` (`trust_amara` → `TRUST · AMARA`). It exists because `key` also has to serve as the identifier a rule condition names, and the two jobs have conflicting naming constraints. NEVER translated by a client: these strings are authored inside the package, in the language of the work. Absent or a non-empty string — `\"\"` is refused, because an empty label cannot be told from an unfilled form field and one meaning with two byte spellings is what `public` already refuses."}}},"maxItems":50,"description":"key is unique."}}},"effectRules":{"type":"object","additionalProperties":true,"required":["conflict"],"description":"How the coordinator settles TWO ROLES proposing incompatible effects on the SAME target in one turn. Absent means `reject`. There is deliberately no \"the model decides\" option. It does NOT govern the caller: a `trigger.proposedPatches` op always beats a role effect on the same axis, whatever this says (world API 1.46.0) — see that field. Making that author-configurable would make the deterministic entrance conditional on an author’s dramatic taste, which is the one property it exists to have.","properties":{"conflict":{"type":"string","enum":["reject","first_wins"]}}},"initialRelations":{"type":"array","maxItems":50,"description":"Contract v2. The relations the story opens with, seeded into a NEW world instance only. A SET, not a list: [a,b] and [b,a] are one relation, a pair declared twice is REFUSED rather than merged (differing descriptors are a contradiction; identical ones are still two rows for one key), and the whole array is sorted before hashing — so authoring order cannot change the content hash. Every other array in this contract keeps declaration order. EVERY relation here is PUBLIC: relations live in the shared block of world state, so a relation that must stay hidden belongs in the authorised role’s `secrets` instead. Supplying this field is what makes a package contract v2; the server decides the version and refuses a client-supplied `contractVersion`.","items":{"type":"object","additionalProperties":true,"required":["between","descriptor"],"properties":{"between":{"type":"array","minItems":2,"maxItems":2,"items":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{1,63}$","description":"Lowercase machine id, 2–64 chars."},"description":"Two DISTINCT declared storyRoleIds. Normalized to ascending order."},"descriptor":{"type":"string","maxLength":120}}}},"series":{"type":"object","additionalProperties":true,"required":["seriesId","title","episodes"],"description":"Contract v3. Episodes, and the deterministic rules that decide when one is over. Supplying this field is what makes a package contract v3; the server decides the version and refuses a client-supplied `contractVersion`. Every id a rule names — node, fact, scene, flag, ending — must be DECLARED in the same package: a rule that can never hold would make the episode end only by fallback, silently. Ending rules are evaluated SERVER-SIDE over committed state after a beat commits; no model, client or prompt supplies an endingId.","properties":{"seriesId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Lowercase machine id; dots allowed for namespacing (fact.ferry)."},"title":{"type":"string","maxLength":120},"episodes":{"type":"array","maxItems":24,"description":"DECLARATION order is broadcast order. At least one; there is no empty-series form, because absent already means \"not a serial\".","items":{"type":"object","additionalProperties":true,"required":["episodeId","title","turnBudget","fallbackEndingId"],"properties":{"episodeId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Lowercase machine id; dots allowed for namespacing (fact.ferry)."},"title":{"type":"string","maxLength":120},"turnBudget":{"type":"integer","minimum":1,"maximum":200,"description":"Committed beats this episode may run before the fallback ending fires. Capped at the evidence window: an episode longer than one evidence read can never be turned into a script."},"fallbackEndingId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"A DECLARED ending, used when no rule ever holds. Required — the alternative is an episode that cannot end."},"endingRules":{"type":"array","maxItems":20,"description":"Order-independent: priority decides (lower wins) and `ruleId` breaks a tie. Reordering this array changes nothing, unlike every other array in this contract.","items":{"type":"object","additionalProperties":true,"required":["ruleId","endingId","priority","all"],"properties":{"ruleId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Lowercase machine id; dots allowed for namespacing (fact.ferry)."},"endingId":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Lowercase machine id; dots allowed for namespacing (fact.ferry)."},"priority":{"type":"integer"},"all":{"type":"array","minItems":1,"maxItems":10,"description":"A CONJUNCTION over a closed condition vocabulary — `node_completed`, `fact_revealed`, `flag_equals`, `scene_is`, `clock_at_least`. An OR is two rules. An empty list would be true of every state and is refused.","items":{"type":"object","additionalProperties":true}}}}}}}}}},"visibility":{"type":"object","additionalProperties":true,"required":["factDefault"],"description":"Role visibility over established facts and (Story Contract v5, world API 1.55.0) story flags. Canon is world-shared by definition and role `secrets` are private by definition; facts and flags sit between. Absent means { factDefault: \"shared\" } and every role sees every flag.","properties":{"factDefault":{"type":"string","enum":["shared","role-scoped"]},"factAllow":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Lowercase machine id; dots allowed for namespacing (fact.ferry)."}},"description":"worldRoleId → factId[]. Every key must be a declared worldRoleId and every value a declared factId; both are refused by name rather than ignored."},"flagDefault":{"type":"string","enum":["shared","role-scoped"],"description":"Story Contract v5 (world API 1.55.0). `role-scoped`: a role sees a flag only when `flagAllow` names it for that role, or the flag is `public` (public is world-public — every client and every role). Hidden means hidden wherever a ROLE reads: its envelope (key and value), its call brief, a deliberation, and its effect brief; a role `set_flag` / `increment_flag` on a flag it cannot see is refused exactly like an undeclared flag (`schema_rejection`). Everything that is not a role sees every flag: your `proposedPatches` may write a hidden flag, ending rules read it, and `getWorldState` returns it. A flag in no role’s list is hidden from every role. Absent or `shared` is the behaviour before v5 (and `shared` is dropped, so it never changes the package’s bytes or version). Using `role-scoped` makes the package contract v5; an environment that has not enabled v5 writes answers 409 `story_contract_v5_write_disabled` with the field intact, and a v5 package cannot be read by a pre-1.55.0 server. The switch is DEPLOYMENT-wide — it covers every project at once and is never set per project; `GET https://pouchy.ai/api/version` (public, no auth) reports whether the serving deployment writes v5 as `storyContractWrites.v5`, so check that before publishing rather than learning it from a 409."},"flagAllow":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{1,63}$","description":"Lowercase machine id; dots allowed for namespacing (fact.ferry)."}},"description":"Story Contract v5. worldRoleId → flag keys, only with `flagDefault: \"role-scoped\"` (refused otherwise, never ignored). Every key must be a declared worldRoleId and every value a declared flag; a `public` flag may not be listed."}}}}}}}},"responses":{"200":{"description":"Content unchanged","content":{"application/json":{"schema":{"type":"object","properties":{"packageId":{"type":"string"},"revision":{"type":"integer"},"contentHash":{"type":"string"},"created":{"type":"boolean"}}}}}},"201":{"description":"New revision","content":{"application/json":{"schema":{"type":"object","properties":{"packageId":{"type":"string"},"revision":{"type":"integer"},"contentHash":{"type":"string"},"created":{"type":"boolean"}}}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}},"delete":{"summary":"Delete a story package NOTHING HAS EVER PINNED, taking its revisions with it. Two distinct 409s. `has_pins` — with `pinnedRevisions` and `lastPinnedBy` — means an environment revision pins it: NEVER pinned rather than not pinned right now, because a world instance stays pinned to the environment revision it started on and the runtime re-reads the story package on every beat, so a package referenced only by a superseded revision is still live content and deleting it would answer `gone` mid-story. `pins_unknown` means the package predates the pin stamp, so the answer is not known and is not guessed. Either way, disable it instead: a disabled story package no longer counts against the per-project ceiling, so the slot comes back. Owner plane only, and deliberately not on the /admin mirror — the mirror carries reads, not this.","security":[{"OwnerToken":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean"}}}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/projects/{projectId}/story-packages/import-candidate":{"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"},"description":"The project id."}],"post":{"summary":"Read a dramatic script and PROPOSE a Story Package candidate. Writes nothing: no package, no world, no agent, and the script itself is never stored. One bounded model call whose output is validated by the same total validator a hand-written package faces — a candidate that does not validate is returned WITH its errors rather than repaired. Confirming a candidate is a separate human act through the normal publish route. `relationsWritable` reports whether THIS environment may publish contract v2 (relations); relations are always PUBLIC shared state.","security":[{"OwnerToken":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"script":{"type":"string","maxLength":16000},"model":{"type":"string"}},"required":["script"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"candidate":{"$ref":"#/components/schemas/StoryPackageContent"},"nodesFound":{"type":"integer"},"relationsWritable":{"type":"boolean"},"relationsArePublic":{"type":"boolean"}}}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/projects/{projectId}/story-packages/import-candidate/validate":{"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"},"description":"The project id."}],"post":{"summary":"Re-run the FULL Story Package validator over an edited candidate. No model call, no storage, no writes. Answers 200 in BOTH directions — `valid: true` with the NORMALIZED candidate (relation pairs sorted, an empty relation list dropped) so a wizard shows what will actually be published, or `valid: false` with `errors[]`. An invalid candidate is the answer to the question, not a failed request.","security":[{"OwnerToken":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"candidate":{"$ref":"#/components/schemas/StoryPackageContent"}},"required":["candidate"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"valid":{"type":"boolean"},"errors":{"type":"array","items":{"type":"string"}},"candidate":{"$ref":"#/components/schemas/StoryPackageContent"},"usesRelations":{"type":"boolean"},"relationsWritable":{"type":"boolean"},"relationsArePublic":{"type":"boolean"}}}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/projects/{projectId}/story-packages/{packageId}/versions":{"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"},"description":"The project id."},{"name":"packageId","in":"path","required":true,"schema":{"type":"string"},"description":"The story package id."}],"get":{"summary":"The immutable revision list for one package. `versions` holds only the revisions THIS deployment can read: a revision stamped with a story contract this build does not support is withheld and counted in `unsupportedCount` instead. Read `latestRevision` — the head’s own — rather than treating `versions[0]` as the newest revision.","security":[{"OwnerToken":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"versions":{"type":"array","items":{"type":"object","additionalProperties":true}},"unsupported":{"type":"array","items":{"type":"object","properties":{"revision":{"type":"integer"},"contractVersion":{"type":["integer","null"]}}}},"unsupportedCount":{"type":"integer"},"hasUnsupported":{"type":"boolean"},"latestRevision":{"type":["integer","null"]}}}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/projects/{projectId}/story-packages/{packageId}/disable":{"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"},"description":"The project id."},{"name":"packageId","in":"path","required":true,"schema":{"type":"string"},"description":"The story package id."}],"post":{"summary":"Disable or re-enable a package. A disabled package stops resolving for every world that pins it — the pinned turn refuses rather than running a story nobody stands behind.","security":[{"OwnerToken":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"disabled":{"type":"boolean"}},"required":["disabled"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"packageId":{"type":"string"},"disabled":{"type":"boolean"}}}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/projects/{projectId}/environments":{"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"},"description":"The project id."}],"get":{"summary":"List worlds (environments) in this project.","security":[{"OwnerToken":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"environments":{"type":"array","items":{"type":"object","additionalProperties":true}}}}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}},"post":{"summary":"Create a world as revision 1. Roles bind agents (one agent per role); capabilityAllow is default-deny and roles may only NARROW it; runtimeMode picks the runtime EXPLICITLY (omitted or \"actor\" = the per-role event wake; \"coordinated\" = the multi-role turn coordinator, which also needs a storyPackageRef pin).","security":[{"OwnerToken":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"rulesText":{"type":"string"},"providerRef":{"type":"string"},"runtimeMode":{"type":"string","enum":["actor","coordinated"]},"storyPackageRef":{"type":"object","properties":{"packageId":{"type":"string"},"revision":{"type":"integer"},"contentHash":{"type":"string"}}},"capabilityAllow":{"type":"object","properties":{"views":{"type":"array","items":{"type":"string"}},"actions":{"type":"array","items":{"type":"string"}},"events":{"type":"array","items":{"type":"string"}}}},"roles":{"type":"array","items":{"type":"object","additionalProperties":true},"maxItems":24,"description":"At most 24 roles (world API 1.48.0; 12 before), each bound to a DISTINCT agent. At most 5 roles may subscribe to the same event. However large the cast, at most 3 roles speak in one beat — pass trigger.focusRoles to choose which."}},"required":["name","roles"]}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"environmentId":{"type":"string"},"environmentRevision":{"type":"integer"},"contentHash":{"type":"string"}}}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/projects/{projectId}/environments/{envId}":{"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"},"description":"The project id."},{"name":"envId","in":"path","required":true,"schema":{"type":"string"},"description":"The world (environment) id — env_…."}],"get":{"summary":"Head plus the current world definition.","security":[{"OwnerToken":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}},"patch":{"summary":"Publish the next world revision. The body is a COMPLETE world definition — this is a whole replacement, never a partial update, so a body without `name` and `roles` is refused as a malformed definition rather than applied as a patch (enable/disable lives on POST …/{envId}/disable). Existing world INSTANCES keep the revision they were created on — a published change reaches new instances only, which is what stops a running story from changing its rules or its runtime underneath its players.","security":[{"OwnerToken":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"responses":{"200":{"description":"Content unchanged","content":{"application/json":{"schema":{"type":"object","properties":{"environmentId":{"type":"string"},"environmentRevision":{"type":"integer"},"contentHash":{"type":"string"},"created":{"type":"boolean"}}}}}},"201":{"description":"New revision","content":{"application/json":{"schema":{"type":"object","properties":{"environmentId":{"type":"string"},"environmentRevision":{"type":"integer"},"contentHash":{"type":"string"},"created":{"type":"boolean"}}}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}},"delete":{"summary":"Delete an experience that has NEVER BEEN RUN. Refused with 409 `has_instances` — and the instance count — the moment any instance exists: a membership parents the story ledger, and the rows under it are the record that a beat happened, with evidence drafts, editorial drafts and approved exports among their descendants. Deleting an experience that HAS run would cascade through reviewed, signed-off content, which is a decision this endpoint does not make for you. Disable it instead: a disabled experience no longer counts against the per-project ceiling, so the slot comes back either way. Owner plane only, and deliberately not on the /admin mirror — the mirror carries reads, not the most destructive write on this plane.","security":[{"OwnerToken":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean"}}}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/projects/{projectId}/environments/{envId}/cost":{"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"},"description":"The project id."},{"name":"envId","in":"path","required":true,"schema":{"type":"string"},"description":"The world (environment) id — env_…."},{"name":"months","in":"query","required":false,"schema":{"type":"integer"},"description":"Window length; clamped server-side."}],"get":{"summary":"What this experience costs: tokens and model hops per month. Read from a per-experience view written in the SAME merge as the project totals — one write, two views, never a second charge. Keyed by ENVIRONMENT, not by instance: a month document has a hard size ceiling and a project has one instance per player, so per-player cost is a separate question this does not answer. Months that could not be read are reported rather than counted as zero spend.","security":[{"OwnerToken":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/projects/{projectId}/environments/{envId}/preflight":{"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"},"description":"The project id."},{"name":"envId","in":"path","required":true,"schema":{"type":"string"},"description":"The world (environment) id — env_…."}],"get":{"summary":"WHY the signed doors are refusing you. Both doors answer one uniform 403 on a bad signature so the wire cannot tell an attacker which credential to keep guessing at; this returns the reason out of the project’s own audit trail — which door, which code, when. Reasons are a closed vocabulary (missing | malformed | unknown_key | stale | bad_signature | no_keys) and the rows carry no key material. OWNER plane only, and deliberately not on the /admin mirror: a machine credential reading a stream of \"which credential is wrong\" is a different risk from one reading world state.","security":[{"OwnerToken":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/projects/{projectId}/environments/{envId}/versions":{"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"},"description":"The project id."},{"name":"envId","in":"path","required":true,"schema":{"type":"string"},"description":"The world (environment) id — env_…."}],"get":{"summary":"The immutable revision list for one world.","security":[{"OwnerToken":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"versions":{"type":"array","items":{"type":"object","additionalProperties":true}}}}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/projects/{projectId}/environments/{envId}/disable":{"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"},"description":"The project id."},{"name":"envId","in":"path","required":true,"schema":{"type":"string"},"description":"The world (environment) id — env_…."}],"post":{"summary":"Disable or re-enable a world. A disabled world stops every session, turn and event at the next hop — the live gate, not a cached flag.","security":[{"OwnerToken":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"disabled":{"type":"boolean"}},"required":["disabled"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"environmentId":{"type":"string"},"disabled":{"type":"boolean"}}}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/projects/{projectId}/environments/{envId}/instances/{worldInstanceId}/episodes/next":{"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"},"description":"The project id."},{"name":"envId","in":"path","required":true,"schema":{"type":"string"},"description":"The world (environment) id — env_…."},{"name":"worldInstanceId","in":"path","required":true,"schema":{"type":"string"},"description":"The world instance id."}],"post":{"summary":"Start the NEXT episode of a worldline that just finished one. The worldline does not restart: its instance id, membership, state and ledger all continue, and this moves the `episode` field on — nothing is copied and nothing is deleted, so there is no migration to get wrong. The response carries the CARRYOVER: a projection of what the next episode gets to stand on (canon the last episode established, where the characters stand, the threads it left hanging, the setups it never paid off — see `WorldCarryoverV1` for which of those a reader may see; `unfulfilledSetups` is unfiltered by fact visibility and is spoilers by definition). Role-private notes are never in it, and no memory is carried because the world runtime writes none. Idempotent — the run id is derived from the worldline, the episode and the revision it starts at, so a retry lands on the same run and answers `created: false`. TWO lanes since 1.30.0: a signed-in project admin (owner lane, empty body), or the same two proofs as the turn door — a Secret Key AND a provider signature over the exact raw body, id slot `epn:{requestId}` with `requestId` in the body — so an unattended serial backend can advance its own player. Still no /admin mirror: an admin key proves the project, never the Provider. Refuses with `episode_not_ended` (the current episode is still running), `series_complete` (there is no episode after this one) or `not_a_serial`.","security":[{"OwnerToken":[]},{"SecretKey":[],"SourceSignature":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"requestId":{"type":"string","description":"Machine lane only: the signature id slot is `epn:{requestId}`. The owner lane sends an empty body. Not an idempotency key — the run id is derived server-side either way."}}}}}},"responses":{"200":{"description":"Already started — same run","content":{"application/json":{"schema":{"type":"object","properties":{"created":{"type":"boolean"},"episode":{"type":"object","additionalProperties":true},"carryover":{"$ref":"#/components/schemas/WorldCarryoverV1"}}}}}},"201":{"description":"Next episode started","content":{"application/json":{"schema":{"type":"object","properties":{"created":{"type":"boolean"},"episode":{"type":"object","additionalProperties":true},"carryover":{"$ref":"#/components/schemas/WorldCarryoverV1"}}}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/projects/{projectId}/environments/{envId}/instances/{worldInstanceId}/deliberations":{"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"},"description":"The project id."},{"name":"envId","in":"path","required":true,"schema":{"type":"string"},"description":"The world id."},{"name":"worldInstanceId","in":"path","required":true,"schema":{"type":"string"},"description":"The world instance id."}],"post":{"summary":"Preview where a beat could go WITHOUT taking it. Returns short, PUBLIC directions plus a signed envelope; selecting one commits it through the ordinary turn door. Writes nothing — no world state, no ledger, no session, no outbox — and the candidates are not stored anywhere: they live in the envelope the caller holds. OFF unless the pinned world revision declares `deliberation`, and coordinated-mode only. Machine lane, with its own signature id namespace (`dlb:{requestId}`) so a deliberation signature can never be replayed as a turn or an event.","security":[{"SecretKey":[],"SourceSignature":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"requestId":{"type":"string","maxLength":128,"description":"Signed in the `dlb:` id slot; also fixes the turn id a later select commits under."},"text":{"type":"string","maxLength":4000},"model":{"type":"string"}},"required":["requestId","text"]}}}},"responses":{"201":{"description":"Candidates and a signed envelope","content":{"application/json":{"schema":{"type":"object","properties":{"candidates":{"type":"array","description":"The WHOLE of what a user may see. No reasoning, no per-role output, no simulated effects, no scores, no raw model response.","items":{"type":"object","properties":{"candidateId":{"type":"string"},"title":{"type":"string"},"direction":{"type":"string"},"shortTeaser":{"type":"string"},"participatingRoles":{"type":"array","items":{"type":"string"}}}}},"envelope":{"type":"string"},"expiresAt":{"type":"integer"},"budgetSpent":{"type":"object","properties":{"modelCalls":{"type":"integer"},"tokens":{"type":"integer"},"ms":{"type":"integer"}}},"stop":{"type":"string","enum":["complete","candidate_cap","model_call_cap","token_cap","time_cap","no_roles"],"description":"Why it stopped. A run that hit a ceiling says so rather than presenting a short list as a complete one."}}}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/projects/{projectId}/environments/{envId}/instances/{worldInstanceId}/deliberations/select":{"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"},"description":"The project id."},{"name":"envId","in":"path","required":true,"schema":{"type":"string"},"description":"The world id."},{"name":"worldInstanceId","in":"path","required":true,"schema":{"type":"string"},"description":"The world instance id."}],"post":{"summary":"Commit the direction a player chose. This IS an ordinary beat — same coordinator, same ledger, same WorldTurnResult. A deliberation has no privileges: its simulated effects are never carried here, roles re-propose their own, and the only thing that crosses is `direction`, a narrative steer. `direction` is caller-supplied — the field on this request, at most 400 characters — and is NOT bound to the chosen candidate by the server: the envelope carries a `candidateDigest` over the candidates, but this operation does not re-prove it, so hold the direction to one of the deliberation candidates on your own backend before calling. Every claim in the envelope is re-proven against live reads BEFORE any model call — membership, revision, story pin, stateRevision, provider and actor binding, and the live gate — so a world that moved answers 409 `stale_deliberation` having spent and written nothing. The turn id is fixed inside the envelope, so re-sending the same envelope is a duplicate rather than a second beat.","security":[{"SecretKey":[],"SourceSignature":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"envelope":{"type":"string","description":"Opaque. Hand back exactly what the deliberation returned."},"candidateId":{"type":"string"},"direction":{"type":"string","maxLength":400},"text":{"type":"string","maxLength":4000,"description":"The same input the deliberation was about — compared against the envelope’s digest."}},"required":["envelope","candidateId","direction","text"]}}}},"responses":{"200":{"description":"The committed beat","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/projects/{projectId}/environments/{envId}/instances/{worldInstanceId}/turns":{"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"},"description":"The project id."},{"name":"envId","in":"path","required":true,"schema":{"type":"string"},"description":"The world (environment) id — env_…."},{"name":"worldInstanceId","in":"path","required":true,"schema":{"type":"string"},"description":"The world instance — one user’s story."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer"},"description":"How many beats to return. Capped server-side."},{"name":"sinceSeq","in":"query","required":false,"schema":{"type":"integer"},"description":"Walk FORWARD from a revision already processed: returns beats with seq > sinceSeq, OLDEST first, plus `nextSinceSeq` to continue with. This is the resume cursor — without it the response is the newest-first window, which cannot be paged past its cap. The response names its own `order` either way."}],"get":{"summary":"The committed TIMELINE — read-only, owner-token. Projects the ledger into a beat-per-turn shape: who acted, which roles spoke and what they said, what the world changed, which effects were accepted or refused (with the taxonomy code), and the revision each beat committed at. Pass `sinceSeq` to resume forward from a known revision. Delivered narrative only; the ledger never held a prompt, a tool trace, or the private role layer.","security":[{"OwnerToken":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}},"post":{"summary":"Drive ONE coordinated beat: server-side role selection, per-role perspective, typed effect proposals settled by the coordinator, one commit, then delivery. turnId is the identity — re-send it to retry (it re-runs no model and commits nothing twice), mint a new one for a new beat. The \"evt:\" prefix is reserved for trusted events. Requires runtimeMode \"coordinated\".","description":"WHAT A BEAT COSTS, since 1.40.0 — published because it is not derivable from anything else here, and measuring it takes a real 12-role run.\n\nAt most 3 roles run a model per beat (`WORLD_TURN_MAX_ROLES`), whatever the cast size, and `skippedRoles` names everyone passed over with a code whose cost class is in `x-pouchy-cost-model`.\n\n**Nothing in a role’s prompt is per-OTHER-role except two things**, so a beat’s prompt cost does NOT grow with the cast: a maximum-size story (24 roles since 1.48.0) and a 3-role story produce byte-identical envelopes for the roles that speak. A role beyond the first three in declaration order only speaks when `focusRoles` (or an event subscription) seats it — a bigger cast needs the beat to choose who is in the room. Each envelope carries the trigger, the caller’s one-beat stage note if any (`trigger.context`, 1.49.0), the chosen direction if any, that role’s OWN server-side projection (scene, location, clock, flags, the facts it may know, its own goals, its own secrets, its own private notes) and — 1.39.0 — the effect vocabulary its own grant allows. The two exceptions are (1) the lines already spoken EARLIER IN THE SAME BEAT, at most `WORLD_TURN_MAX_ROLES - 1` of them, and (2) inside the effect brief, capped id lists (other roles this role already holds a relation with; entities in the world), which carry ids only and report what they cut.\n\n`roles[].description` reaches no prompt at all, and `roles[].goals` and `roles[].secrets` reach only that role’s own envelope. So narrowing a beat with `focusRoles` narrows the model calls; it is not also needed to narrow the prompt, because the prompt was never wide.","security":[{"SecretKey":[],"SourceSignature":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"turnId":{"type":"string"},"trigger":{"type":"object","properties":{"kind":{"type":"string","enum":["user"]},"text":{"type":"string","description":"What the reader said or did this beat — rendered to every speaking role as \"The user says: …\". Keep it to the reader’s own words: a circumstance (\"this is a phone call\") belongs in `context`, where no role will read it as speech."},"context":{"type":"string","maxLength":500,"description":"World API 1.49.0. A STAGE NOTE for this beat only — the circumstance the lines are spoken in, e.g. \"You are on the phone; the reader is not in the room.\" Rendered into every speaking role’s envelope right after the user’s words, labelled as not being anything anyone said, so a role plays the circumstance instead of quoting it. It writes NO world state (the commit is the same clock tick a beat without it makes), grants no role anything, is not a fact, and no later beat carries it. It does ride into each speaking role’s own session record with the rest of its envelope, so a character can recall that the conversation was a call; the world cannot. To make the WORLD remember (a missed call, a call that happened), send a `proposedPatches` op. Trimmed; blank is the same as absent; longer than 500 characters or a non-string is a 400 before anything is read or spent. Billed as input tokens once per speaking role."},"allowSilence":{"type":"boolean","description":"World API 1.52.0. Let a seated role CHOOSE not to speak this beat. Without it every speaking role’s envelope ends with an instruction to reply, which outranks a \"stay silent\" line in the agent’s own prompt — so a role told in its prompt that it may stay silent still speaks. With it, each envelope ends instead with one decision the role makes first (1.52.1): is this beat its to answer? It is not when the user addresses someone else — names another character, or says only that one should answer — and the role has nothing the scene needs; the other roles seated on the beat are named for it. Then exactly `[SILENT]`, or its reply. A correction pass after a refused effect is never offered silence. The choice is the MODEL’s: the platform guarantees how the token is handled, not how often a model takes it, so when you KNOW the addressee, narrow `focusRoles` to them instead — that is deterministic, and the role left out costs no model call. MEASURED (an integrator, 2026-10-07, 1.52.1): the role that should have stayed silent did so in 1 of 6 beats where the reader named the other role, and 0 of 6 beats where the reader was continuing an exchange with the other role; on beats that addressed both roles neither went silent (6 of 6). Treat it as permitted, not relied on. The continuation case has a structural reason: roles see only their OWN past beats and the lines already spoken in THIS beat (perspective isolation), so a role that was not seated on the earlier exchange never saw it and cannot tell the reader is continuing it — no wording can supply what is not in its context. To seat it anyway, state the circumstance in `trigger.context` (\"the reader is answering the landlord, who just asked when the rent is due\"); better, seat only the role the exchange belongs to. A role that takes it is listed in `skippedRoles` with `code: \"no_message\"` and `silence: \"chosen\"`, publishes nothing, and does not make the beat `partial`; its model call was made and is billed like any other. The token is never published even without this flag. If EVERY seated role chooses silence the beat still commits (the clock tick, any `proposedPatches`) with `roleMessages: []` and `deliveryStatus: \"none\"` — narrow `focusRoles` to the role being addressed when a reply is required. A non-boolean is a 400. Absent or false: unchanged."},"sourceRoleId":{"type":"string","description":"Excludes this role from the beat’s selection — \"this character does not answer itself\". That is ALL it does: it does not attribute `text` to the role (every other role still reads \"The user says: …\"), it is not a way for the reader to speak as a character, and an excluded role is not listed in skippedRoles. To choose who answers, use focusRoles."},"focusRoles":{"type":"array","items":{"type":"string"},"maxItems":16,"description":"Narrow this beat to the named roles — shrink-only, mirroring the event lane’s subscription routing. Intersected with the bound story cast: an id outside the cast selects nothing, and a focus that selects nobody is the ordinary \"no selectable role\" refusal, which spends no model call. Roles outside the focus appear in skippedRoles as \"not focused\". Absent or empty means every bound role, capped as before. A role that has LEFT the worldline (1.53.0, `set_role_presence`) is never selected, focused or not: naming it selects nothing, and it reads as `role_departed` — so a focus does not need to repeat who has left, and an empty focus does not bring them back."},"proposedPatches":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"op":{"type":"string","enum":["set_scene"]},"sceneId":{"type":"string"}},"required":["op","sceneId"]},{"type":"object","properties":{"op":{"type":"string","enum":["advance_clock"]},"by":{"type":"integer"}},"required":["op","by"]},{"type":"object","properties":{"op":{"type":"string","enum":["set_location"]},"location":{"type":"string"}},"required":["op","location"]},{"type":"object","properties":{"op":{"type":"string","enum":["set_flag"]},"key":{"type":"string"},"value":{"type":["string","number","boolean"],"description":"Checked against the PINNED story three ways and no more: the key must be declared in `stateSchema.flags`, the value’s JSON type must match that flag’s `kind`, and a string is length-capped. A string flag’s VALUE SET IS NOT VALIDATED — `StoryFlagSpec` has no enum field, so the legal values live only in a `label`’s prose, which is not machine-readable. Writing a value nobody declared commits silently. Validating that set is the caller’s job, deliberately: the platform will not invent an enum it cannot read."}},"required":["op","key","value"]},{"type":"object","properties":{"op":{"type":"string","enum":["increment_flag"]},"key":{"type":"string"},"by":{"type":"integer","description":"World API 1.54.0. Added to the NUMBER flag `key` (declared in `stateSchema.flags` with `kind: \"number\"`; any other kind is refused). A non-zero integer within ±1000; negative to spend. Applied inside the commit’s own transaction to the value the world holds at that moment (the declared `initial`, then 0, if the flag was never written), so you do not need to know the current value and a role’s earlier write is composed with, not overwritten. Ops run in array order, so a `set_flag` earlier in the same batch is the base. Precedence: like `set_flag`, it holds this flag for the beat — a role `set_flag` or `increment_flag` on the same key is refused with `provider_precedence`. The story’s `range` is presentation-only and is never consulted; use `min` / `max`."},"min":{"type":"number","description":"Optional guard. If the result would be below it, the WHOLE batch is refused (`rejected`, HTTP 422, the op named with the values in `rejectedEffects`) — never clamped. \"The keycard is used up\" is a refusal. Judged on the running value, so the second of two spends in one batch is the one refused."},"max":{"type":"number","description":"Optional guard, the same as `min` in the other direction. `min` above `max` is refused."}},"required":["op","key","by"]},{"type":"object","properties":{"op":{"type":"string","enum":["reveal_fact"]},"factId":{"type":"string"}},"required":["op","factId"]},{"type":"object","properties":{"op":{"type":"string","enum":["complete_node"]},"nodeId":{"type":"string"}},"required":["op","nodeId"]},{"type":"object","properties":{"op":{"type":"string","enum":["set_relation"]},"between":{"type":"array","items":{"type":"string"},"minItems":2,"maxItems":2},"descriptor":{"type":"string"}},"required":["op","between","descriptor"]},{"type":"object","properties":{"op":{"type":"string","enum":["set_entity"]},"entityId":{"type":"string"},"descriptor":{"type":"string"}},"required":["op","entityId","descriptor"]},{"type":"object","properties":{"op":{"type":"string","enum":["set_role_presence"]},"roleId":{"type":"string"},"present":{"type":"boolean","description":"World API 1.53.0. `false`: the role LEAVES this worldline (dies, exits for good) — from the NEXT beat it is never cast on this instance, on any lane: the turns door, an event wake (even if it subscribes to the event), a deliberation and its select. It is reported in `skippedRoles` with `code: \"role_departed\"` and spends no model call. Since 1.58.0 a departed role also gets no world session mint (409 `code: \"role_departed\"`), no turn on a session it already holds, and no call. `true`: it returns from the next beat — and, since 1.58.0, a revival in a beat’s OWN `proposedPatches` already counts for that beat’s selection, so an instance whose every eligible role had departed can be revived (it used to refuse with \"no selectable role\" before any commit). Per INSTANCE: other instances of the environment are untouched. Idempotent either way. The role must be declared in the pinned story; a role can never write this (no role effect maps to it). The beat that commits the departure still casts the role, as every patch takes effect after its own beat; leave it out of that beat’s `focusRoles` to silence it there too. Readable as `departedRoles` on the state read (`[]` when nobody has left). A departed role keeps its own transcript; while away it records nothing, and on return its view is the world as it now stands, not what was said meanwhile."}},"required":["op","roleId","present"]}]},"maxItems":20,"description":"DETERMINISTIC world writes YOUR backend authors for this beat — the entrance that does not depend on a model’s judgement, published 1.39.0 and accepted since Batch 4. Use it when your system already KNOWS something the story must record: the player pressed a choice, so `complete_node` the objective and `set_flag` the decision; an item was bought, so `set_entity`. Commits in ONE all-or-nothing batch with the turn’s clock tick and the surviving role effects, so a single refused op moves nothing and the beat answers `rejected` with per-op reasons in `rejectedEffects`. That outcome is served as **HTTP 422, and the body is the whole turn result** — the per-op reasons are in it, so read the body rather than treating the status as an opaque error; there is no second, different failure shape to handle. Note the COST: the caller batch is validated by the single writer, which runs AFTER the beat’s role turns, so a malformed op refuses a beat whose model calls have already been spent. Validate keys and ids on your side if that matters to you. Ordered: a `complete_node` may satisfy a later op’s prerequisite inside the same array. Every op is re-validated against the PINNED story — an undeclared flag, fact, node, scene or role is refused, and a `complete_node` whose prerequisites are unmet is refused by name. `note_private` is not available to a provider (it needs an acting role). At most 20 ops. This is not a way to bypass the role grant: it is the PROVIDER acting as itself, recorded in the ledger as the coordinator’s batch, and it never widens what a role may propose. PRECEDENCE (world API 1.46.0): when a role proposes a DIFFERENT value on an axis this batch writes (`set_flag` or, from 1.54.0, `increment_flag` on the same key — a role increment there is refused too, since it would erase yours; `set_scene`, `set_entity` on the same entity), the role effect is refused with code `provider_precedence` and its reason in `rejectedEffects`, and YOUR value is what the world holds — your patch is the reader’s own action on a signed request, a role proposal is model output, and deterministic input is not overridable by model judgement. Agreement is not a conflict: a role proposing the SAME value stays accepted. Before 1.46.0 both ops committed and the plain last-write-wins apply order gave the ROLE the final value, with nothing reporting it. One op still merges by array order — `set_relation`, because a patch names the PAIR while a proposal names the other role, and mapping them would be a guess. `reveal_fact`, `complete_node` and `advance_clock` never collide: they are append-only or commutative."}},"required":["kind","text"]},"traceId":{"type":"string"}},"required":["turnId","trigger"]}}}},"responses":{"200":{"description":"The turn ran (see completionStatus / executionStatus / deliveryStatus). With `Accept: text/event-stream` (world API 1.50.0) the same beat answers as a stream instead; see `x-pouchy-turn-stream`.","content":{"application/json":{"schema":{"type":"object","properties":{"turnId":{"type":"string"},"worldInstanceId":{"type":"string"},"beforeStateRevision":{"type":"integer"},"afterStateRevision":{"type":"integer"},"selectedRoles":{"type":"array","items":{"type":"string"}},"roleMessages":{"type":"array","description":"What each seated role said this beat. `fallback: true` marks a platform-authored neutral line rather than something the character said. `agentRevision` is the agent template revision that PRODUCED the line, and it is the field to read when a capability you turned on does not appear to be in effect: a world beat resolves the PINNED PRODUCTION revision in both key envs, never the staging head that PATCH /admin/agents/{id} writes and that a test-key session mint resolves — so `genui`, `instructions`, `skills` and `social.pair` reach a beat only after the agent is promoted. Absent on a `fallback` line, which no template produced.","items":{"type":"object","properties":{"roleId":{"type":"string"},"message":{"type":"string"},"fallback":{"type":"boolean"},"agentRevision":{"type":"integer"},"attachments":{"type":"array","description":"Files this role beat produced (create_spreadsheet / create_document). OFF BY DEFAULT since world API 1.45.0: `files` is in the platform default set, so those tools used to be on every beat by accident of that default — a beat now has the scope SUBTRACTED unless the agent template sets `worldFiles: true` (PERSONA-class, so promote the agent before expecting a beat to see it, and read `agentRevision` to confirm which template ran). Each is { fileId, fileName, mimeType, url }. The url is an UNAUTHENTICATED capability link served for 7 days: possession of the 128-bit id is the permission, by design, because a download tapped in an IM client cannot carry an Authorization header. Reported here because the beat otherwise records the file NOWHERE — the producing tool tells the model to put that link in its reply, so on this plane a file reaches the reader inside a character line. Deliver it your own way if that is not what you want.","items":{"type":"object","properties":{"fileId":{"type":"string"},"fileName":{"type":"string"},"mimeType":{"type":"string"},"url":{"type":"string"}}}}}}},"committedStateDiff":{"type":"array","items":{"type":"object","additionalProperties":true}},"rejectedEffects":{"type":"array","items":{"type":"object","properties":{"index":{"type":"integer"},"reason":{"type":"string"},"roleId":{"type":"string"},"kind":{"type":"string"},"code":{"type":"string","enum":["narrative_conflict","policy_rejection","schema_rejection","canon_rejection","concurrency_rejection","repair_failure","provider_precedence"],"description":"WHY, as a code. `narrative_conflict` is the only one that describes story content; every other code is a defect in the story package, the prompt or the integration. Absent on a turn committed before the taxonomy — read that as unknown, never as narrative. The read-back (GET …/turns/{turnId}) carries it since 1.51.1."}}}},"skippedRoles":{"type":"array","description":"Roles that were in the cast but did not speak. `reason` is the HUMAN line and carries the sub-cause; it is free to be reworded, so never key off it. `code` is the machine half — a closed enum (see below) added 2026-09-09 after an integrator, given the prefixes in a letter because this document carried none, built a cost model on `reason.startsWith(...)`. To read a beat: every role in `selectedRoles` either has a line in `roleMessages` or is listed here — a role that produced no line, deliberately or not, is listed here as `no_message`, never left out. (Before 1.52.0 this sentence said a role in `selectedRoles` and absent here \"chose silence\"; that was never true.) A role that CHOSE silence under `trigger.allowSilence` additionally carries `silence: \"chosen\"`; `x-pouchy-skip-semantics` is per code, so branch on `silence` for that one distinction.","items":{"type":"object","properties":{"roleId":{"type":"string"},"reason":{"type":"string"},"silence":{"type":"string","enum":["chosen"],"description":"World API 1.52.0. Present only with `code: \"no_message\"`, and only when the role output the silence token `trigger.allowSilence` offered it: it CHOSE not to speak — not a fault, and it does not make the beat `partial`. Absent on every other `no_message` (a blank reply, a terminal that was not a message); read that as unknown, never as a choice — a blank reply is also what a failed final model call returns."},"code":{"type":"string","enum":["not_focused","role_cap_reached","no_actor_bound","role_departed","deadline_exhausted","not_admitted","actor_unavailable","session_busy","content_policy","turn_error","no_message","effect_refused","conflict_undelivered"],"x-pouchy-cost-model":{"version":"1.1.0","noModelCall":["not_focused","role_cap_reached","no_actor_bound","role_departed","deadline_exhausted","not_admitted","actor_unavailable","session_busy","content_policy"],"modelMayHaveRun":["turn_error","no_message","effect_refused","conflict_undelivered"],"digest":"1f836a3d5fe7c4db"},"x-pouchy-skip-semantics":{"version":"1.1.0","codes":{"not_focused":{"stage":"never_cast","fault":"none","meaning":"The caller narrowed this beat with focusRoles and did not name this role."},"role_cap_reached":{"stage":"never_cast","fault":"capacity","meaning":"The beat was already full at WORLD_TURN_MAX_ROLES. Seats fill in DECLARATION order, so the same role is skipped on every beat until the cast or the focus changes."},"no_actor_bound":{"stage":"never_cast","fault":"configuration","meaning":"No actor is bound to this role in this world. An operator fixes it by minting one."},"role_departed":{"stage":"never_cast","fault":"none","meaning":"The role has left this worldline (set_role_presence with present:false) and is not cast on this instance, on any lane, until a present:true brings it back. Other instances of the environment are unaffected."},"deadline_exhausted":{"stage":"cast_no_model","fault":"capacity","meaning":"The beat's time budget ran out before this role's turn; the roles ahead of them spent it."},"not_admitted":{"stage":"cast_no_model","fault":"policy","meaning":"Admission refused the run — metering, a suspended instance, or another gate. A decision, not a defect: the sub-cause is in `reason`, and retrying changes nothing until the underlying condition does."},"actor_unavailable":{"stage":"cast_no_model","fault":"defect","meaning":"The actor's registry row is missing or belongs to the other environment. Distinct from not_admitted on purpose: one is a policy answering, the other is a thing that should exist and does not."},"session_busy":{"stage":"cast_no_model","fault":"transient","meaning":"A beat for this role is already in flight and its turn lease refused. Transient by construction."},"content_policy":{"stage":"cast_no_model","fault":"policy","meaning":"The moderation gate silenced this role BEFORE any model ran — a classifier verdict or a blocked term."},"turn_error":{"stage":"model_may_have_run","fault":"defect","meaning":"The runtime failed. Deliberately does NOT promise whether a model call happened: context assembly failing and a provider dying mid-stream both land here, and no code can separate them after the fact."},"no_message":{"stage":"model_may_have_run","fault":"none","meaning":"The turn completed without a line — a terminal that was not a message, or a deliberately silent beat. The model ran."},"effect_refused":{"stage":"spoke_withheld","fault":"policy","meaning":"This role's effect was refused and the one correction pass did not save it, so their line was never published."},"conflict_undelivered":{"stage":"spoke_withheld","fault":"transient","meaning":"The world moved under the turn; nothing was published, by anyone, on this beat."}},"digest":"70c09e4e50a838f7"},"description":"WHY, machine-readable. Each code also carries a structured `stage` and `fault` in `x-pouchy-skip-semantics` — branch on those rather than on this paragraph. No model call was made for `not_focused` (the caller narrowed the cast), `role_cap_reached` (the three-seat staging limit, decided in declaration order — the same role is skipped every beat until the cast changes), `no_actor_bound` (setup incomplete), `deadline_exhausted` (the beat ran out of budget first), `not_admitted` (metering / suspension / a gate said no — a decision), `actor_unavailable` (the actor registry row is missing or belongs to the other env — a defect), `session_busy` (a beat for this role is already in flight) or `content_policy` (the moderation gate silenced them BEFORE any model ran). A model call was made, or may have been, for `turn_error` (the runtime failed — deliberately does NOT promise a call happened: assembly failing and a provider dying mid-stream both land here) and `no_message` (the turn completed with no line — a non-message terminal, a blank reply, or, with `silence: \"chosen\"`, a role that chose not to speak). The role DID speak and the line was withheld for `effect_refused` (their effect was refused and the one correction did not save it) and `conflict_undelivered` (the world moved under the turn; nothing was published to anyone). Absent only from a non-production caller; every path in the platform sets it."}}}},"nextOptions":{"type":"array","items":{"type":"object","properties":{"branchId":{"type":"string"},"condition":{"type":"string"}}}},"repairs":{"type":"array","items":{"type":"object","properties":{"roleId":{"type":"string"},"outcome":{"type":"string","enum":["repaired","fallback"]},"reason":{"type":"string"}}}},"traceId":{"type":"string"},"executionStatus":{"type":"string","enum":["committed","not_committed"]},"deliveryStatus":{"type":"string","enum":["delivered","pending","none"]},"completionStatus":{"type":"string","enum":["complete","partial","duplicate","conflict","rejected","refused"]}}}},"text/event-stream":{"schema":{"type":"string"},"description":"World API 1.50.0. Send `Accept: text/event-stream` to receive each role’s line the moment that role finishes, instead of waiting for the whole beat. Roles still run one after another and a later role still reads the earlier lines; only WHEN the caller sees a line changes. Every refusal that happens before the beat runs (key, signature, rate limit, a 400) is still a plain JSON response with its usual status, so a stream only opens on a beat that is going to run; inside it, the HTTP status is 200 and the beat’s own status travels in the closing `result` frame. Each frame is `id: <n>`, `event: <type>`, `data: <JSON>`, plus `: ping` comments every 15 s. **A streamed line is PROVISIONAL until `lines_settled`**: the beat commits once, at the end, and `lines_settled.withdrawn` lists every streamed role whose line the commit does not stand behind. The beat runs to completion even if the reader disconnects; a retry under the same turnId answers `duplicate`, and the committed lines are on GET `…/turns`. Full frame list in `x-pouchy-turn-stream`."}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"x-pouchy-turn-stream":{"frames":{"beat_started":"`{ attempt, selectedRoles }`: who will be asked, in speaking order. `selectedRoles.length` is the most `role_line` frames this attempt can produce, so a reader can show \"1 of 3\" with no text at all.","role_line":"`{ attempt, roleId, message, order }`: one role finished and this is its line, exactly as the beat would deliver it (content policy already applied). `order` is 1-based within the attempt.","role_skipped":"`{ attempt, roleId, reason, code, silence? }`: a selected role produced no line (same codes as `skippedRoles`). It still counts toward progress. `silence: \"chosen\"` (1.52.0) marks a role that chose not to speak under `trigger.allowSilence`.","attempt_restarted":"`{ attempt: 2, reason: \"conflict\" }`: the commit lost a race and the beat re-runs EVERY role on fresh state. Every line streamed so far is void; a second `beat_started` follows.","role_line_replaced":"`{ attempt, roleId, message, outcome }`: the role’s effect was refused (possibly because a LATER role proposed something that conflicts with it) and its line was replaced by its one correction (`repaired`) or by the platform’s neutral beat (`fallback`). Supersedes that role’s `role_line`. Roles that spoke after the original line had already read it.","lines_settled":"`{ standing: [{ roleId, message, fallback? }], withdrawn: [{ roleId, code }] }`: the verdict on every streamed line. `standing` is what the world committed. `withdrawn.code` is `effect_refused` (the beat committed but silenced that role) or the beat’s `completionStatus` when nothing committed: `conflict`, `rejected`, `refused`, `partial` (the commit store was unavailable) or `duplicate` (a racing retry committed first; read its lines from GET `…/turns`).","result":"`{ httpStatus, result }`: the exact body and status the same request answers without the header. Always the last frame of a beat that ran.","error":"`{ error, errorId, httpStatus }` (`errorId` / `httpStatus` since 1.50.1): the beat failed unexpectedly after the stream opened — the fault the JSON door answers as a 500. `httpStatus` is 500; `errorId` is the recorded `err_…` reference to quote to support, the same one a JSON 500 carries. Retry with the same turnId."},"provisional":"A line stands only when `result.executionStatus` is `committed` and the role is not withdrawn. Show streamed lines as provisional (or simply show them) and reconcile on `lines_settled`."}}},"/projects/{projectId}/environments/{envId}/instances/{worldInstanceId}/turns/{turnId}":{"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"},"description":"The project id."},{"name":"envId","in":"path","required":true,"schema":{"type":"string"},"description":"The world (environment) id — env_…."},{"name":"worldInstanceId","in":"path","required":true,"schema":{"type":"string"},"description":"The world instance — one user’s story."},{"name":"turnId","in":"path","required":true,"schema":{"type":"string"},"description":"The turn id."}],"get":{"summary":"Read back a COMMITTED turn in the same shape the door returned — the recovery path when a response was lost. 404 means the turn never committed: there is no \"maybe\" state to report.","security":[{"OwnerToken":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"properties":{"turnId":{"type":"string"},"worldInstanceId":{"type":"string"},"beforeStateRevision":{"type":"integer"},"afterStateRevision":{"type":"integer"},"roleMessages":{"type":"array","items":{"type":"object","properties":{"roleId":{"type":"string"},"message":{"type":"string"}}}},"committedStateDiff":{"type":"array","items":{"type":"object","additionalProperties":true}},"rejectedEffects":{"type":"array","items":{"type":"object","properties":{"index":{"type":"integer"},"reason":{"type":"string"},"roleId":{"type":"string"},"kind":{"type":"string"},"code":{"type":"string","enum":["narrative_conflict","policy_rejection","schema_rejection","canon_rejection","concurrency_rejection","repair_failure","provider_precedence"],"description":"WHY, as a code. `narrative_conflict` is the only one that describes story content; every other code is a defect in the story package, the prompt or the integration. Absent on a turn committed before the taxonomy — read that as unknown, never as narrative. The read-back (GET …/turns/{turnId}) carries it since 1.51.1."}}}},"selectedRoles":{"type":"array","items":{"type":"string"}},"skippedRoles":{"type":"array","description":"Roles that were in the cast but did not speak. `reason` is the HUMAN line and carries the sub-cause; it is free to be reworded, so never key off it. `code` is the machine half — a closed enum (see below) added 2026-09-09 after an integrator, given the prefixes in a letter because this document carried none, built a cost model on `reason.startsWith(...)`. To read a beat: every role in `selectedRoles` either has a line in `roleMessages` or is listed here — a role that produced no line, deliberately or not, is listed here as `no_message`, never left out. (Before 1.52.0 this sentence said a role in `selectedRoles` and absent here \"chose silence\"; that was never true.) A role that CHOSE silence under `trigger.allowSilence` additionally carries `silence: \"chosen\"`; `x-pouchy-skip-semantics` is per code, so branch on `silence` for that one distinction.","items":{"type":"object","properties":{"roleId":{"type":"string"},"reason":{"type":"string"},"silence":{"type":"string","enum":["chosen"],"description":"World API 1.52.0. Present only with `code: \"no_message\"`, and only when the role output the silence token `trigger.allowSilence` offered it: it CHOSE not to speak — not a fault, and it does not make the beat `partial`. Absent on every other `no_message` (a blank reply, a terminal that was not a message); read that as unknown, never as a choice — a blank reply is also what a failed final model call returns."},"code":{"type":"string","enum":["not_focused","role_cap_reached","no_actor_bound","role_departed","deadline_exhausted","not_admitted","actor_unavailable","session_busy","content_policy","turn_error","no_message","effect_refused","conflict_undelivered"],"x-pouchy-cost-model":{"version":"1.1.0","noModelCall":["not_focused","role_cap_reached","no_actor_bound","role_departed","deadline_exhausted","not_admitted","actor_unavailable","session_busy","content_policy"],"modelMayHaveRun":["turn_error","no_message","effect_refused","conflict_undelivered"],"digest":"1f836a3d5fe7c4db"},"x-pouchy-skip-semantics":{"version":"1.1.0","codes":{"not_focused":{"stage":"never_cast","fault":"none","meaning":"The caller narrowed this beat with focusRoles and did not name this role."},"role_cap_reached":{"stage":"never_cast","fault":"capacity","meaning":"The beat was already full at WORLD_TURN_MAX_ROLES. Seats fill in DECLARATION order, so the same role is skipped on every beat until the cast or the focus changes."},"no_actor_bound":{"stage":"never_cast","fault":"configuration","meaning":"No actor is bound to this role in this world. An operator fixes it by minting one."},"role_departed":{"stage":"never_cast","fault":"none","meaning":"The role has left this worldline (set_role_presence with present:false) and is not cast on this instance, on any lane, until a present:true brings it back. Other instances of the environment are unaffected."},"deadline_exhausted":{"stage":"cast_no_model","fault":"capacity","meaning":"The beat's time budget ran out before this role's turn; the roles ahead of them spent it."},"not_admitted":{"stage":"cast_no_model","fault":"policy","meaning":"Admission refused the run — metering, a suspended instance, or another gate. A decision, not a defect: the sub-cause is in `reason`, and retrying changes nothing until the underlying condition does."},"actor_unavailable":{"stage":"cast_no_model","fault":"defect","meaning":"The actor's registry row is missing or belongs to the other environment. Distinct from not_admitted on purpose: one is a policy answering, the other is a thing that should exist and does not."},"session_busy":{"stage":"cast_no_model","fault":"transient","meaning":"A beat for this role is already in flight and its turn lease refused. Transient by construction."},"content_policy":{"stage":"cast_no_model","fault":"policy","meaning":"The moderation gate silenced this role BEFORE any model ran — a classifier verdict or a blocked term."},"turn_error":{"stage":"model_may_have_run","fault":"defect","meaning":"The runtime failed. Deliberately does NOT promise whether a model call happened: context assembly failing and a provider dying mid-stream both land here, and no code can separate them after the fact."},"no_message":{"stage":"model_may_have_run","fault":"none","meaning":"The turn completed without a line — a terminal that was not a message, or a deliberately silent beat. The model ran."},"effect_refused":{"stage":"spoke_withheld","fault":"policy","meaning":"This role's effect was refused and the one correction pass did not save it, so their line was never published."},"conflict_undelivered":{"stage":"spoke_withheld","fault":"transient","meaning":"The world moved under the turn; nothing was published, by anyone, on this beat."}},"digest":"70c09e4e50a838f7"},"description":"WHY, machine-readable. Each code also carries a structured `stage` and `fault` in `x-pouchy-skip-semantics` — branch on those rather than on this paragraph. No model call was made for `not_focused` (the caller narrowed the cast), `role_cap_reached` (the three-seat staging limit, decided in declaration order — the same role is skipped every beat until the cast changes), `no_actor_bound` (setup incomplete), `deadline_exhausted` (the beat ran out of budget first), `not_admitted` (metering / suspension / a gate said no — a decision), `actor_unavailable` (the actor registry row is missing or belongs to the other env — a defect), `session_busy` (a beat for this role is already in flight) or `content_policy` (the moderation gate silenced them BEFORE any model ran). A model call was made, or may have been, for `turn_error` (the runtime failed — deliberately does NOT promise a call happened: assembly failing and a provider dying mid-stream both land here) and `no_message` (the turn completed with no line — a non-message terminal, a blank reply, or, with `silence: \"chosen\"`, a role that chose not to speak). The role DID speak and the line was withheld for `effect_refused` (their effect was refused and the one correction did not save it) and `conflict_undelivered` (the world moved under the turn; nothing was published to anyone). Absent only from a non-production caller; every path in the platform sets it."}}}},"repairs":{"type":"array","items":{"type":"object","properties":{"roleId":{"type":"string"},"outcome":{"type":"string","enum":["repaired","fallback"]},"reason":{"type":"string"}}}},"nextOptions":{"type":"array","items":{"type":"object","properties":{"branchId":{"type":"string"},"condition":{"type":"string"}}}},"committedAt":{"type":"integer","description":"Epoch milliseconds the single writer committed the beat."},"environmentRevision":{"type":"integer"},"eventId":{"type":"string"},"traceId":{"type":"string"},"completionStatus":{"type":"string","enum":["committed"],"description":"Always `committed`: a turn that did not commit has no entry and answers 404."},"executionStatus":{"type":"string","enum":["committed"]},"deliveryStatus":{"type":"string"}},"description":"The committed turn, read from its ledger entry in the live result’s shape. `selectedRoles`, `skippedRoles`, `repairs` and `nextOptions` are absent on an entry that predates their recording — read that as unknown, not \"none\"."}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/projects/{projectId}/environments/{envId}/overview":{"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"},"description":"The project id."},{"name":"envId","in":"path","required":true,"schema":{"type":"string"},"description":"The world (environment) id — env_…."}],"get":{"summary":"Read-only operational overview for one world: readiness (are all roles bound, is the provider connected, do all capabilities resolve) with a machine next-step per blocker, plus a bounded rollup of active instances, delivery health, pending script drafts, and whether quality has been evaluated. Composes existing reads into one call for the dashboard; writes nothing.","security":[{"OwnerToken":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/projects/{projectId}/environments/{envId}/instances/{worldInstanceId}/state":{"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"},"description":"The project id."},{"name":"envId","in":"path","required":true,"schema":{"type":"string"},"description":"The world (environment) id — env_…."},{"name":"worldInstanceId","in":"path","required":true,"schema":{"type":"string"},"description":"The world instance — one user’s story."}],"get":{"summary":"The world’s current state in the USER projection — the shape is described below rather than left to a live call. `flags` is EVERY flag, not only those an author marked `public: true`: that filter belongs to the progress checkpoint, whose audience is a player. `completedNodes` is the plot progress the checkpoint reports only as a COUNT. `factsVisible` is the world-revealed set. The role-private layer is never here. `stateRevision` is the CAS counter the turns door expects.","security":[{"OwnerToken":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"worldInstanceId":{"type":"string"},"environmentId":{"type":"string"},"environmentRevision":{"type":"integer"},"storyPackageRef":{"type":"string"},"state":{"type":"object","properties":{"stateRevision":{"type":"integer"},"clock":{"type":"integer"},"sceneId":{"type":"string"},"location":{"type":"string"},"flags":{"type":"object","additionalProperties":true},"completedNodes":{"type":"array","items":{"type":"string"}},"factsVisible":{"type":"array","items":{"type":"string"}},"relations":{"type":"object","additionalProperties":true},"entities":{"type":"object","additionalProperties":true},"departedRoles":{"type":"array","items":{"type":"string"},"description":"World API 1.53.0. Roles that have left this worldline (`set_role_presence` with `present: false`), sorted; `[]` when nobody has. Always present from 1.53.0 — absent means an older server (unknown), never \"nobody\". A role listed here is not cast on this instance, on any lane, until a `present: true`."},"lastEntries":{"type":"array","items":{"type":"object","properties":{"entryId":{"type":"string"},"kind":{"type":"string"},"at":{"type":"integer"}}}}}}}}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/projects/{projectId}/environments/{envId}/instances/{worldInstanceId}/consistency":{"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"},"description":"The project id."},{"name":"envId","in":"path","required":true,"schema":{"type":"string"},"description":"The world (environment) id — env_…."},{"name":"worldInstanceId","in":"path","required":true,"schema":{"type":"string"},"description":"The world instance — one user’s story."},{"name":"cursor","in":"query","required":false,"schema":{"type":"integer"},"description":"Resume an `incomplete` scan from its cursor."},{"name":"layers","in":"query","required":false,"schema":{"type":"string"},"description":"Comma-separated. Omitted (the default) means deterministic only, with zero model calls and a body byte-identical to world API 1.19. `model_assisted` additionally requests the judgement layer, which also needs a server flag and a declaration on the published world revision — three gates, all required."}],"get":{"summary":"What this world’s own record proves about itself — the DETERMINISTIC layer. Every finding is a comparison between committed facts and the story package this instance pins; nothing is a judgement about writing, and nothing is repaired. Rate limited on two axes (project and IP, 30/min each) and never cached. Answers 200 with `status: \"unavailable\"` when the world cannot be read, and 429 with Retry-After when a ceiling is hit.","security":[{"OwnerToken":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"status":{"type":"string","enum":["complete","incomplete"]},"contractVersion":{"type":"integer"},"findings":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","enum":["revision_mismatch","ledger_drift","ledger_missing_entries","unrecorded_beat_claim","orphan_branch_reference","node_prereq_violation","effect_without_grant","flag_schema_violation","relation_change_without_cause","verbatim_secret_disclosure"],"description":"A CLOSED set. Members may be added in a later minor; a published member is never removed, renamed, or widened in meaning — a criterion found to be wrong is deprecated and replaced beside it. Treat an unknown code as informational rather than failing on it."},"layer":{"type":"string","enum":["deterministic"]},"status":{"type":"string","enum":["proven"]},"severity":{"type":"string","enum":["blocking","warning","info"]},"evidence":{"type":"array","description":"Never empty. Identifiers and already-committed facts; `quote` appears only on verbatim matches and carries the matched fragment, capped — never a whole line.","items":{"type":"object","properties":{"turnId":{"type":"string"},"seq":{"type":"integer"},"roleId":{"type":"string"},"sceneId":{"type":"string"},"nodeId":{"type":"string"},"branchId":{"type":"string"},"flagKey":{"type":"string"},"effectKind":{"type":"string"},"quote":{"type":"string"},"ledgerRejectionCode":{"type":"string"}}}}},"required":["code","layer","status","severity","evidence"]}},"coverage":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"skipped":{"type":"integer","description":"Entries this check could not examine."},"reason":{"type":"string","enum":["evidence_field_absent","scan_incomplete"]}},"required":["code","skipped","reason"]},"description":"What could NOT be examined, and why. A non-empty coverage list on an otherwise clean report means the report is clean about less than the whole world — absence of a finding is not proof of absence."},"entriesScanned":{"type":"integer"},"findingsTruncated":{"type":"boolean"},"cursor":{"type":"integer","description":"Present only when `status` is `incomplete`. Pass it back to continue."},"modelAssistedFindings":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","enum":["suspected_canon_conflict","suspected_revival","suspected_persona_conflict","suspected_secret_leak","suspected_cross_ip_contamination"],"description":"A SEPARATE closed set from the deterministic one, which stays frozen. These are judgements, not proofs."},"layer":{"type":"string","enum":["model_assisted"]},"status":{"type":"string","enum":["potential","confirmed","dismissed"],"description":"`potential` until a person decides. Confirmation is an editorial judgement recorded against the finding — it changes nothing about the story, the canon, the ledger or the world state."},"severity":{"type":"string","enum":["warning","info"],"description":"Never `blocking`, at the type level as well as here. A suspicion must not be able to fail somebody’s build. `suspected_cross_ip_contamination` in particular is an editorial opinion even once confirmed, and is not evidence of isolation."},"findingId":{"type":"string","description":"Stable across scans for the same judgement: derived from the code and the server-normalised evidence refs, and from nothing else. A rephrased model note does not mint a new finding, which is what lets a dismissal stick."},"evidence":{"type":"array","items":{"type":"object","properties":{"turnId":{"type":"string"},"roleId":{"type":"string"}}},"description":"Assembled server-side from committed rows. The model selects which rows by index; it never writes evidence."},"suggestion":{"type":"object","properties":{"kind":{"type":"string","enum":["author_note"]},"text":{"type":"string"}}},"reviewedBy":{"type":"string"},"reviewedAt":{"type":"string"}},"required":["code","layer","status","severity","findingId","evidence"]},"description":"Present ONLY when the request asked for `model_assisted` in `layers`. A default request carries neither this key nor `modelAssistedNote`, byte-identically to world API 1.19. Suspicions never appear in `findings` — a consumer testing `severity === \"blocking\"` there must not begin failing on an opinion."},"modelAssistedNote":{"type":"string","enum":["disabled_server","not_declared","rate_limited","unavailable","incomplete"],"description":"Why the model layer produced less than a full pass, when it was asked for. `disabled_server` and `not_declared` mean a gate was shut; `incomplete` means the per-scan budget stopped it early and the suspicions listed are not the whole picture."}},"required":["status","contractVersion","findings","coverage","entriesScanned","findingsTruncated"]},{"type":"object","properties":{"status":{"type":"string","enum":["rate_limited"]},"retryAfterSec":{"type":"integer"}},"required":["status","retryAfterSec"]},{"type":"object","properties":{"status":{"type":"string","enum":["unavailable"]},"reason":{"type":"string","enum":["world_unavailable"]}},"required":["status","reason"]}]}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/projects/{projectId}/environments/{envId}/instances/{worldInstanceId}/progress":{"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"},"description":"The project id."},{"name":"envId","in":"path","required":true,"schema":{"type":"string"},"description":"The world (environment) id — env_…."},{"name":"worldInstanceId","in":"path","required":true,"schema":{"type":"string"},"description":"The world instance — one user’s story."}],"get":{"summary":"Where the story is, and whether it can go on — a DERIVED checkpoint over facts that already committed. Nothing is stored, no model is asked, and the story package is the one this instance pinned, at its exact revision and content hash. Answers 200 even with no checkpoint: \"this world cannot be resumed\" is an answer about the story, not an error about the request.","security":[{"OwnerToken":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"status":{"type":"string","enum":["ready"]},"resumable":{"type":"boolean"},"checkpoint":{"type":"object","properties":{"contractVersion":{"type":"integer","description":"Checkpoint contract version. 1 today."},"environmentRevision":{"type":"integer","description":"The revision this INSTANCE is pinned to for life — never the environment head."},"storyPackageRef":{"type":"object","properties":{"packageId":{"type":"string"},"revision":{"type":"integer"},"contentHash":{"type":"string"}},"required":["packageId","revision","contentHash"]},"stateRevision":{"type":"integer","description":"The CAS counter the turns door expects."},"started":{"type":"boolean","description":"At least one beat has committed."},"currentScene":{"type":["object","null"],"properties":{"sceneId":{"type":"string"},"title":{"type":"string"}},"description":"Null when the world has not opened a scene. A sceneId the pinned story does not declare is NOT reported as null — the whole read answers `story_reference_invalid` instead."},"completedNodeCount":{"type":"integer"},"declaredNodeCount":{"type":"integer","description":"How many nodes the AUTHOR declared. Not a denominator: a branching story never visits all of them, so a ratio of these two numbers means nothing."},"publicRelations":{"type":"array","items":{"type":"object","properties":{"between":{"type":"array","items":{"type":"string"}},"descriptor":{"type":"string"}}},"description":"World-public relations, sorted. Role-private material never appears here."},"publicRelationsTruncated":{"type":"boolean"},"publicFlags":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"value":{"description":"boolean | number | string, or null when the flag was never set and declares no initial"}}},"description":"Contract v4. The flags the pinned story declared `public: true`, sorted by key, with the value the state THIS checkpoint reports (the declared initial when never set; null when there is neither). ALWAYS present — an empty array for a story that publishes nothing — so absent never reads as unknown. A flag the story did not mark public never appears here."},"publicFlagsTruncated":{"type":"boolean"},"recentProgressRecords":{"type":"array","items":{"type":"object","properties":{"seq":{"type":"integer"},"kind":{"type":"string","enum":["turn","event","system"]},"at":{"type":"integer"}}},"description":"The newest committed beats, newest first — that one happened, at a revision, at a time. Not a story summary: no patches, no lines, no actors."},"recentProgressTruncated":{"type":"boolean"},"nextOptions":{"type":"array","items":{"type":"object","properties":{"branchId":{"type":"string"},"condition":{"type":"string"}}},"description":"Branches reachable from the completed nodes, derived from the pinned story. Never model-generated, and capped server-side."},"rebuildable":{"type":"boolean","description":"Always true — nothing here is stored; it is a function of committed facts."}},"required":["contractVersion","environmentRevision","storyPackageRef","stateRevision","started","completedNodeCount","declaredNodeCount","publicFlags","publicFlagsTruncated","nextOptions"]}},"required":["status","resumable","checkpoint"]},{"type":"object","properties":{"status":{"type":"string","enum":["unavailable"]},"resumable":{"type":"boolean","enum":[false]},"reason":{"type":"string","enum":["world_unavailable","story_reference_invalid"],"description":"`world_unavailable` covers every \"no answer\" cause at once — no such instance, no access, a revoked or deleted package, a contract this build cannot read. They are one word on purpose: naming them apart would let a caller map which internal objects exist by reading the refusals."}},"required":["status","resumable","reason"]}]}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/projects/{projectId}/environments/{envId}/instances/{worldInstanceId}/consistency/reviews/{findingId}":{"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"},"description":"The project id."},{"name":"envId","in":"path","required":true,"schema":{"type":"string"},"description":"The world (environment) id — env_…."},{"name":"worldInstanceId","in":"path","required":true,"schema":{"type":"string"},"description":"The world instance — one user’s story."},{"name":"findingId","in":"path","required":true,"schema":{"type":"string"},"description":"The finding this verdict is about."}],"post":{"summary":"Record a person’s verdict on one model-assisted finding. Owner plane only and NOT mirrored on the admin key: reading a scan is something a backend can do unattended, deciding that a suspicion about someone’s writing is real is not. The body is a CLAIM — the server re-normalises the evidence refs, checks each against this instance’s committed ledger, and recomputes the findingId before writing. The effect is bounded to the verdict: no Story Package, no canon, no ledger, no world state. Idempotent on the same decision; pass `expectedRevision` for CAS.","security":[{"OwnerToken":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"decision":{"type":"string","enum":["confirmed","dismissed","potential"]},"code":{"type":"string"},"evidenceRefs":{"type":"array","items":{"type":"object","properties":{"turnId":{"type":"string"},"roleId":{"type":"string"}}}},"expectedRevision":{"type":"integer"}},"required":["decision","code","evidenceRefs"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/projects/{projectId}/environments/{envId}/instances/{worldInstanceId}/ledger/replay":{"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"},"description":"The project id."},{"name":"envId","in":"path","required":true,"schema":{"type":"string"},"description":"The world (environment) id — env_…."},{"name":"worldInstanceId","in":"path","required":true,"schema":{"type":"string"},"description":"The world instance — one user’s story."}],"post":{"summary":"Verify that the materialized state is what the committed ledger says. ALWAYS a dry run: it reports consistent / drift / missing_entries / illegal_patch / incomplete / unavailable, and never repairs. Bounded and resumable — pass a previous report’s cursor to continue.","security":[{"OwnerToken":[]}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"cursor":{"type":"string"},"maxEntries":{"type":"integer"}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"worldInstanceId":{"type":"string"},"verdict":{"type":"string"},"detail":{"type":"string"},"fromRevision":{"type":"integer"},"rebuiltRevision":{"type":"integer"},"rebuiltHash":{"type":"string"},"currentRevision":{"type":"integer"},"currentHash":{"type":"string"},"entriesScanned":{"type":"integer"},"cursor":{"type":"string"},"saturated":{"type":"boolean"},"elapsedMs":{"type":"integer"},"dryRun":{"type":"boolean"}}}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/projects/{projectId}/environments/{envId}/instances/{worldInstanceId}/deliveries":{"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"},"description":"The project id."},{"name":"envId","in":"path","required":true,"schema":{"type":"string"},"description":"The world (environment) id — env_…."},{"name":"worldInstanceId","in":"path","required":true,"schema":{"type":"string"},"description":"The world instance — one user’s story."}],"get":{"summary":"The delivery queue for this instance: statuses, timings, attempt counts, error CLASSES, and which row is blocking its session. Never message bodies — one delivery’s text comes from the single-row route, explicitly, and that read is audited. Optional filters: status, sessionId, turnId, cursor, limit. Filters narrow what you see and never change the blocking calculation, which runs over the whole instance.","security":[{"OwnerToken":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"rows":{"type":"array","items":{"type":"object","additionalProperties":true}},"truncated":{"type":"boolean"}}}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/projects/{projectId}/environments/{envId}/instances/{worldInstanceId}/deliveries/drain":{"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"},"description":"The project id."},{"name":"envId","in":"path","required":true,"schema":{"type":"string"},"description":"The world (environment) id — env_…."},{"name":"worldInstanceId","in":"path","required":true,"schema":{"type":"string"},"description":"The world instance — one user’s story."}],"post":{"summary":"Run ONE bounded drain now, scoped to this instance — for the moment after a requeue, when the queue should move without waiting for the sweeper’s next tick. The report includes `blocked`: sessions still owing their oldest line, which is what says whether the requeue helped.","security":[{"OwnerToken":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/projects/{projectId}/environments/{envId}/instances/{worldInstanceId}/deliveries/{deliveryId}":{"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"},"description":"The project id."},{"name":"envId","in":"path","required":true,"schema":{"type":"string"},"description":"The world (environment) id — env_…."},{"name":"worldInstanceId","in":"path","required":true,"schema":{"type":"string"},"description":"The world instance — one user’s story."},{"name":"deliveryId","in":"path","required":true,"schema":{"type":"string"},"description":"The delivery id."}],"get":{"summary":"One delivery. `?includePayload=true` returns the line itself and writes an audit row naming the reader — the audit is what separates debugging a stuck queue from reading someone’s conversation. Signing material and the payload hash never travel.","security":[{"OwnerToken":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/projects/{projectId}/environments/{envId}/instances/{worldInstanceId}/deliveries/{deliveryId}/requeue":{"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"},"description":"The project id."},{"name":"envId","in":"path","required":true,"schema":{"type":"string"},"description":"The world (environment) id — env_…."},{"name":"worldInstanceId","in":"path","required":true,"schema":{"type":"string"},"description":"The world instance — one user’s story."},{"name":"deliveryId","in":"path","required":true,"schema":{"type":"string"},"description":"The delivery id."}],"post":{"summary":"Put a dead letter back in the queue. Takes NO body by design: the beat was committed by the coordinator, and an operator who could supply a replacement line would be authoring world history through the delivery plane. Idempotent by state — an already-queued row is a no-op. Refused for a delivered row, and for one whose payload has aged out (30 days), because sending \"something\" is worse than sending nothing. There is no discard verb.","security":[{"OwnerToken":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/projects/{projectId}/environments/{envId}/instances/{worldInstanceId}/script-drafts/{draftId}/editorial/{editorialId}/approved-export":{"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"},"description":"The project id."},{"name":"envId","in":"path","required":true,"schema":{"type":"string"},"description":"The world (environment) id — env_…."},{"name":"worldInstanceId","in":"path","required":true,"schema":{"type":"string"},"description":"The world instance — one user’s story."},{"name":"draftId","in":"path","required":true,"schema":{"type":"string"},"description":"The evidence draft id."},{"name":"editorialId","in":"path","required":true,"schema":{"type":"string"},"description":"The editorial draft id."}],"get":{"summary":"List the approved exports produced from this editorial draft, without their bodies.","security":[{"OwnerToken":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"exports":{"type":"array","items":{"type":"object","additionalProperties":true}}}}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}},"post":{"summary":"Turn an APPROVED editorial draft into a versioned approved script export — the production hand-off. Carries lineage from the original story package through the world instance, ledger range, evidence draft and editorial draft to the reviewer who approved it; every line keeps the origin the server verified, and human edits keep the editor’s text. Idempotent on content: the same approved review exports to the same id forever. `version` selects the contract and DEFAULTS TO 1 (`ApprovedScriptExportV1`, frozen); `version: 2` returns `ApprovedScriptExportV2`, which adds a synopsis assembled from scene headings, character notes, a mechanical episode split, the beats that completed a story node, committed relation changes and where the run diverged from its story — all derived from committed material, with no model call. The two contracts hash under separate domains, so one approval exported both ways yields two different ids; that is correct, and each is stable. Any other `version` value is a 400. `notify: true` also hands it to the project’s webhooks as `world.script_approved`, carrying identifiers, lineage and `contractVersion` but never the script itself. Publishes nothing.","security":[{"OwnerToken":[]}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"notify":{"type":"boolean"},"version":{"type":"integer","enum":[1,2],"description":"Export contract. Defaults to 1."}}}}}},"responses":{"200":{"description":"Existing export","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"201":{"description":"Export created","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/projects/{projectId}/environments/{envId}/instances/{worldInstanceId}/script-drafts/{draftId}/editorial/{editorialId}/approved-export/{exportId}":{"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"},"description":"The project id."},{"name":"envId","in":"path","required":true,"schema":{"type":"string"},"description":"The world (environment) id — env_…."},{"name":"worldInstanceId","in":"path","required":true,"schema":{"type":"string"},"description":"The world instance — one user’s story."},{"name":"draftId","in":"path","required":true,"schema":{"type":"string"},"description":"The evidence draft id."},{"name":"editorialId","in":"path","required":true,"schema":{"type":"string"},"description":"The editorial draft id."},{"name":"exportId","in":"path","required":true,"schema":{"type":"string"},"description":"The approved export id — a content digest."}],"get":{"summary":"Read ONE approved export, SCRIPT INCLUDED. The collection GET beside this lists exports without their bodies; this is the only read that returns one. Mirrored on the admin key at the matching `/v1/admin/...` path, so an unattended backend can fetch an approved script without a browser anywhere in its deployment — every field it needs is in the `world.script_approved` webhook (`exportId`, and `environmentId` / `worldInstanceId` / `evidenceDraftId` / `editorialDraftId` inside `lineage`). It READS only: minting an export is a reviewer signing off on a specific text and stays on the owner plane. The body cannot change — `exportId` is its content digest. Narrow `content.contractVersion` before reading version-specific fields. Any miss — wrong project, environment, instance, draft, editorial or id — answers the SAME 404, so refusals cannot be used to map which objects exist. Served `Cache-Control: no-store`.","security":[{"OwnerToken":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/projects/{projectId}/environments/{envId}/instances/{worldInstanceId}/script-drafts/{draftId}/editorial/{editorialId}/approved-export/candidate":{"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"},"description":"The project id."},{"name":"envId","in":"path","required":true,"schema":{"type":"string"},"description":"The world (environment) id — env_…."},{"name":"worldInstanceId","in":"path","required":true,"schema":{"type":"string"},"description":"The world instance — one user’s story."},{"name":"draftId","in":"path","required":true,"schema":{"type":"string"},"description":"The evidence draft id."},{"name":"editorialId","in":"path","required":true,"schema":{"type":"string"},"description":"The editorial draft id."}],"post":{"summary":"Derive the next Story Package as a CANDIDATE from a VERSION 1 approved export (`?exportId=`). A version 2 export is refused with 422: the candidate is the one path from an export into published canon, and which V2 fields may become canon has not been decided — so export the approval as version 1 and derive from that. Validated against the same validator the publish route runs, then returned — this writes no package, and the story-package plane holds published revisions only, so a candidate becomes a package exactly when a person submits it. Only evidence-origin material becomes canon: a model’s connective line does not arrive as an established fact, and a voice that only ever spoke in suggested lines does not become a role. `droppedSuggestions` reports what was left behind. The source package is carried as provenance and never overwritten.","security":[{"OwnerToken":[]}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"summary":{"type":"string"}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/projects/{projectId}/environments/{envId}/instances/{worldInstanceId}/deliveries/{deliveryId}/rehydrate":{"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"},"description":"The project id."},{"name":"envId","in":"path","required":true,"schema":{"type":"string"},"description":"The world (environment) id — env_…."},{"name":"worldInstanceId","in":"path","required":true,"schema":{"type":"string"},"description":"The world instance — one user’s story."},{"name":"deliveryId","in":"path","required":true,"schema":{"type":"string"},"description":"The delivery id."}],"post":{"summary":"Rebuild a dead letter whose text was redacted, FROM THE RECORD — the committed ledger entry, or the verified archive segment holding it once the live rows are pruned. Takes no body: nobody types a replacement line and no model regenerates one. Accepted only if the rebuild re-derives the same delivery id AND the same content digest captured before the text was destroyed. A refusal carries a code (source_missing, archive_unavailable, hash_mismatch, ledger_drift, identity_mismatch) and leaves the row `unrecoverable`, which still blocks the session. A storage fault while reading the record or writing the outcome is not a refusal: it answers 503 `unavailable` and puts the row back to `dead`, so a retry runs the same rebuild. A row left `rehydrating` (a rebuild that did not finish) is accepted as well. Note: the turn log’s other half (the prompt envelope the role answered) is not in the ledger and is not reconstructed.","security":[{"OwnerToken":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/projects/{projectId}/environments/{envId}/instances/{worldInstanceId}/deliveries/{deliveryId}/resolve-gap":{"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"},"description":"The project id."},{"name":"envId","in":"path","required":true,"schema":{"type":"string"},"description":"The world (environment) id — env_…."},{"name":"worldInstanceId","in":"path","required":true,"schema":{"type":"string"},"description":"The world instance — one user’s story."},{"name":"deliveryId","in":"path","required":true,"schema":{"type":"string"},"description":"The delivery id."}],"post":{"summary":"Account for a beat that cannot be recovered. The only exit from `unrecoverable`, and it takes NO text — the notice’s wording comes from a fixed server-side catalogue and is rendered in the reader’s language by their client. It creates a notice delivery that rides the same at-least-once queue, positioned where the missing beat was; the original becomes `resolved_gap` when that notice LANDS, not when you call this. Not a discard: it makes “content was lost here” a user-visible, audited fact. Writes no world state, touches no canon, deletes no dead-letter audit.","security":[{"OwnerToken":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/projects/{projectId}/environments/{envId}/instances/{worldInstanceId}/metrics":{"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"},"description":"The project id."},{"name":"envId","in":"path","required":true,"schema":{"type":"string"},"description":"The world (environment) id — env_…."},{"name":"worldInstanceId","in":"path","required":true,"schema":{"type":"string"},"description":"The world instance — one user’s story."}],"get":{"summary":"Operator metrics for one instance, in two families kept deliberately apart: DELIVERY (outbox depth by status, retry pressure, dead letters, commit→delivered latency) answers “did the lines arrive”, and TURN (proposals refused, repair-eligible turns, fallback beats, load across the cast) answers “did the world play well”. Every number is derived from records that already exist, and every scan reports whether it was cut short.","security":[{"OwnerToken":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/projects/{projectId}/environments/{envId}/instances/{worldInstanceId}/eval":{"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"},"description":"The project id."},{"name":"envId","in":"path","required":true,"schema":{"type":"string"},"description":"The world (environment) id — env_…."},{"name":"worldInstanceId","in":"path","required":true,"schema":{"type":"string"},"description":"The world instance — one user’s story."}],"post":{"summary":"Score this instance’s committed history against a quality suite — `drama` or `npc`. Deterministic: no model judges the output. A metric with nothing to measure reports not-applicable rather than scoring zero. Read-only; it replays nothing and advances nothing.","security":[{"OwnerToken":[]}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"suite":{"type":"string"},"replayVerdict":{"type":"string"}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/projects/{projectId}/environments/{envId}/instances/{worldInstanceId}/ledger/archive":{"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"},"description":"The project id."},{"name":"envId","in":"path","required":true,"schema":{"type":"string"},"description":"The world (environment) id — env_…."},{"name":"worldInstanceId","in":"path","required":true,"schema":{"type":"string"},"description":"The world instance — one user’s story."}],"post":{"summary":"Ledger archival. `action` is one of plan (the default — what archiving WOULD copy; writes nothing), execute (copy the replay-verified range into immutable hash-chained segments; creates only, deletes nothing), verify (recompute the archive chain end to end), prune (delete the LIVE rows a verified, retained archive already holds — the only operation in the world plane that destroys a row). execute and prune both require confirm:true, and prune additionally requires the archive to verify and to have outlived its retention. Nothing calls prune on a schedule. A drift is reported, never repaired.","security":[{"OwnerToken":[]}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string"},"confirm":{"type":"boolean"}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/projects/{projectId}/environments/{envId}/instances/{worldInstanceId}/script-drafts":{"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"},"description":"The project id."},{"name":"envId","in":"path","required":true,"schema":{"type":"string"},"description":"The world (environment) id — env_…."},{"name":"worldInstanceId","in":"path","required":true,"schema":{"type":"string"},"description":"The world instance — one user’s story."}],"get":{"summary":"List this instance’s script drafts — metadata only, never the draft bodies.","security":[{"OwnerToken":[]},{"SecretKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"drafts":{"type":"array","items":{"type":"object","additionalProperties":true}}}}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}},"post":{"summary":"Generate (or return) the draft for a COMMITTED range: beats, delivered lines, state changes, refused attempts as conflicts, cast, branches reached, unresolved objectives — every line traceable to a turnId. Deterministic and idempotent on the ledger range: the same range is the same draft. WHOLE or not at all — a draft’s ledgerRange is a claim everything downstream inherits, so a history too long to read at once is refused rather than drafted from its tail. Default scope is the whole INSTANCE; pass `episodeRunId` to scope it to ONE ended episode, whose range comes from that episode’s own terminal record rather than from the caller. That is what keeps a serial worldline draftable after it outgrows the instance-wide cap.","security":[{"OwnerToken":[]},{"SecretKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"episodeRunId":{"type":"string","description":"Optional. Scope the draft to one ENDED episode. A running episode has no range to draft yet and answers 404 `episode_run_not_found`."}}}}}},"responses":{"200":{"description":"Existing draft","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"201":{"description":"Draft created","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/projects/{projectId}/environments/{envId}/instances/{worldInstanceId}/script-drafts/{draftId}":{"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"},"description":"The project id."},{"name":"envId","in":"path","required":true,"schema":{"type":"string"},"description":"The world (environment) id — env_…."},{"name":"worldInstanceId","in":"path","required":true,"schema":{"type":"string"},"description":"The world instance — one user’s story."},{"name":"draftId","in":"path","required":true,"schema":{"type":"string"},"description":"The draft id."}],"get":{"summary":"One draft, body and all. Reading is not exporting.","security":[{"OwnerToken":[]},{"SecretKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/projects/{projectId}/environments/{envId}/instances/{worldInstanceId}/script-drafts/{draftId}/review":{"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"},"description":"The project id."},{"name":"envId","in":"path","required":true,"schema":{"type":"string"},"description":"The world (environment) id — env_…."},{"name":"worldInstanceId","in":"path","required":true,"schema":{"type":"string"},"description":"The world instance — one user’s story."},{"name":"draftId","in":"path","required":true,"schema":{"type":"string"},"description":"The draft id."}],"post":{"summary":"Mark a draft reviewed. The reviewer is the signed-in human whose token this call carries — never a field in the body. Reviewing twice keeps the FIRST reviewer.","security":[{"OwnerToken":[]}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"note":{"type":"string"}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/projects/{projectId}/environments/{envId}/instances/{worldInstanceId}/script-drafts/{draftId}/export":{"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"},"description":"The project id."},{"name":"envId","in":"path","required":true,"schema":{"type":"string"},"description":"The world (environment) id — env_…."},{"name":"worldInstanceId","in":"path","required":true,"schema":{"type":"string"},"description":"The world instance — one user’s story."},{"name":"draftId","in":"path","required":true,"schema":{"type":"string"},"description":"The draft id."}],"get":{"summary":"The reviewed draft as standard JSON. Refuses with 409 until a human has reviewed it — humanReviewRequired is part of the content, and this is where it has teeth.","security":[{"OwnerToken":[]},{"SecretKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/projects/{projectId}/environments/{envId}/instances/{worldInstanceId}/script-drafts/{draftId}/editorial":{"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"},"description":"The project id."},{"name":"envId","in":"path","required":true,"schema":{"type":"string"},"description":"The world (environment) id — env_…."},{"name":"worldInstanceId","in":"path","required":true,"schema":{"type":"string"},"description":"The world instance — one user’s story."},{"name":"draftId","in":"path","required":true,"schema":{"type":"string"},"description":"The draft id."}],"get":{"summary":"List the editorial drafts derived from this evidence draft — metadata, scene counts and suggestion ratios, never the bodies.","security":[{"OwnerToken":[]},{"SecretKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"editorials":{"type":"array","items":{"type":"object","additionalProperties":true}}}}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}},"post":{"summary":"Generate (or return) a model-organized EDITORIAL reading of the evidence draft: scenes, per-scene source turnIds, role and world-state changes. Every line the model claims came from the story is re-checked against the committed record server-side; anything that does not match is recorded as a suggestion. Suggestions are never written back to world canon. Idempotent per model + prompt version + input digest. The evidence draft is never modified.","security":[{"OwnerToken":[]},{"SecretKey":[]}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"model":{"type":"string"}}}}}},"responses":{"200":{"description":"Existing editorial","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"201":{"description":"Editorial created","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/projects/{projectId}/environments/{envId}/instances/{worldInstanceId}/script-drafts/{draftId}/editorial/{editorialId}":{"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"},"description":"The project id."},{"name":"envId","in":"path","required":true,"schema":{"type":"string"},"description":"The world (environment) id — env_…."},{"name":"worldInstanceId","in":"path","required":true,"schema":{"type":"string"},"description":"The world instance — one user’s story."},{"name":"draftId","in":"path","required":true,"schema":{"type":"string"},"description":"The draft id."},{"name":"editorialId","in":"path","required":true,"schema":{"type":"string"},"description":"The editorial draft id."}],"get":{"summary":"One editorial draft, body and all — scenes, provenance and review state.","security":[{"OwnerToken":[]},{"SecretKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}},"patch":{"summary":"Move an editorial draft along its human review lifecycle: draft → under_review → changes_requested | approved | rejected. The reviewer is the signed-in human whose token this call carries. `exported` is not settable here — the export route writes it.","security":[{"OwnerToken":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string"},"note":{"type":"string"}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/projects/{projectId}/environments/{envId}/instances/{worldInstanceId}/script-drafts/{draftId}/editorial/{editorialId}/scenes":{"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"},"description":"The project id."},{"name":"envId","in":"path","required":true,"schema":{"type":"string"},"description":"The world (environment) id — env_…."},{"name":"worldInstanceId","in":"path","required":true,"schema":{"type":"string"},"description":"The world instance — one user’s story."},{"name":"draftId","in":"path","required":true,"schema":{"type":"string"},"description":"The draft id."},{"name":"editorialId","in":"path","required":true,"schema":{"type":"string"},"description":"The editorial draft id."}],"post":{"summary":"Record one human verdict on one scene: accepted, edited (with the editor’s own text) or rejected. Viewing a scene’s sources and its role / world-state changes is a read of the draft itself — the scene carries both.","security":[{"OwnerToken":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"sceneId":{"type":"string"},"decision":{"type":"string"},"editedText":{"type":"string"},"note":{"type":"string"}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/projects/{projectId}/environments/{envId}/instances/{worldInstanceId}/script-drafts/{draftId}/editorial/{editorialId}/export":{"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"},"description":"The project id."},{"name":"envId","in":"path","required":true,"schema":{"type":"string"},"description":"The world (environment) id — env_…."},{"name":"worldInstanceId","in":"path","required":true,"schema":{"type":"string"},"description":"The world instance — one user’s story."},{"name":"draftId","in":"path","required":true,"schema":{"type":"string"},"description":"The draft id."},{"name":"editorialId","in":"path","required":true,"schema":{"type":"string"},"description":"The editorial draft id."}],"get":{"summary":"Preview the export payload without exporting. Refuses with 409 unless the draft is approved. Pure: it changes no state.","security":[{"OwnerToken":[]},{"SecretKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}},"post":{"summary":"Export an APPROVED editorial draft and stamp it exported. Rejected scenes do not travel; edited scenes travel as the human’s text, marked humanEdited. Nothing here writes to the story package.","security":[{"OwnerToken":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Malformed request — the body did not meet the contract.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential for this lane.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Refused: wrong lane, missing or invalid source signature, or a world/provider that is no longer live. One generic message by design — the specific reason goes to the audit trail.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such project, world, instance, turn or draft. An episode-scoped draft answers 404 with `code: \"episode_run_not_found\"` when no ENDED episode carries that run id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The world disagreed: a dead pin, an actor-runtime world, no selectable role, or a draft that has not been reviewed. Retrying the same request will disagree again. A WORLD session mint (`POST /v1/sessions` with `world`) may answer 409 with `code: \"world_actor_conflict\"` — this role in this world instance is bound to a different agent, registry instance or key environment (test vs live); mint with the ones that first entered it. Editing or promoting the role’s agent, or republishing one of its capabilities, does NOT cause it: that worldline is served on its stored binding (since 1.51.0). `world_revision_mismatch` and `world_provider_mismatch` are a race between two concurrent FIRST mints of one world instance; retry the mint. A Story Package publish may also answer 409 with body `code: \"story_contract_v2_write_disabled\"` — the package carries `initialRelations` but this environment is not yet permitted to write contract v2; the field is never dropped to let the publish succeed as v1. A publish carrying `series` may likewise answer `code: \"story_contract_v3_write_disabled\"` — contract v3, same rule, same refusal to drop the field. A publish declaring a `public` flag may answer `code: \"story_contract_v4_write_disabled\"` — contract v4, same rule. An episode-scoped script draft may answer 409 with `code: \"episode_history_incomplete\"` — the beats that episode declares could not all be read, and a draft would claim a range it does not contain. A turn or a deliberation on a finished episode answers 409 `code: \"episode_ended\"`, and starting the next episode answers 409 with one of `episode_not_ended`, `series_complete` or `not_a_serial` — each names a different thing to go and do. A Story Package CREATION refused by a per-project ceiling answers 409 with `code: \"story_package_limit\"` (the ENABLED ceiling — disable one to free a slot) or `code: \"story_package_documents_limit\"` (the DOCUMENT ceiling, counting disabled ones — delete one that has never been pinned); the codes are the stable observation point, the sentences are for humans. Story Package CREATION may also answer 409 with `code: \"idempotency_key_reused\"` — the supplied `clientRequestId` already created a package with DIFFERENT content; retrying an interrupted creation must re-send the same key AND the same body. Selecting a deliberation candidate may answer 409 with `code: \"stale_deliberation\"` — the world moved since the candidates were produced, and nothing was spent or written; deliberate again. A deliberation may also answer 409 with one of `deliberation_disabled` (not switched on in this environment), `deliberation_uncalibrated` (the token/latency budget has not been measured for this deployment), `deliberation_not_enabled` (this world’s published revision does not declare it) or `deliberation_candidates_exceeded` — each names a different thing to go and do, and all four are refused before any model call. A world session mint for a role that has left the instance answers 409 `code: \"role_departed\"` (since 1.58.0). Reading a story package (`GET …/story-packages/{packageId}`) whose current revision cannot be served answers 409 with `code: \"story_unsupported_contract\"` (this deployment is older than the revision — deploy), `story_hash_mismatch` or `story_contract_content_mismatch` (the stored revision is damaged — publish again) or `story_revision_missing` (the head points at nothing readable — publish again), since 1.57.0; only a missing package is a 404, and a store fault is a 503 `unavailable`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds the endpoint cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"The request was understood and its effects were refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limited — the response carries Retry-After.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Storage or a dependency was unavailable. Retry the same request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}}}}