{"description":"Timepoint Beta API as MCP-shaped tools — one tool per HTTP operation, generated from openapi.json (the same utoipa-annotated handlers). Auth: every /v1 tool needs `Authorization: Bearer tpb_…`.","name":"timepoint-beta","note":"The MCP endpoint is POST /mcp (streamable HTTP, JSON-RPC 2.0, with the same bearer): MCP 2026-07-28 statelessly (server/discover, tools/list, tools/call), and 2025-11-25 / 2025-06-18 through initialize. Its tools are these /v1 operations, less the event stream and the multipart upload, and tools/list shows only what the caller's token may call. Each tool is also the HTTP operation named by its method + path.","spec":{"openapi":"3.1.0","title":"Timepoint Beta API","version":"0.1.0"},"tools":[{"description":"Liveness + honest degraded-mode signals. No auth. — Liveness probe plus the honest degraded-mode signals: `ok`/`db` (database reachability), `version`, `balance_gate` (the LLM admission gate's label: ok / no_key / no_headroom / budget_exhausted / unknown), and `account_headroom_usd` (a number only when a management key is configured AND the credits endpoint answered; null is a LABELED absence, never a fabricated 0).","inputSchema":{"properties":{},"required":[],"type":"object"},"method":"GET","name":"health","path":"/health","requires_auth":false},{"description":"Free meeting previews per account — How many free meeting previews a new account gets, and whether they are open today.","inputSchema":{"properties":{},"required":[],"type":"object"},"method":"GET","name":"public_free_previews","path":"/public/v1/free-previews","requires_auth":false},{"description":"Public home page document — The documents the public home page renders.","inputSchema":{"properties":{},"required":[],"type":"object"},"method":"GET","name":"public_home","path":"/public/v1/home","requires_auth":false},{"description":"Read a Gallery moment — One published Gallery moment.","inputSchema":{"properties":{"slug":{"description":"A moment slug.","type":"string"}},"required":["slug"],"type":"object"},"method":"GET","name":"public_get_moment","path":"/public/v1/moments/{slug}","requires_auth":false},{"description":"Recent published items — Routes, no-routes, moments and sealed forecasts, newest first (at most 50). The MCP tool list_published serves the same list with an absolute `url` per item.","inputSchema":{"properties":{},"required":[],"type":"object"},"method":"GET","name":"public_news","path":"/public/v1/news","requires_auth":false},{"description":"Read a published no-route — A published walk-back that found no route.","inputSchema":{"properties":{"id":{"description":"A no-route id (`nr_…`).","type":"string"}},"required":["id"],"type":"object"},"method":"GET","name":"public_get_no_route","path":"/public/v1/no-routes/{id}","requires_auth":false},{"description":"List live Play challenges — The live public challenges. Start one with POST /v1/play/attempts (`challenge_id`) or the MCP tool play_start (`slug`). Cached for a minute.","inputSchema":{"properties":{},"required":[],"type":"object"},"method":"GET","name":"public_list_play_challenges","path":"/public/v1/play/challenges","requires_auth":false},{"description":"Read a live Play challenge — One live public challenge with its leaderboard. Cached for a minute.","inputSchema":{"properties":{"slug":{"description":"A challenge slug.","type":"string"}},"required":["slug"],"type":"object"},"method":"GET","name":"public_get_play_challenge","path":"/public/v1/play/challenges/{slug}","requires_auth":false},{"description":"Read a Play leaderboard — Best finished attempt per player who chose a handle. Cached for a minute.","inputSchema":{"properties":{"slug":{"description":"A challenge slug.","type":"string"}},"required":["slug"],"type":"object"},"method":"GET","name":"public_get_play_leaderboard","path":"/public/v1/play/challenges/{slug}/leaderboard","requires_auth":false},{"description":"Read a published route — A published walk-back that found a route.","inputSchema":{"properties":{"id":{"description":"A route id (`rt_…`).","type":"string"}},"required":["id"],"type":"object"},"method":"GET","name":"public_get_route","path":"/public/v1/routes/{id}","requires_auth":false},{"description":"List sealed forecasts — Every public seal: hash, timestamp proofs, probability and settlement. The MCP tool list_seals serves the same list.","inputSchema":{"properties":{},"required":[],"type":"object"},"method":"GET","name":"public_list_seals","path":"/public/v1/seals","requires_auth":false},{"description":"Read one sealed forecast — One public seal.","inputSchema":{"properties":{"id":{"description":"A seal id (`seal_…`).","type":"string"}},"required":["id"],"type":"object"},"method":"GET","name":"public_get_seal","path":"/public/v1/seals/{id}","requires_auth":false},{"description":"A seal's generated frame image — One generated frame as PNG (`<t>.png`, from the media document's `url`).","inputSchema":{"properties":{"file":{"description":"`<t>.png`.","type":"string"},"id":{"description":"A seal id (`seal_…`).","type":"string"}},"required":["id","file"],"type":"object"},"method":"GET","name":"public_get_seal_frame","path":"/public/v1/seals/{id}/frames/{file}","requires_auth":false},{"description":"A seal's generated frames and charts — Generated frames and simulated charts for a seal, each labelled as generated.","inputSchema":{"properties":{"id":{"description":"A seal id (`seal_…`).","type":"string"}},"required":["id"],"type":"object"},"method":"GET","name":"public_get_seal_media","path":"/public/v1/seals/{id}/media","requires_auth":false},{"description":"Download a seal's OpenTimestamps proof — A detached OpenTimestamps proof, served unchanged so `ots verify` can check it against the seal's hash.","inputSchema":{"properties":{"file":{"description":"`a.ots` or `b.ots` (one per calendar).","type":"string"},"id":{"description":"A seal id (`seal_…`).","type":"string"}},"required":["id","file"],"type":"object"},"method":"GET","name":"public_get_seal_proof","path":"/public/v1/seals/{id}/proof/{file}","requires_auth":false},{"description":"GET /v1/account/context","inputSchema":{"properties":{},"required":[],"type":"object"},"method":"GET","name":"get_context","path":"/v1/account/context","requires_auth":true},{"description":"POST /v1/account/onboarding","inputSchema":{"properties":{"body":{"description":"The JSON request body.","properties":{"company":{"type":"string"},"goals":{"type":"string"},"role":{"type":"string"},"what":{"type":"string"},"work":{"type":"string"},"you":{"type":"string"}},"required":["you","role","work","company","what","goals"],"type":"object"}},"required":["body"],"type":"object"},"method":"POST","name":"post_onboarding","path":"/v1/account/onboarding","requires_auth":true},{"description":"The caller's one pending card. — The caller's single pending gentle-action card (CONTRACTS §11.11 B3.5): `{action: {id, run_id, text, basis, branches: 1, window, window_end, entity_path, created_at} | null}` — at most ONE, newest first; `null` is the honest nothing-pending state.","inputSchema":{"properties":{},"required":[],"type":"object"},"method":"GET","name":"get_pending_action","path":"/v1/actions/pending","requires_auth":true},{"description":"Decide the card: owner-only, pending-only, choice-typed. — Decide a pending gentle-action card (CONTRACTS §11.11 B3.5). OWNER-ONLY and PENDING-ONLY. A card that is not yours answers exactly as an id that does not exist does — 404, same body, same wording — so this route cannot be used to find out which card ids are real. 409 is the caller's OWN card in the wrong state. `did` mints a `user_attested` GROUNDED moment (real tt-core envelope, participants = the entity's registry path, t=1 — the schema floor; provenance source is the attestation marker) + a participation edge, records `moment_id` on the card, then attempts a NORMAL classification (a closed LLM gate leaves the moment honestly untagged, never fails the attestation). `not_now` extends the window 3 days and returns to pending after 24h (worker sweep). `not_this` is terminal — the training signal is the state row.","inputSchema":{"properties":{"body":{"description":"Decide request: the three-verb loop plus an optional margin note.","properties":{"choice":{"description":"`did` | `not_now` | `not_this`.","example":"did","type":"string"},"note":{"description":"Optional note (≤2000 chars). Persisted only on `did` (inside the minted\nmoment's payload, outside the content hash) — the 0012 schema has no\nnote column; other choices acknowledge + log it, stated.","type":["string","null"]}},"required":["choice"],"type":"object"},"id":{"description":"Action id","format":"uuid","type":"string"}},"required":["id","body"],"type":"object"},"method":"POST","name":"decide_action","path":"/v1/actions/{id}/decide","requires_auth":true},{"description":"Admin only, and only a person's own sign-in (email link or Google login) — an agent or API token gets `person_sign_in_required`: agents never attest (PLAN.md policy 9: one person's re-authenticated attestation over named items, each with its hash). Records one batch: an action (`ack_flags`, `pass`, `request_changes`, `deliver`; `urgent` = acknowledge every open flag, pass and deliver, owner only) over 1 to 50 items of ONE client, each with the hash the reader read (must be current). `read` must be true. `note` (the person's own, at most 2000 characters, not a placeholder, never one they recorded before) is required for `ack_flags` and `request_changes`. Re-authentication: a sign-in at most 300 seconds old, or `phrase` = the preview's confirmation phrase (case- and whitespace-insensitive). Every item runs through its single-item route's own checks — the run's own gate, its scenario contract and the pre-QA lint still bind every pass and delivery, in urgent mode too — in ONE transaction, each in a savepoint; any refusal rolls everything back and names every failing item. Writes one `run.attestation_batches` row (with a factual note naming every item and the first 12 characters of its hash) and each item's own rows linked by `batch_id` (and `urgent`), each with a note composed for that item. Never approves staged actions, cast additions or research proposals.","inputSchema":{"properties":{"body":{"description":"The JSON request body.","properties":{"action":{"description":"`ack_flags`, `pass`, `request_changes`, `deliver` or `urgent`.","type":"string"},"items":{"description":"1 to 50 items of one client, no duplicates, each with its hash.","items":{"description":"One item named in a batch attestation, with the hash the reader read.","properties":{"hash":{"description":"The hash the reader read, as the preview showed it: a run's evidence\nhash, a file's `review_sha256`, a wrap-up's current snapshot hash.\nMust be current.","type":["string","null"]},"id":{"type":"string"},"kind":{"description":"`run` (id = run id), `file` (id = file id) or `wrapup` (id = engagement id).","type":"string"}},"required":["kind","id"],"type":"object"},"type":"array"},"note":{"description":"The person's own note (at most 2000 characters, not a placeholder, not\none they recorded before). Required for `ack_flags` and\n`request_changes`.","type":["string","null"]},"phrase":{"description":"The batch's confirmation phrase (from the preview), when the sign-in is\nmore than five minutes old.","type":["string","null"]},"read":{"description":"Must be `true`: the person read every item.","type":["boolean","null"]}},"required":["action"],"type":"object"}},"required":["body"],"type":"object"},"method":"POST","name":"record_attestation_batch","path":"/v1/admin/attestations","requires_auth":true},{"description":"Admin only (any admin caller; it records nothing, but it runs the pre-QA check of a draft or returned run that has none for its current version, R3). Names a batch — an action (`ack_flags`, `pass`, `request_changes`, `deliver`, `urgent`) over 1 to 50 items of ONE client (`run` = run id, `file` = file id, `wrapup` = engagement id; `ack_flags` is runs only) — and answers, per item, its short title, state, grade (runs), open flags (runs), the hash a reader attests (a run's evidence hash, a file's `review_sha256`, a wrap-up's current snapshot hash) and whether the action would succeed now for a person signed in now: each item is run through its single-item route's own checks inside a transaction that is rolled back (`problem` is that route's `{error, reason}`). Also the confirmation `phrase` (an intent check, not a secret), the caller's `step_up` state (a sign-in at most 300 seconds old re-authenticates; otherwise type the phrase), `note_required` (ack_flags and request_changes) and `owner` (the caller may use urgent mode: an administrator signed in as a person, not an agent). A token may preview; only a person records (`record_attestation_batch`).","inputSchema":{"properties":{"body":{"description":"The JSON request body.","properties":{"action":{"description":"`ack_flags`, `pass`, `request_changes`, `deliver` or `urgent`.","type":"string"},"items":{"description":"1 to 50 items of one client, no duplicates; `ack_flags` names runs only.","items":{"description":"One item named in a batch preview.","properties":{"id":{"type":"string"},"kind":{"description":"`run` (id = run id), `file` (id = file id) or `wrapup` (id = engagement id).","type":"string"}},"required":["kind","id"],"type":"object"},"type":"array"}},"required":["action"],"type":"object"}},"required":["body"],"type":"object"},"method":"POST","name":"preview_attestation_batch","path":"/v1/admin/attestations/preview","requires_auth":true},{"description":"Admin only. Every non-merged client record: owner id, display name (falling back to the note, then \"Unnamed client\"), note, consent to be named, and its sign-in email addresses.","inputSchema":{"properties":{},"required":[],"type":"object"},"method":"GET","name":"list_clients","path":"/v1/admin/clients","requires_auth":true},{"description":"Admin only. Create or update a client record by `owner_id`: an absent field leaves the stored value unchanged, and `consent_to_name` (to be NAMED — marketing labels etc) starts FALSE for a brand-new client and is only ever raised by an explicit write here. `note` and `display_name` pass the entity-language lint (PLAN.md policy 2); a rejection is a 422, a milder flag is returned in `language_flags` and the write still lands.","inputSchema":{"properties":{"body":{"description":"The JSON request body.","properties":{"consent_to_name":{"description":"Consent to be NAMED (marketing labels etc). Absent = leave as stored;\na brand-new client without it starts FALSE (the schema default).","type":["boolean","null"]},"display_name":{"type":["string","null"]},"note":{"type":["string","null"]},"owner_id":{"type":"string"}},"required":["owner_id"],"type":"object"}},"required":["body"],"type":"object"},"method":"POST","name":"upsert_client","path":"/v1/admin/clients","requires_auth":true},{"description":"Attach a sign-in address to an existing client. Never moves an address that\nanother account already holds. — Admin only. Attach a sign-in address to an existing client. Never moves an address away from another account that already holds it.","inputSchema":{"properties":{"body":{"description":"The JSON request body.","properties":{"email":{"type":"string"},"owner_id":{"type":"string"}},"required":["owner_id","email"],"type":"object"}},"required":["body"],"type":"object"},"method":"POST","name":"attach_client_email","path":"/v1/admin/clients/email","requires_auth":true},{"description":"`POST /v1/admin/clients/merge` — fold a duplicate client row into another.\nEngagements, reviews and wrap-ups move to `into`; `from` is marked merged\n(kept for the audit trail, hidden from the client list). Consent to be\nnamed is the stricter of the two; `from`'s note is carried over. Each moved\nreview gets a `client_merged` history row. — Admin only. Fold a duplicate client row (`from`) into another (`into`): their engagements, reviews and wrap-ups move to `into`, consent to be named becomes the stricter of the two, and `from`'s note is carried over. `from` is marked merged (kept for the audit trail, hidden from `list_clients`) — never deleted. A person's browser sign-in merges directly; any token, an operator's included, gets 403 `stage_required` and stages `merge_clients` instead.","inputSchema":{"properties":{"body":{"description":"The JSON request body.","properties":{"from":{"description":"The duplicate row to retire.","type":"string"},"into":{"description":"The row that survives (usually the client's login identity).","type":"string"}},"required":["from","into"],"type":"object"}},"required":["body"],"type":"object"},"method":"POST","name":"merge_clients","path":"/v1/admin/clients/merge","requires_auth":true},{"description":"Admin only. Create a brand-new client identity (`client:<uuid>`) with a fresh API token and no starter credits (D3: a client logs in to READ what was delivered to it — the console, `list_delivered_results`, the read-delivered MCP scope — never to start a paid simulation on its own token). Optionally attaches a sign-in email. `note`/`display_name` pass the entity-language lint like `upsert_client`. The response carries the token once and is sent `Cache-Control: no-store`.","inputSchema":{"properties":{"Idempotency-Key":{"description":"M4: a caller-chosen key (≤200 chars). The same caller + key + method/path/body within 24h replays the first response, TOKEN INCLUDED (header idempotent-replay: true), instead of minting a second client; the same key with a different request answers 422 idempotency_key_reused; a concurrent duplicate answers 409 idempotency_in_progress (retryable). Absent = ordinary behavior — each call mints a new client.","type":["string","null"]},"body":{"description":"The JSON request body.","properties":{"display_name":{"type":"string"},"email":{"description":"Optional sign-in address. Stored on the identity so email links (and a\nlater Google sign-in) reach this account.","type":["string","null"]},"note":{"type":["string","null"]}},"required":["display_name"],"type":"object"}},"required":["body"],"type":"object"},"method":"POST","name":"provision_client","path":"/v1/admin/clients/provision","requires_auth":true},{"description":"Mail a sign-in link to every address on file for a client. — Admin only. Mail a sign-in link to every address on file for a client.","inputSchema":{"properties":{"body":{"description":"The JSON request body.","properties":{"owner_id":{"type":"string"}},"required":["owner_id"],"type":"object"}},"required":["body"],"type":"object"},"method":"POST","name":"send_client_login","path":"/v1/admin/clients/send-login","requires_auth":true},{"description":"`GET /v1/admin/clients/{owner}/engagements/{id}/grant-book` — the client\nview built for a named identity (admin): the record preview's grant book. — Admin only. The grant book exactly as the named identity reads it (`get_my_engagement_grant_book` built for them): the record preview's grant book.","inputSchema":{"properties":{"id":{"description":"Engagement id","format":"uuid","type":"string"},"owner":{"description":"The client identity","type":"string"}},"required":["owner","id"],"type":"object"},"method":"GET","name":"get_client_engagement_grant_book","path":"/v1/admin/clients/{owner}/engagements/{id}/grant-book","requires_auth":true},{"description":"`GET /v1/admin/clients/{owner}/engagements/{id}/record` — the same\ndocument for a named identity (admin only): what that client sees. — Admin only. The engagement record exactly as the named identity reads it (`get_engagement_record` built for them): the service desk's preview of a client's page.","inputSchema":{"properties":{"id":{"description":"Engagement id","format":"uuid","type":"string"},"owner":{"description":"The client identity","type":"string"}},"required":["owner","id"],"type":"object"},"method":"GET","name":"get_client_engagement_record","path":"/v1/admin/clients/{owner}/engagements/{id}/record","requires_auth":true},{"description":"GET /v1/admin/clients/{owner}/world-model","inputSchema":{"properties":{"cursor":{"format":"uuid","type":"string"},"engagement_scope":{"description":"in or all; client projections always use in. Only explicit scope decisions omit research.","type":"string"},"include_generated":{"type":"boolean"},"kind":{"type":"string"},"limit":{"minimum":0,"type":"integer"},"owner":{"type":"string"},"part":{"description":"entities (default), facts, or relationships; iterate each to inventory the entire model.","type":"string"},"q":{"type":"string"},"updated_since":{"format":"date-time","type":"string"},"x-timepoint-workspace":{"format":"uuid","type":["string","null"]}},"required":["owner"],"type":"object"},"method":"GET","name":"get_client_world_model","path":"/v1/admin/clients/{owner}/world-model","requires_auth":true},{"description":"Admin only. Every engagement, newest first: id, client, question, side, whether it is live, and its linked workspace (if any).","inputSchema":{"properties":{},"required":[],"type":"object"},"method":"GET","name":"list_engagements","path":"/v1/admin/engagements","requires_auth":true},{"description":"Admin only. Open an engagement claiming one side of a question for a client — THE CONFLICT CHECK: a second live intake on the same `question_key` (normalized to lowercase, collapsed whitespace) is a 409, race-safe because the database's unique index is the arbiter, not the code. `workspace_id` (W3, gate G7) links the engagement to a workspace up front so its delivered result reaches that workspace's members too; `client_owner` must already be a `client` member of that workspace.","inputSchema":{"properties":{"Idempotency-Key":{"description":"M4: a caller-chosen key (≤200 chars). The same caller + key + method/path/body within 24h replays the first response (header idempotent-replay: true) instead of re-running; the same key with a different request answers 422 idempotency_key_reused; a concurrent duplicate answers 409 idempotency_in_progress (retryable). Absent = ordinary behavior.","type":["string","null"]},"body":{"description":"The JSON request body.","properties":{"client_owner":{"type":"string"},"question_key":{"type":"string"},"side":{"type":"string"},"workspace_id":{"description":"W3 (gate G7): link this engagement to a workspace, so its delivered\nresult becomes visible to that workspace's providers and admin, not\nonly to `client_owner`. Optional and additive: omit it and the\nengagement behaves exactly as before workspaces existed. When given,\n`client_owner` must already hold a `client` row in that workspace —\nthe 0055 exclusivity trigger guarantees that is the ONLY workspace\nthey hold a client row in, so this never lets one engagement point at\na workspace the client does not belong to.","format":"uuid","type":["string","null"]}},"required":["client_owner","question_key","side"],"type":"object"}},"required":["body"],"type":"object"},"method":"POST","name":"open_engagement","path":"/v1/admin/engagements","requires_auth":true},{"description":"Admin only. Close a live engagement (`live = false`): its question is no longer claimed, so a new engagement may intake it. Closing changes no review, and delivered results stay readable. After a close, the engagement's reviews can no longer be taken in or retitled (`intake_review`), QA'd (`preliminary_qa`, `qa_review`) or delivered (`deliver_review`): those answer 404 `no_engagement` (410 `withdrawn` for a withdrawn review), and `deliver_all` delivers none of them, listing each under `skipped`. Its wrap-ups and files can still be QA'd and delivered. Close once the engagement's reviews are delivered.","inputSchema":{"properties":{"id":{"description":"Engagement id","format":"uuid","type":"string"}},"required":["id"],"type":"object"},"method":"POST","name":"close_engagement","path":"/v1/admin/engagements/{id}/close","requires_auth":true},{"description":"Admin only. Stage a client deck: a PowerPoint (.pptx) report the client-deck skill built from this engagement's delivered work, with the audit it wrote beside it. The audit must be `timepoint-deck-audit/1` and name these exact bytes (`deck.sha256`), this file name, this engagement and its client. Every claim needs a source (a run artifact, the wrap-up, a file or a service record), and every run, wrap-up, file and grant proposal it cites, and every run in the deck's scope, must belong to this engagement. The deck is filed under `Report decks` and is STAGED: a person passes its QA (`qa_file`, their own sign-in) and delivers it (`deliver_file`) before the client can download it; delivery also needs every run in its scope delivered. Staging the same name again replaces a staged or withdrawn deck, never a delivered one.","inputSchema":{"properties":{"body":{"description":"The JSON request body.","properties":{"audit":{"description":"The audit the skill wrote beside the deck (`timepoint-deck-audit/1`): `deck` (its sha256,\nfile name, pages, length and audience modes, generation time and identity, release,\nengagement, client and scope) and `claims` (each with `key`, `slides` and `source`).","type":"object"},"content_base64":{"description":"The deck's bytes, base64 (standard alphabet). At most 10 MB decoded.","type":"string"},"name":{"description":"The file name the skill wrote: `client-<id prefix>-<date>-<length>-<audience>.pptx`.\n1-160 characters, ends in `.pptx`, no slashes, not hidden, never the client's name.","type":"string"},"title":{"description":"What the client reads in the list (optional, at most 200 characters).","type":["string","null"]}},"required":["name","content_base64","audit"],"type":"object"},"id":{"description":"Engagement id","format":"uuid","type":"string"}},"required":["id","body"],"type":"object"},"method":"POST","name":"stage_deck","path":"/v1/admin/engagements/{id}/decks","requires_auth":true},{"description":"DELIVER ALL. Every review of one engagement that can be delivered, in one\ncall, by a person: the two checks, per item (PLAN.md, 2026-10-13). — Admin only, a person's own sign-in. Delivers every review of this engagement that is not withdrawn or already delivered and whose run is complete with a document, passing each in the same call where no pass is on record (as `deliver_review` does). Answers `{engagement_id, delivered: [run_id], skipped: [{run_id, state, reason}]}`; a review that cannot be delivered is skipped with its reason, never a refusal of the whole call.","inputSchema":{"properties":{"id":{"description":"Engagement id","format":"uuid","type":"string"}},"required":["id"],"type":"object"},"method":"POST","name":"deliver_all","path":"/v1/admin/engagements/{id}/deliver-all","requires_auth":true},{"description":"`GET /v1/admin/engagements/{id}/entities` — R4 roll-up over every\nnon-withdrawn review of the engagement. Read-only. — Admin only (an agent token may read it). The entities of every review of the engagement that is not withdrawn, merged by name (case-insensitive) and kind: the origins seen, how many runs list it, whether any run's space holds it, how many runs require it or never show it, and its summed appearances. Read-only.","inputSchema":{"properties":{"id":{"description":"Engagement id","format":"uuid","type":"string"}},"required":["id"],"type":"object"},"method":"GET","name":"get_engagement_entities","path":"/v1/admin/engagements/{id}/entities","requires_auth":true},{"description":"Admin only. Every file staged, delivered or withdrawn for an engagement, with its state. Stored media (`storage: \"object\"`) also carries `bound_run_id`, `bound_evidence_hash`, `provenance`, `duration_seconds`, a film's `poster_file_id` and `captions_file_id`, a companion's `poster_for`/`captions_for` and `detached` (replaced). `review_sha256` is the hash a QA must name: the file's `sha256`, or for a film the composite of the film and its current poster and captions; `review.current` compares against it.","inputSchema":{"properties":{"id":{"description":"Engagement id","format":"uuid","type":"string"}},"required":["id"],"type":"object"},"method":"GET","name":"list_engagement_files","path":"/v1/admin/engagements/{id}/files","requires_auth":true},{"description":"Admin only: a person or an agent's admin token (bearer or `mcp:operator`) may upload. Stage a document for an engagement (or replace it while still staged/withdrawn): base64 content, up to 5 MB, one of text/markdown, text/csv, application/json, text/plain or application/pdf. A text format must be valid UTF-8; a PDF must start with the PDF magic bytes. Staging the same `folder`/`name` again replaces the content UNLESS it is already delivered (withdraw it first). It stays staged until a person delivers it with `deliver_file` (a token stages that delivery with `stage_action`).","inputSchema":{"properties":{"body":{"description":"The JSON request body.","properties":{"content_base64":{"description":"The file's bytes, base64 (standard alphabet).","type":"string"},"content_type":{"description":"One of text/markdown, text/csv, application/json, text/plain, application/pdf.","type":"string"},"folder":{"description":"Subfolder path segment, e.g. \"briefs\" (empty for the root folder).","type":"string"},"name":{"description":"File name within the folder, 1-160 characters, no slashes, not hidden.","type":"string"},"title":{"type":["string","null"]}},"required":["name","content_type","content_base64"],"type":"object"},"id":{"description":"Engagement id","format":"uuid","type":"string"}},"required":["id","body"],"type":"object"},"method":"POST","name":"stage_file","path":"/v1/admin/engagements/{id}/files","requires_auth":true},{"description":"`GET /v1/admin/engagements/{id}/grant-book` — the working view (admin). — Admin only. The engagement's grant book: one row per grant its searches found (the record's find_grants runs: attached to it, or top-level in its linked workspace), de-duplicated by funder and program across searches (`grant_id` is the latest find's proposal id, `also_found_in` the others'), with its decision (approved, rejected, ineligible, waiting), its quick read (the slate's outcome tag: win, loss or unknown, with `award_p` and the top outcome), its deep simulation (status delivered, not_delivered, in_progress or did_not_finish; top outcome, estimate, `result_word` positive, negative, mixed or unresolved, and `the_move`), the next step in a sentence and a `status_word`: found, not eligible, set aside, read: likely win, read: likely loss, simulating, simulated: won or simulated: lost. Every id and result, delivered or not.","inputSchema":{"properties":{"id":{"description":"Engagement id","format":"uuid","type":"string"}},"required":["id"],"type":"object"},"method":"GET","name":"get_engagement_grant_book","path":"/v1/admin/engagements/{id}/grant-book","requires_auth":true},{"description":"LINK an existing engagement to a workspace (client platform D2). An\nengagement opened without `workspace_id` keeps delivery client_owner-only;\nlinking it makes its delivered runs readable by the workspace's members\n([`crate::runs::delivered_review_sql`]) — how a client identity provisioned\nAFTER the work was delivered gets to read it. One-time (a different target\nis 409; the same target is idempotent), admin-only, refused when any\nreviewed run lives outside the workspace, and written to review history. — Admin only. Link an engagement opened without `workspace_id` to a workspace after the fact (client platform D2), so its delivered runs become readable by that workspace's members. One-time: linking the same workspace again is a no-op 200 (`already: true`); a different target is a 409. Refused when any of the engagement's reviewed runs lives outside the target workspace.","inputSchema":{"properties":{"body":{"description":"The JSON request body.","properties":{"workspace_id":{"format":"uuid","type":"string"}},"required":["workspace_id"],"type":"object"},"id":{"description":"Engagement id","format":"uuid","type":"string"}},"required":["id","body"],"type":"object"},"method":"POST","name":"link_engagement_workspace","path":"/v1/admin/engagements/{id}/link","requires_auth":true},{"description":"Admin only: a person or an agent's admin token (bearer or `mcp:operator`) may upload. Start uploading a film (video/mp4, up to 500 MB), a poster (image/png or image/jpeg, up to 10 MB, `poster_for` a film) or captions (text/vtt, up to 10 MB, `captions_for` a film) into an engagement's files. Answers a presigned `put_url` valid for 15 minutes: PUT the exact bytes to it with exactly `put_headers` (the store refuses any other length, type or checksum), then call `complete_media_upload`. A film may name the `run_id` it depicts (a run of this engagement whose QA a person passed): it is bound to that run's approved evidence hash, and delivering it later refuses if the run is not delivered at that hash. `provenance` (the render's record, a JSON object up to 16 KB; hashes as `sha256:<hex>`) must agree with the upload: `run_id`, `approved_evidence_hash` (the run's) and `mp4_sha256` (this file's), is never a sample render (`sample: true`), and gives the film its `duration_seconds`; other keys are kept as they are. Nothing is public; nothing is stored until complete.","inputSchema":{"properties":{"body":{"description":"The JSON request body.","properties":{"captions_for":{"description":"For a text/vtt captions file: the film (file id) it belongs to.","format":"uuid","type":["string","null"]},"content_type":{"description":"One of video/mp4, image/png, image/jpeg, text/vtt.","type":"string"},"folder":{"description":"Folder; default \"Films\".","type":["string","null"]},"name":{"description":"File name within the folder, 1-160 characters, no slashes, not hidden.","type":"string"},"poster_for":{"description":"For a png/jpeg poster: the film (file id) it belongs to.","format":"uuid","type":["string","null"]},"provenance":{"description":"For a film: its render's provenance record (`render.json`), a JSON object of at most 16 KB.","type":["object","null"]},"run_id":{"description":"For a film: the run it depicts (a run of this engagement whose QA a person passed).","format":"uuid","type":["string","null"]},"sha256":{"description":"SHA-256 of the exact bytes, hex.","type":"string"},"size_bytes":{"description":"Exact byte length: at most 500 MB for video/mp4, 10 MB otherwise.","format":"int64","type":"integer"},"title":{"type":["string","null"]}},"required":["name","content_type","size_bytes","sha256"],"type":"object"},"id":{"description":"Engagement id","format":"uuid","type":"string"}},"required":["id","body"],"type":"object"},"method":"POST","name":"create_media_upload","path":"/v1/admin/engagements/{id}/media/uploads","requires_auth":true},{"description":"Admin only: a person or an agent's admin token may complete. Finish an upload: the server asks storage for the object and checks its size and SHA-256 against what `create_media_upload` declared, then stages the file (`storage: \"object\"`, `uploaded_by` the caller) for a person's QA and delivery. A poster or captions file replaces the film's previous one (kept, detached), and the film then needs a new QA read. Idempotent: completing a completed upload answers the same file.","inputSchema":{"properties":{"id":{"description":"Engagement id","format":"uuid","type":"string"},"upload_id":{"description":"Upload id from create_media_upload","format":"uuid","type":"string"}},"required":["id","upload_id"],"type":"object"},"method":"POST","name":"complete_media_upload","path":"/v1/admin/engagements/{id}/media/uploads/{upload_id}/complete","requires_auth":true},{"description":"`POST /v1/admin/engagements/{id}/record-settings` — the engagement's\nrecord switch (admin only). — Admin only. Whether the engagement's record (`get_engagement_record`) lists the runs that were not delivered: ones that did not finish, are still running, or finished and were not delivered. On by default; delivered runs are always listed. Records who changed it and when.","inputSchema":{"properties":{"body":{"description":"The JSON request body.","properties":{"show_unfinished":{"description":"List the runs that were not delivered (did not finish, still running,\nor finished and not delivered) on the client's record. On by default.","type":"boolean"}},"required":["show_unfinished"],"type":"object"},"id":{"description":"Engagement id","format":"uuid","type":"string"}},"required":["id","body"],"type":"object"},"method":"POST","name":"set_engagement_record_settings","path":"/v1/admin/engagements/{id}/record-settings","requires_auth":true},{"description":"GET /v1/admin/engagements/{id}/research-scope","inputSchema":{"properties":{"id":{"format":"uuid","type":"string"}},"required":["id"],"type":"object"},"method":"GET","name":"get_engagement_research_scope","path":"/v1/admin/engagements/{id}/research-scope","requires_auth":true},{"description":"Everything open on one engagement, in one call. Administrators only. `{engagement, reviews: {counts: {draft, qa_passed, qa_failed, delivered}, by_state: {<state>: [{run_id, kind, run_state, phase, reviewed_at, created_at}]}}, live_runs: [{run_id, kind, state, phase, created_at}], live_research_jobs: [{job_id, status, scope_name, kickoff_id, created_at}], open_decisions: [{decision_id, run_id, kind, prompt, expires_at}], pending_proposals: {drilldown: [{job_id, pending}], drilldown_total, kickoffs: [{kickoff_id, title, status}]}, spend: {usd: {exa, openrouter, openrouter_research, openrouter_runs}, runs_estimated_usd, credits: {held, settled}}}`. The engagement's runs are its reviewed runs plus, when it is linked to a workspace, every run admitted in that workspace; research jobs, proposals, kickoffs and research spend come from the linked workspace (none when unlinked). Research spend counts settled actuals plus every reservation not settled, as the pass ledger does.","inputSchema":{"properties":{"id":{"description":"Engagement id","format":"uuid","type":"string"}},"required":["id"],"type":"object"},"method":"GET","name":"engagement_status","path":"/v1/admin/engagements/{id}/status","requires_auth":true},{"description":"Admin only. Read one version of an engagement's wrap-up (default: the current version). `format=json` (default) returns structured data; `format=html`/`format=md` render the document instead (`view=client` previews exactly what the client will read: no spend, no QA notes, no reviewer identities). `version_state` is `draft`, `qa_passed`, `qa_failed`, `delivered` or `superseded`.","inputSchema":{"properties":{"format":{"description":"`json` (default), `html` or `md`.","type":"string"},"id":{"description":"Engagement id","format":"uuid","type":"string"},"version":{"description":"A specific version (default: the current one).","format":"int32","type":"integer"},"view":{"description":"`client` renders exactly what the client reads.","type":"string"}},"required":["id"],"type":"object"},"method":"GET","name":"get_wrapup","path":"/v1/admin/engagements/{id}/wrapup","requires_auth":true},{"description":"Admin only. Compose a new wrap-up snapshot (`wrapup/0.1`) for an engagement: every DELIVERED run projected from its cockpit doc, research rolled up through the CRM export's own builder when the engagement is linked to a workspace, and admin-only spend. The hash is over content, not compose time — re-composing unchanged inputs returns the SAME version (`created: false`); a real change starts a new draft version, resetting QA.","inputSchema":{"properties":{"id":{"description":"Engagement id","format":"uuid","type":"string"}},"required":["id"],"type":"object"},"method":"POST","name":"compose_wrapup","path":"/v1/admin/engagements/{id}/wrapup","requires_auth":true},{"description":"HAND-OFF: only a qa_passed wrap-up whose approved hash is the current\nversion's hash. Pins `delivered_version` — what the client reads. — HAND-OFF, for a wrap-up. Admin only. Only a `qa_passed` wrap-up whose approved hash still matches the current version's hash may be delivered. Pins `delivered_version` — composing again afterwards starts the next draft without taking the delivered one away from the client. Delivering the version that is ALREADY delivered is not a conflict: it answers 200 with the same body and `already_delivered: true`, and writes no second history row. A changed (redrafted) version still refuses `not_passed` like any undelivered draft.","inputSchema":{"properties":{"id":{"description":"Engagement id","format":"uuid","type":"string"}},"required":["id"],"type":"object"},"method":"POST","name":"deliver_wrapup","path":"/v1/admin/engagements/{id}/wrapup/deliver","requires_auth":true},{"description":"Admin only. The append-only history of an engagement's wrap-up: every compose, QA verdict and delivery, per version, oldest first.","inputSchema":{"properties":{"id":{"description":"Engagement id","format":"uuid","type":"string"}},"required":["id"],"type":"object"},"method":"GET","name":"get_wrapup_history","path":"/v1/admin/engagements/{id}/wrapup/history","requires_auth":true},{"description":"THE GATE, for a wrap-up: a person's own read of one named version (its\nnumber and hash), with a note that says what they checked. Agent and API\ntokens cannot record it (PLAN.md policy 9). — THE GATE, for a wrap-up. Admin only, and only a person's own sign-in (email link or Google login) — an agent or API token gets `person_sign_in_required` (PLAN.md policy 9). `version` and `snapshot_hash` must name the CURRENT version and its hash exactly (a QA note is evidence about ONE document, never \"whatever is there now\"); `note` is required, not a placeholder, and not reused verbatim from another wrap-up's QA. Refused once the current version is already delivered.","inputSchema":{"properties":{"body":{"description":"The JSON request body.","properties":{"note":{"type":"string"},"pass":{"type":"boolean"},"reader":{"description":"Retired: the reader is the signed-in caller (PLAN.md policy 9).\nAccepted only when it names the caller.","type":["string","null"]},"snapshot_hash":{"description":"The snapshot hash of the version the reader read, as shown with it.\nRequired; must be that version's hash.","type":["string","null"]},"version":{"description":"The version the reader read. Must be the current one: a QA note is\nevidence about one document, never about \"whatever is there now\".","format":"int32","type":"integer"}},"required":["pass","note","version"],"type":"object"},"id":{"description":"Engagement id","format":"uuid","type":"string"}},"required":["id","body"],"type":"object"},"method":"POST","name":"qa_wrapup","path":"/v1/admin/engagements/{id}/wrapup/qa","requires_auth":true},{"description":"Admin only. A staged, delivered or withdrawn deck's audit, as the client-deck skill wrote it (every claim with its pages, words and source), and the deck's metadata. Never shown to a client.","inputSchema":{"properties":{"id":{"description":"File id of a deck","format":"uuid","type":"string"}},"required":["id"],"type":"object"},"method":"GET","name":"get_deck_audit","path":"/v1/admin/files/{id}/audit","requires_auth":true},{"description":"Deliver one file. Track H: the same gate as a run — a person's own pass of\nthis exact file (its sha256), and for a composed brief, every cited run\ndelivered at the hash the brief cites. Delivering a delivered file again\nchanges nothing. — Admin only. Deliver a staged file: the engagement's client (or a member of its linked workspace) can then read it with `get_file`/`list_my_files`. Idempotent — delivering an already-delivered file keeps its original `delivered_at` and clears any withdrawal. A delivered file is otherwise immutable: withdraw it and stage a new one to change it. A film delivers its poster and captions with it, needs a person's pass of its current `review_sha256` and a poster, and, when bound to a run, that run delivered to this engagement at the approved evidence hash it was bound to. A poster or captions file is never delivered on its own.","inputSchema":{"properties":{"id":{"description":"File id","format":"uuid","type":"string"}},"required":["id"],"type":"object"},"method":"POST","name":"deliver_file","path":"/v1/admin/files/{id}/deliver","requires_auth":true},{"description":"GET /v1/admin/files/{id}/link","inputSchema":{"properties":{"id":{"format":"uuid","type":"string"}},"required":["id"],"type":"object"},"method":"GET","name":"get_engagement_file_link","path":"/v1/admin/files/{id}/link","requires_auth":true},{"description":"`POST /v1/admin/files/{id}/qa` — a person's own read of one file's exact\nbytes (policy 9): their sign-in, the sha256 they read, and a note that\nsays what they checked. A pass lets the file be delivered. — Admin, signed in as a person (email link or Google), never a token; not available over MCP. Record your own read of one staged file's exact bytes: `pass`, a note that says what you checked, and the `sha256` you read (must be current). A pass lets the file be delivered. For a film, send its `review_sha256` (the film with its current poster and captions; `list_engagement_files`): the read covers all three, and a pass needs a poster. A poster or captions file is reviewed with its film, not alone.","inputSchema":{"properties":{"body":{"description":"The JSON request body.","properties":{"note":{"type":"string"},"pass":{"type":"boolean"},"sha256":{"description":"The hash of what the reader read: the file's current sha256, or for a\nfilm its current `review_sha256` (the film plus its poster and captions).","type":"string"}},"required":["pass","note","sha256"],"type":"object"},"id":{"description":"File id","format":"uuid","type":"string"}},"required":["id","body"],"type":"object"},"method":"POST","name":"qa_file","path":"/v1/admin/files/{id}/qa","requires_auth":true},{"description":"Admin only. Watch or open a stored media file in any state (a reviewer previews a staged film before passing it): a 302 to a presigned URL valid for 5 minutes, or with `format=json` the same URL as `{url, expires_at}`. Never cached.","inputSchema":{"properties":{"format":{"description":"`json` answers `{url, expires_at}` with 200 instead of a 302 (for a page\nthat authenticates with a bearer header, which `<video src>` cannot send).","type":"string"},"id":{"description":"File id","format":"uuid","type":"string"}},"required":["id"],"type":"object"},"method":"GET","name":"stream_engagement_file","path":"/v1/admin/files/{id}/stream","requires_auth":true},{"description":"`GET /v1/admin/files/{id}/text` — a staged (or delivered, or withdrawn)\nfile's text, so a person reads it in the page before passing it. — Admin only. One engagement file's text, whatever its state (staged, delivered or withdrawn), so a person can read it before passing it: its metadata, `state`, `sha256`, `review_sha256` (the hash a QA names) and, for a db-stored text format (markdown, CSV, JSON, plain text), its full `text` up to 2 MB. PDFs expose embedded text when extraction succeeds, labeled `text_source: extracted`; no OCR is claimed. Oversized text and stored media have null text. A retracted file is gone: 404.","inputSchema":{"properties":{"id":{"description":"File id","format":"uuid","type":"string"}},"required":["id"],"type":"object"},"method":"GET","name":"get_engagement_file","path":"/v1/admin/files/{id}/text","requires_auth":true},{"description":"Admin only. Withdraw a delivered file: the client can no longer read it. Idempotent. Stage a replacement (same folder/name) and deliver it to supersede. A film withdraws its poster and captions with it and keeps its stored object, so it can be delivered again; a poster or captions file is withdrawn with its film, not alone.","inputSchema":{"properties":{"id":{"description":"File id","format":"uuid","type":"string"}},"required":["id"],"type":"object"},"method":"POST","name":"withdraw_file","path":"/v1/admin/files/{id}/withdraw","requires_auth":true},{"description":"`GET /v1/admin/grant-proposals` — one workspace's grant proposals, newest\nfirst. — Admin only. One workspace's grant proposals (find_grants), newest first: `{items: [{id, run_id, workspace_id, funder_name, program, title, source_url, deadline, award_min_usd, award_max_usd, eligibility, excerpt, state, decided_by, decided_at, note, created_at, found_at_level, applicant_path, eligibility_verdict, eligibility_quote}]}` (at most 500). Each one is a proposal until a person approves it (PLAN.md policy 5). Eligibility is a hard filter: a solicitation whose stated applicant types exclude the applicant is listed with `state: ineligible`, `eligibility_verdict: ineligible` and the clause that says so in `eligibility_quote`, and can never be approved. `found_at_level` is the level of the search that found it (city, county, region, state, multi_state, federal, unknown). Proposals of a withdrawn run are not listed. Not exposed over MCP: the queue serves the person who decides it.","inputSchema":{"properties":{"run_id":{"description":"Only this run's proposals.","format":"uuid","type":"string"},"state":{"description":"`proposed`, `approved`, `rejected` or `ineligible`.","type":"string"},"workspace_id":{"description":"The workspace whose proposals to list (required).","format":"uuid","type":"string"}},"required":[],"type":"object"},"method":"GET","name":"list_grant_proposals","path":"/v1/admin/grant-proposals","requires_auth":true},{"description":"`POST /v1/admin/grant-proposals/approve-all {run_id}`: every proposal of\nthe run still waiting (state `proposed`, which an ineligible one never is)\nis approved as [`approve_grant_proposal`] approves one, in one\ntransaction, and deciding the last one resumes the run. Two checks\n(PLAN.md, 2026-10-13): a person, and proposals that exist for this run. — Admin only, and a person's own sign-in. Approves every proposal of the run that is still waiting (`proposed`; a solicitation recorded ineligible is never waiting) exactly as approving each one would, in one transaction, then resumes the run the way deciding its last proposal does. Answers `{run_id, approved: [ids], resumed}`; a run with nothing waiting answers `approved: []`.","inputSchema":{"properties":{"body":{"description":"The resume body.","properties":{"run_id":{"description":"The find_grants run waiting on its proposals.","format":"uuid","type":"string"}},"required":["run_id"],"type":"object"}},"required":["body"],"type":"object"},"method":"POST","name":"approve_all_grant_proposals","path":"/v1/admin/grant-proposals/approve-all","requires_auth":true},{"description":"`POST /v1/admin/grant-proposals/resume` — Admin only, and a person's own sign-in (never an agent or API token; not exposed over MCP). Resumes a find_grants run waiting on its proposals with the ones approved so far: each approved opportunity whose deadline has not passed is simulated, soonest deadline first. Proposals still undecided stay proposals (approving one later still records it, but it is not simulated by this run). A run also resumes by itself when its last undecided proposal is decided. Answers `{run_id, resumed: true, approved}`.","inputSchema":{"properties":{"body":{"description":"The resume body.","properties":{"run_id":{"description":"The find_grants run waiting on its proposals.","format":"uuid","type":"string"}},"required":["run_id"],"type":"object"}},"required":["body"],"type":"object"},"method":"POST","name":"resume_grant_proposals","path":"/v1/admin/grant-proposals/resume","requires_auth":true},{"description":"`POST /v1/admin/grant-proposals/{id}/approve` — Admin only, and a person's own sign-in (never an agent or API token; not exposed over MCP). Approves one grant proposal (PLAN.md policy 5): resolves or mints the funder org in the proposal's workspace and records ONE GROUNDED `grant-opportunity` assertion on it, its source the opportunity's URL, its run the sourcing run, its value `{opp_id (sha256 of the URL), program, title, source_url, deadline, award_min_usd, award_max_usd, eligibility}`. Deciding the run's last undecided proposal resumes the run. Answers `{id, state: approved, entity_id, assertion: applied | unchanged, resumed}`.","inputSchema":{"properties":{"body":{"description":"A decision's body: an optional note on approve, a required one on reject.","properties":{"note":{"description":"Why (at most 1000 characters). Required to reject.","type":["string","null"]}},"type":"object"},"id":{"description":"Grant proposal id","format":"uuid","type":"string"}},"required":["id","body"],"type":"object"},"method":"POST","name":"approve_grant_proposal","path":"/v1/admin/grant-proposals/{id}/approve","requires_auth":true},{"description":"`POST /v1/admin/grant-proposals/{id}/reject` — Admin only, and a person's own sign-in (never an agent or API token; not exposed over MCP). Rejects one grant proposal with a required `note`; nothing is recorded about the funder. Deciding the run's last undecided proposal resumes the run. Answers `{id, state: rejected, resumed}`.","inputSchema":{"properties":{"body":{"description":"A decision's body: an optional note on approve, a required one on reject.","properties":{"note":{"description":"Why (at most 1000 characters). Required to reject.","type":["string","null"]}},"type":"object"},"id":{"description":"Grant proposal id","format":"uuid","type":"string"}},"required":["id","body"],"type":"object"},"method":"POST","name":"reject_grant_proposal","path":"/v1/admin/grant-proposals/{id}/reject","requires_auth":true},{"description":"Admin only. Distinct identities outside Timepoint active in the trailing 7 days (`created_at >= now() - 7 days`), and how many of them used MCP. One person can hold more than one identity (GitHub, email, member), so `active_identities` can count a person more than once. Active: an MCP tools/call (`identity.mcp_calls`, any outcome but `refused`), a meeting preview, a pin, or a run it created (root or child, any kind but `monitor`, the watch sweep's, and `onboarding`, a lineage row, which also leaves out a person's own calendar import; a pick's deep-simulation relaunch counts the original author). Counted: identities holding `user`, not `agent`, other than the house identity, not an administrator (effective `admin`: stored `admin` plus a verified `TP_ADMIN_EMAILS` address), not internal (a verified address at `timepointai.com`, exactly that domain; an unverified one does not exclude) and not a client login (`entity.clients`; a client's `member:` identities count). `excluded` counts the active identities removed as `internal` or as `clients` (one that is both counts once, as internal); administrators, agents and the house are not reported. `via_mcp` is the subset of the counted identities with an MCP call in the window. A read: counts only, never a name.","inputSchema":{"properties":{},"required":[],"type":"object"},"method":"GET","name":"get_directory_metrics","path":"/v1/admin/metrics/directory","requires_auth":true},{"description":"Admin only. Today's free-preview funnel (UTC day): `new_accounts` (accounts created today), `new_accounts_with_first_preview` (of those, how many ran a quick preview or a meeting preview, free or paid), the free uses the house paid for today against each daily cap (`previews`, `play_turns`: `{used, cap}`; a refunded claim is not counted) and `refunded_today` (free claims given back because the work failed). Caps come from `TP_FREE_PREVIEWS_PER_DAY` and `TP_FREE_PLAY_TURNS_PER_DAY`. The audit behind it is `freemium.claims`. A read: counts only, never a name.","inputSchema":{"properties":{},"required":[],"type":"object"},"method":"GET","name":"get_freemium_metrics","path":"/v1/admin/metrics/freemium","requires_auth":true},{"description":"Play funnel metrics (admin) — Admin only. The Play funnel for today (UTC day) and the last 7 UTC days (today included): `challenges_live`; per window the attempts started and finished (`attempts_won`, `attempts_lost`, `attempts_abandoned`), turns stored by funding (`turns_free`, `turns_credits`), `distinct_players`, `win_rate` (won / (won + lost), null when none), `median_turns_per_finished_attempt`, `model_calls` (house-funded calls from `freemium.house_days.calls`, refunds included) and `failed_calls` (judge calls that stored no turn, `play.attempts.failed_calls`, both funding paths), and `estimated_house_cost_usd_upper_bound` (free turns x `per_turn_cost_cap_usd`, an upper bound, not a bill); plus the `top_challenges` by attempts in the last 7 days (slug and title only). A read: counts only, never an owner id, handle or table text.","inputSchema":{"properties":{},"required":[],"type":"object"},"method":"GET","name":"get_play_metrics","path":"/v1/admin/metrics/play","requires_auth":true},{"description":"Admin only (MR2, gate G9). Per role: its current default, the stored slug (null = inherit), what it inherits from, where it is used, and the eligible registry models (live, text output, in the OpenRouter catalog or configured here). Also the deployment synthesis mode (single | alternate), its model pair, the A/B surcharge fraction and the alternation counter; and every eligible model with its registry prices. MR3's Boss model pickers read this.","inputSchema":{"properties":{},"required":[],"type":"object"},"method":"GET","name":"get_model_roles","path":"/v1/admin/model-roles","requires_auth":true},{"description":"Admin only (MR2, gate G9). Set per-role default models (a slug, or null to inherit again), the deployment synthesis mode (single | alternate), its model pair and the A/B surcharge fraction. A slug not yet registered triggers an on-demand OpenRouter catalog refresh (prices and parameters recorded; ledger prices untouched). Every named slug must then be registered, live, eligible (tp_llm::require_slug) and, when the catalog lists its parameters, list structured_outputs: beta sends a strict json_schema with provider.require_parameters (422 `model_lacks_structured_outputs`). A free model (`:free` or `openrouter/free`) is refused before anything else (422 `model_is_free`): free endpoints may log or train on prompts. Applies to runs admitted from now on; admitted runs keep their frozen models. Each change is logged append-only.","inputSchema":{"properties":{"body":{"additionalProperties":false,"description":"`PUT /v1/admin/model-roles` body. Every field is optional.","properties":{"roles":{"additionalProperties":{"type":["string","null"]},"description":"role → slug, or null to inherit again.","propertyNames":{"type":"string"},"type":["object","null"]},"synthesis":{"oneOf":[{"type":"null"},{"additionalProperties":false,"properties":{"ab_extra_base_fraction":{"description":"The A/B surcharge as a fraction of `deep_sim_base`, in (0, 4].","format":"double","type":["number","null"]},"mode":{"description":"`single` or `alternate` (`ab` is a per-run admin choice, never a default).","type":["string","null"]},"pair":{"description":"`[model_a, model_b]`, or null to clear (only while mode is single).","items":{"type":"string"},"type":["array","null"]}},"type":"object"}]}},"type":"object"}},"required":["body"],"type":"object"},"method":"PUT","name":"put_model_roles","path":"/v1/admin/model-roles","requires_auth":true},{"description":"Every `llm.model_registry` row with its catalog fields and prices (admin only) — the data MR3's Boss model pickers and MR2's per-run role resolution read from.","inputSchema":{"properties":{},"required":[],"type":"object"},"method":"GET","name":"list_model_registry","path":"/v1/admin/models","requires_auth":true},{"description":"Admin only. Fetches OpenRouter's live `/api/v1/models` catalog RIGHT NOW and upserts `llm.model_registry` in one transaction: new slugs are added, changed prices/fields update in place (never re-tiering an operator-set row), and slugs absent from the catalog are marked `in_catalog=false` (never touching `live`, which stays a real-call probe result). No scheduler calls this — every refresh is this admin action. V2: the refresh then re-prices `ledger.price_list` against the held derivation (`/v1/admin/pricing`): a new all-items version only when model cost moved, the first refresh derives instead, and every attempt is recorded. Only when `auto_reprice_enabled` is 1 (it ships 0: `PUT /v1/admin/pricing/params`); a move above `max_step_ratio` is held for `POST /v1/admin/pricing/confirm`.","inputSchema":{"properties":{},"required":[],"type":"object"},"method":"POST","name":"refresh_model_catalog","path":"/v1/admin/models/refresh","requires_auth":true},{"description":"Admin only. Sets `live = true` and `last_probe = now()` on one registered `llm.model_registry` row, undoing a dead-slug mark (a provider 404) without a hand UPDATE. Makes no provider call; the next real call is the probe. Catalog refresh never touches `live`, so this is the only revive.","inputSchema":{"properties":{"slug":{"description":"Registry slug, its `/` percent-encoded as `%2F` (e.g. `anthropic%2Fclaude-haiku-5.5`).","type":"string"}},"required":["slug"],"type":"object"},"method":"POST","name":"revive_model","path":"/v1/admin/models/{slug}/revive","requires_auth":true},{"description":"List every Play challenge (admin) — Admin. Play challenges, newest first (at most 200), filtered by `status`. The admin view includes `table_def` (personas, concerns, red lines), which no public route serves.","inputSchema":{"properties":{"status":{"description":"`draft`, `live`, `retired` or `all` (default).","type":["string","null"]}},"required":[],"type":"object"},"method":"GET","name":"list_play_challenges","path":"/v1/admin/play/challenges","requires_auth":true},{"description":"Compile a Play challenge (admin) — Admin. Compile a Play challenge from a simulation: exactly one of `run_id`, `news_id` (the story run behind a news card) or `preview_id`. One structured model call (the planning role) drafts the table: personas with concerns and red lines, the seat, the goal and the brief. The draft must pass the engine's checks (weighted starting support at most 0.40, no win reachable in one turn, 2 to 4 concerns per persona) and the naming rule: only a deceased public figure that a live Gallery moment vouches for is named; everyone else is a role, and no other person's name reaches the model or the challenge. A refused draft gets one more call. The challenge is stored as a `draft`; it goes live only when a person approves `POST /v1/admin/play/challenges/{id}/publish`.","inputSchema":{"properties":{"body":{"description":"The Boss knobs a compile may set, with `POST /v1/runs`' own semantics.\nConstructible in the crate (`CompileReq { parent_run_id: Some(p),\n..Default::default() }`) so other code compiles a kickoff through\n[`compile`], the one entry point.","properties":{"cast_approval_id":{"description":"A usable C1 cast approval of the caller's account; needed when the\napproved cast is above the admin tier.","format":"uuid","type":["string","null"]},"dry_run":{"description":"Track E: `true` runs every compile and admission check and answers 200\nwith the run it would admit (`POST /v1/runs {dry_run}`'s preview plus\n`kickoff_id`); the kickoff stays as it is, no run is minted and nothing\nis reserved. A refusal is listed in `refusals`. Compile only; a\nrecompile refuses it.","type":["boolean","null"]},"models":{"description":"Model picks per role, exactly as `POST /v1/runs {models}` (admin only)."},"parent_run_id":{"description":"Lineage: the run this compile follows from. It must be a run that is\nnot withdrawn, in the SAME space this compile acts in (a workspace\nkickoff: a run of that workspace; a personal kickoff: your own personal\nrun). Unknown, another space's and withdrawn ids are one typed 422\n`invalid_parent_run`. Frozen as the run's `parent_run_id` and recorded on\nits `run.accepted` event as `lineage: {parent_run_id, relation: \"child\"}`.","format":"uuid","type":["string","null"]},"sim_params":{"additionalProperties":{"format":"double","type":"number"},"description":"Per-run simulation parameters, exactly as `POST /v1/runs {sim_params}`\n(bounded; `admin_only` keys need the `admin` capability).","propertyNames":{"type":"string"},"type":["object","null"]}},"type":"object"}},"required":["body"],"type":"object"},"method":"POST","name":"compile_play_challenge","path":"/v1/admin/play/challenges","requires_auth":true},{"description":"Stage publishing a Play challenge (admin) — Admin. Ask for a draft challenge to go live. This never publishes: it stages a `publish_challenge` action (as `POST /v1/staged-actions` would) that a person approves in /service with their own browser sign-in (email link or Google). An agent, an approval grant, an operator's token and any other token cannot approve it (403 `person_sign_in_required`). Staging runs the publish checks and rolls them back: the challenge must still be a draft, pass every compile check under the naming rule derived again from its source, and a public board must pin `model_slug`. Staging again while one is open returns the open one (200).","inputSchema":{"properties":{"body":{"description":"The JSON request body.","properties":{"model_slug":{"description":"The model a public board pins (cheap, structured outputs, never `:free`).\nRequired for a public or unlisted challenge that has none yet.","type":["string","null"]},"reason":{"description":"Why, for the person who approves it.","type":["string","null"]}},"type":"object"},"id":{"description":"Challenge id, `ch_` and 32 hex digits","type":"string"}},"required":["id","body"],"type":"object"},"method":"POST","name":"publish_play_challenge","path":"/v1/admin/play/challenges/{id}/publish","requires_auth":true},{"description":"Admin only. The derived-pricing record: the process params, the served price list, the held derivation, and the latest 50 derivations and re-prices (append-only; before and after credits for every re-price).","inputSchema":{"properties":{},"required":[],"type":"object"},"method":"GET","name":"get_pricing","path":"/v1/admin/pricing","requires_auth":true},{"description":"Admin only. Apply the moves a re-price HELD because they exceeded `max_step_ratio` (optionally only `items`) as a new price_list version copying every item, recorded in the re-price log with trigger `admin_confirm`. Refused 409 when the price list, the held derivation or the registry prices changed since that record: re-run the re-price and confirm the new record.","inputSchema":{"properties":{"body":{"description":"The JSON request body.","properties":{"items":{"description":"Only these held items (default: every held item of that record).","items":{"type":"string"},"type":["array","null"]},"reprice_id":{"description":"The `ledger.reprices` record whose held moves to apply.","format":"int64","type":"integer"}},"required":["reprice_id"],"type":"object"}},"required":["body"],"type":"object"},"method":"POST","name":"confirm_pricing","path":"/v1/admin/pricing/confirm","requires_auth":true},{"description":"Admin only. Re-derive the implied model cost per credit from today's credits, recorded usage and registry prices, and hold it for re-pricing. Changes no credits; the derivation is appended to the record.","inputSchema":{"properties":{},"required":[],"type":"object"},"method":"POST","name":"derive_pricing","path":"/v1/admin/pricing/derive","requires_auth":true},{"description":"Admin only. Set pricing process params: `{key: value}` over the known keys (`auto_reprice_enabled` 0/1 or a boolean, `rederive_threshold`, `usage_window_days`, `min_sample_units`, `max_step_ratio`), each bounded by its row. All or nothing; every change is logged append-only with the actor.","inputSchema":{"properties":{"body":{"description":"The JSON request body."}},"required":["body"],"type":"object"},"method":"PUT","name":"put_pricing_params","path":"/v1/admin/pricing/params","requires_auth":true},{"description":"Admin only. One queue of everything that needs a person, across clients (R2): run reviews (`run`), client-work runs nobody has attached yet (`unattached_run`; the default view shows only the caller's own, other creators' are `not_mine`, including failed runs, with `created_by`, `mine`, `run_state` and `has_doc`; only the creator can attach), wrap-ups (`wrapup`, id = engagement id), client files and films (`file`, `film`; a film's poster and captions follow it and are not listed), staged actions waiting for approval (`staged_action`, with `can_approve` and `approve_refusal` for this caller and credential, and the approval grant fields `grantable`, `can_grant`, `can_approve_with_grant` and `grant`), pending research proposals (`research`, one row per drill-down job, with `pending` and `workspace_id`), grant proposals waiting for a person's decision (`grant_proposals`, id = the find_grants run, one row per run with at least one proposal still `proposed`, `next_action: decide`, with `run_id`, `workspace_id`, `awaiting` (how many are still proposed), `run_state` and `phase`; `engagement_id` and `client_owner` name the engagement linked to the run's workspace: the only one, else the only live one, else null, and the client is that engagement's, else the one client all of the workspace's engagements serve, else null) and pending cast additions (`cast_addition`). Every row has a short `title` (at most 60 characters, never empty), `summary`, `state`, `grade` (`A`/`B`/`C`/`F` for runs; `F` for a staged brief whose cited runs changed; else null) with `grade_reason`, `open_flags`, `next_action`, the `batch_actions` valid for it now, `hash` (a run's evidence hash, a file's review hash, a wrap-up's current snapshot hash) and its age. Run rows also expose the full `query`, execution `run_state` and `has_doc` separately from review state and quality; a review row also has `engagement_live`. Rows whose `next_action` is `none` are left out unless `include_done=true`. The default view also leaves out rows nobody will act on, each marked with its `hidden_reason`: `house_run` (an unattached run made by the house identity, a news scan's child, or a public research project's run; listed with `kind=unattached_run`), `stale_run` (any other unattached run that failed or was made more than 7 days ago; listed with `kind=unattached_run`), `not_mine` (any other unattached run made by someone other than the caller, who alone can attach it; listed with `kind=unattached_run`), `closed_engagement` (a review of a closed engagement, which can no longer be QA'd or delivered, and that engagement's files and wrap-up while they only wait for a read; a file or wrap-up ready to deliver stays listed; the waiting ones are listed under their own `kind`) and `demo_client` (a demo client's row; listed under that client's `client` filter). `include_hidden=true` lists them all. `counts` and `clients` cover every row `include_done` admits, hidden or not, before the `client`/`kind`/`needs` filters; `counts.hidden` says how many of them the default view leaves out (`total`, `by_reason`, `by_next_action`). Ordered by what needs a person now: approve, then deliver, then read, reread, ack_flags and decide, then the rest; newest first within each. Reading the queue runs a missing pre-QA check for a review at `draft` or `qa_failed` whose run is complete, never for any other review.","inputSchema":{"properties":{"client":{"description":"Only rows serving this client (its `owner_id`).","type":"string"},"include_done":{"description":"`true` also lists rows that need nothing now (`next_action: none`, e.g.\ndelivered work). Default `false`.","type":"boolean"},"include_hidden":{"description":"`true` also lists the rows the default view leaves out (`hidden_reason`:\n`house_run`, `stale_run`, `not_mine`, `closed_engagement`, `demo_client`). Default\n`false`.","type":"boolean"},"kind":{"description":"Only rows of this kind: `run`, `unattached_run`, `wrapup`, `file`,\n`film`, `staged_action`, `research`, `grant_proposals` or `cast_addition`.","type":"string"},"needs":{"description":"Only rows whose `next_action` is this: `ack_flags`, `read`, `deliver`,\n`reread`, `fix`, `revise`, `approve`, `decide`, `attach` or `none`.","type":"string"}},"required":[],"type":"object"},"method":"GET","name":"get_review_queue","path":"/v1/admin/queue","requires_auth":true},{"description":"Admin (agent tokens included). Preview a retraction of simulations (runs), kickoffs, wrap-ups and client files in one workspace. Writes nothing. Returns, per target, whether it was found in the workspace, its state, whether the client can see it now, `blocked` (`not_found`, `not_in_workspace`, `already_withdrawn`, `live`, `published`) and `rows`, the rows per table it would delete, redact, revoke or keep; `research_safety`, approved research facts whose only support is a row it would delete; `dependents`, other items that quote a target and must be retracted with it; `child_runs` (shown only); `totals`; `retractable`; each target's current `label` (not hashed, never stored); and `preview_hash`. To retract, stage `retract_items` with `POST /v1/staged-actions` and target `{workspace_id, targets, preview_hash, research_at_risk}`; a person approves it in /service. Research (entities, edges, GROUNDED and GENERATED facts, drill-down jobs and proposals) is never touched.","inputSchema":{"properties":{"body":{"description":"The JSON request body.","properties":{"reason":{"description":"Why, for the person who approves it. Nonempty, at most 2000 characters.","type":"string"},"research_at_risk":{"description":"`refuse` (default): the retraction cannot run while an approved research\nfact rests only on rows it would delete. `keep`: keep those rows.","type":["string","null"]},"targets":{"description":"The items to retract (at most 500).","items":{"properties":{"id":{"description":"The item's id (a run id, kickoff id, wrap-up id or client file id).","type":"string"},"type":{"description":"`run`, `kickoff`, `wrapup` or `file`.","example":"run","type":"string"}},"required":["type","id"],"type":"object"},"type":"array"},"workspace_id":{"description":"The workspace every target must belong to.","type":"string"}},"required":["workspace_id","targets","reason"],"type":"object"}},"required":["body"],"type":"object"},"method":"POST","name":"preview_retraction","path":"/v1/admin/retractions/preview","requires_auth":true},{"description":"Admin only. Every review (a run attached to a client via intake), newest first: the review row plus the run's creator and state, its engagement's question key (`question`) and side, the client's display name, whether it has a cockpit doc, whether its kind is gate-required and the gate's verdict, and a count of policy-9 person reads recorded against it. `title` is the SHORT title (≤ 60 characters: the one written at intake, else derived from the run's question, `title_derived: true`), `summary` likewise (≤ 160), and `query` the run's whole question. Every non-withdrawn review carries its grade (R3: `grade` A/B/C/F with `grade_reason`, `open_flags`, `lint_current`, `preliminary` `{verdict, by, at, current}` or null, `evidence_hash`) and what it needs next (`next_action`, `batch_actions`), computed exactly as `get_review_queue` computes them. Reading the list runs a missing pre-QA check for a review at `draft` or `qa_failed` whose run is complete, never for any other review. Filter with `state` (`draft`, `qa_passed`, `qa_failed`, `delivered`).","inputSchema":{"properties":{"state":{"description":"Filter to one review state: `draft`, `qa_passed`, `qa_failed` or `delivered`.","type":"string"}},"required":[],"type":"object"},"method":"GET","name":"list_reviews","path":"/v1/admin/reviews","requires_auth":true},{"description":"INTAKE: attach a prepared run to a client under a live engagement. The\nengagement must exist, be live, and belong to that client — preparing\nwork outside a claimed question is exactly what the conflict rule forbids. — Admin only. Attach a prepared run (created by the calling admin) to a client under a live engagement, starting it at `draft` — or, if it is already attached to THIS client and engagement, update its intake fields (title, summary, og title/description, resolution criterion, reality note; only the fields sent change). The engagement must exist, be live, and belong to `client_owner`; a workspace-linked engagement additionally requires the run to be in that workspace. Once the review reached `delivered` only `title` and/or `summary` are accepted (under the same authorizations: the calling admin must have created the run): each value that differs is written and one `retitled` history row names what changed (`short title set`, `summary set`); an identical repeat changes nothing and writes no row. Any other field on a delivered run answers 409 `already_delivered`. Refused when the run is already assigned to a different client or engagement. `title` (≤ 60 characters, shown in every list) and `summary` (≤ 160) are trimmed and never cut: an empty or longer value is a 422. Without them a first intake uses `og_title` / `og_description` when they fit, else lists derive both from the run's question. When the run is complete and the review is at `draft` or `qa_failed`, intake runs the pre-QA check of the run as it stands (track F) in the same transaction; a check that cannot run never loses the intake.","inputSchema":{"properties":{"Idempotency-Key":{"description":"M4: a caller-chosen key (≤200 chars). The same caller + key + method/path/body within 24h replays the first response (header idempotent-replay: true) instead of re-running; the same key with a different request answers 422 idempotency_key_reused; a concurrent duplicate answers 409 idempotency_in_progress (retryable). Absent = ordinary behavior.","type":["string","null"]},"body":{"description":"The JSON request body.","properties":{"client_owner":{"type":"string"},"engagement_id":{"format":"uuid","type":"string"},"og_description":{"description":"What is being decided and when it resolves, ≤~200 chars — the social\ncard's description. NEVER the leading outcome.","type":["string","null"]},"og_title":{"description":"The question, plainly, ≤~70 chars — the social card's title. NEVER the\nanswer (tp-public-run/1.2, campaign rule).","type":["string","null"]},"reality_note":{"description":"When the SUBJECT is real: the sentence separating the real event from\nthe invented scenario. Prefixes the provenance string in the manifest.","type":["string","null"]},"resolution_criterion":{"description":"How the public entry resolves, stated up front — rendered on the post\n(tp-public-run/1.1). Optional: client work has no public resolution.","type":["string","null"]},"run_id":{"format":"uuid","type":"string"},"summary":{"description":"A summary, ≤ 160 characters (R1). Trimmed; an empty or longer value is\nrefused, never cut. Absent on first intake: `og_description` when it\nfits, else derived from the run's question.","type":["string","null"]},"title":{"description":"A short title, ≤ 60 characters, shown in every list (R1). Trimmed; an\nempty or longer value is refused, never cut. Absent on first intake:\n`og_title` when it fits, else derived from the run's question.","type":["string","null"]}},"required":["run_id","client_owner","engagement_id"],"type":"object"}},"required":["body"],"type":"object"},"method":"POST","name":"intake_review","path":"/v1/admin/reviews","requires_auth":true},{"description":"HAND-OFF. Refuses anything not at qa_passed — the gate cannot be walked\naround, only through. At `delivered` the client's read opens. — HAND-OFF. Admin only, a person's own sign-in. Two checks (2026-10-13): the caller is a person, and the run is complete with a document. A review not yet passed is passed in the same call (history rows `qa_passed` with the note \"passed at delivery\", then `delivered`); the approved evidence hash becomes the run as served now. The run's own gate report, the scenario contract and the pre-QA lint are information on the card, never a block. At `delivered` the client's read of this run opens. Delivering an already-`delivered` review is not a conflict: it answers 200 with the same body and `already_delivered: true`, and writes no second history row.","inputSchema":{"properties":{"run_id":{"description":"Run id (the review's id)","format":"uuid","type":"string"}},"required":["run_id"],"type":"object"},"method":"POST","name":"deliver_review","path":"/v1/admin/reviews/{run_id}/deliver","requires_auth":true},{"description":"`GET /v1/admin/reviews/{run_id}/entities` — R4: who the run could use and\nwho appeared. Read-only: it never writes a lint. — Admin only (an agent token may read it). The run's entities: its roster (origin `selected`, `planner_added` or `planner`), selected cast members that never reached the roster (`selected`), and actors the planner proposed (`proposed`, with `proposal_state`). Each says whether a live entity in the run's own space holds it (`in_registry`), whether the run requires it, how many moments and dialogue lines it appears in, and `never_appears` (required, in no moment and not named in the text: the pre-QA check's `required_missing` rule). Read-only; it never writes a check.","inputSchema":{"properties":{"run_id":{"description":"Run id (the review's id)","format":"uuid","type":"string"}},"required":["run_id"],"type":"object"},"method":"GET","name":"get_review_entities","path":"/v1/admin/reviews/{run_id}/entities","requires_auth":true},{"description":"`GET /v1/admin/reviews/{run_id}/evidence` — the hash a reader attests to:\nthe evidence hash of the run as it is served now, next to the hash the\nlast passing QA approved. The review UI shows it and sends it back. — Admin only. The evidence hash a reader attests to when they QA or human-read this item: the review's current evidence hash next to the hash the last passing QA approved, whether they match, and the run's own quality-check gate (`{required, verdict}`). Send `evidence_hash` back with `qa_review`, `record_human_read` or `preliminary_qa` to name the version read.","inputSchema":{"properties":{"run_id":{"description":"Run id (the review's id)","format":"uuid","type":"string"}},"required":["run_id"],"type":"object"},"method":"GET","name":"get_review_evidence","path":"/v1/admin/reviews/{run_id}/evidence","requires_auth":true},{"description":"Admin only. The full append-only history of one review: every state transition and attestation (draft, human reads, preliminary and gate QA verdicts, delivery, merges, workspace links), oldest first.","inputSchema":{"properties":{"run_id":{"description":"Run id (the review's id)","format":"uuid","type":"string"}},"required":["run_id"],"type":"object"},"method":"GET","name":"get_review_history","path":"/v1/admin/reviews/{run_id}/history","requires_auth":true},{"description":"`POST /v1/admin/reviews/{run_id}/human-read` — record that the signed-in\nperson read this one item, whatever its state (a delivered review cannot\npass through the QA gate again). Append-only history; the review itself\nis unchanged. Needs a person's own sign-in, the hash read, and a real note. — Admin only, and only a person's own sign-in (email link or Google login) — an agent or API token gets `person_sign_in_required`; use `preliminary_qa` instead (PLAN.md policy 9). Records that the signed-in person read this one item, whatever its state (a `delivered` review may still be read again) — append-only history, the review itself is unchanged. `evidence_hash` must name the CURRENT hash (`get_review_evidence`), and `note` must say something about this item, not a placeholder and not reused verbatim from another item's read.","inputSchema":{"properties":{"body":{"description":"The JSON request body.","properties":{"evidence_hash":{"description":"The evidence hash of the version the reader read, as `GET\n…/evidence` showed it. Required; must be the current hash.","type":["string","null"]},"note":{"type":"string"},"reader":{"description":"Retired: the reader is the signed-in caller. Accepted only when it\nnames the caller.","type":["string","null"]}},"required":["note"],"type":"object"},"run_id":{"description":"Run id (the review's id)","format":"uuid","type":"string"}},"required":["run_id","body"],"type":"object"},"method":"POST","name":"record_human_read","path":"/v1/admin/reviews/{run_id}/human-read","requires_auth":true},{"description":"`POST /v1/admin/reviews/{run_id}/preliminary-qa` — an agent's (or a\nperson's) preliminary read of one item. Any admin caller, including agent\nand API tokens. It records a note and a pass/fail verdict against the\ncurrent hash, and NEVER changes the review's state: only a person's\n`…/qa` can pass or fail a review, and a preliminary QA is never a human\nread. — Admin only — any admin caller, including an agent or API token. Records a preliminary pass/fail read against the current evidence hash; it NEVER changes the review's state (only a person's `qa_review` can pass or fail a review) and is never treated as the human read. Refused once the review is `delivered`. Same evidence-hash and note rules as `record_human_read`.","inputSchema":{"properties":{"body":{"description":"The JSON request body.","properties":{"evidence_hash":{"type":["string","null"]},"note":{"type":"string"},"pass":{"type":"boolean"}},"required":["pass","note"],"type":"object"},"run_id":{"description":"Run id (the review's id)","format":"uuid","type":"string"}},"required":["run_id","body"],"type":"object"},"method":"POST","name":"preliminary_qa","path":"/v1/admin/reviews/{run_id}/preliminary-qa","requires_auth":true},{"description":"THE GATE. A person's own read against the boundary list: their sign-in\n(never an agent or API token), the hash of the version they read, and a\nnote that says what they checked. A run whose own quality check blocked\nit cannot pass. Records who and when. — THE GATE. Admin only, and only a person's own sign-in (email link or Google login) — an agent or API token gets `person_sign_in_required`; it records a preliminary QA instead (PLAN.md policy 9). A `pass` needs a readable, complete result AND (for a `deep_sim`/`portal_sim` run) its own quality-check report to have passed, plus any scenario contract on the plan; a `fail` needs only a readable result. `evidence_hash` must name the CURRENT hash, and `note` is the human read's required evidence — not a placeholder, not reused verbatim from another item's read. Refused once `delivered`.","inputSchema":{"properties":{"body":{"description":"The JSON request body.","properties":{"note":{"type":"string"},"pass":{"type":"boolean"},"reader":{"description":"Retired: the reader is the signed-in caller (PLAN.md policy 9).\nAccepted only when it names the caller.","type":["string","null"]},"snapshot_hash":{"description":"The snapshot hash of the version the reader read, as shown with it.\nRequired; must be that version's hash.","type":["string","null"]},"version":{"description":"The version the reader read. Must be the current one: a QA note is\nevidence about one document, never about \"whatever is there now\".","format":"int32","type":"integer"}},"required":["pass","note","version"],"type":"object"},"run_id":{"description":"Run id (the review's id)","format":"uuid","type":"string"}},"required":["run_id","body"],"type":"object"},"method":"POST","name":"qa_review","path":"/v1/admin/reviews/{run_id}/qa","requires_auth":true},{"description":"`POST /v1/admin/runs/{id}/picks/{option_id}/rerun` — Admin only, and a person's own sign-in (never an agent or API token; not exposed over MCP). Relaunches the deep simulation of one pick of a delivered find_grants or find_money run whose last deep simulation failed (or was withdrawn): a new `deep_sim` run with the failed one's question word for word, its workspace, its owner and applicant, and the same `parent_run_id`, admitted as the pick's own was (no reservation; its plan card is the spend consent). The parent records the relaunch as a `run.child` event `{child_run_id, opp_id, kind: deep_sim, rerun_of, by}`, so the grant book shows the newest. Answers 202 `{run_id, parent_run_id, option_id, rerun_of, kind: deep_sim, state: created, query}`.","inputSchema":{"properties":{"id":{"description":"The find_grants or find_money run","format":"uuid","type":"string"},"option_id":{"description":"The pick: its `opp_id`, as the run's pick_opportunities options and artifact `picks` carry it","type":"string"}},"required":["id","option_id"],"type":"object"},"method":"POST","name":"rerun_pick_deep_sim","path":"/v1/admin/runs/{id}/picks/{option_id}/rerun","requires_auth":true},{"description":"Admin. Ask for a public sealed forecast to be settled, with no deploy. This never writes the settlement: it stages a `settle_seal` action (as `POST /v1/staged-actions` would) that a person approves in /service with their own browser sign-in (email link or Google). An agent, an approval grant and an operator's token cannot approve it (403 `person_sign_in_required`). Staging runs the settlement's checks and rolls them back: `preview.would_return` is the public seal record as it would read. On approval the settlement is recorded (final; it is never edited) and `/public/v1/seals/{id}` serves it at once, with `settlement_status`, `resolution`, `hit` and `resolved_at`. `resolution` (`yes`, `no` or `void`) is required and must match the outcome's first word. Staging again while one is open for this seal returns the open one (200).","inputSchema":{"properties":{"body":{"description":"The JSON request body.","properties":{"outcome":{"description":"One line, at most 240 characters, starting with the word that matches\n`resolution`: `Yes:`, `No:` or `Void:`.","example":"No: the central bank held its policy rate (statement, Oct 30, 2026).","type":"string"},"reason":{"description":"Why, for the person who approves it. Defaults to a sentence naming the source.","type":["string","null"]},"resolution":{"description":"How the question resolved: `yes`, `no` or `void`. Required. The public\nseal serves it as `resolution` and derives `hit` from it.","example":"no","type":"string"},"settled":{"description":"The date of the result, `YYYY-MM-DD` (the date where it was published).","example":"2026-10-30","type":"string"},"source_url":{"description":"The primary source of the result: an https URL. Public once settled.","example":"https://example.gov/statement","type":"string"}},"required":["settled","outcome","resolution","source_url"],"type":"object"},"id":{"description":"Seal id, `seal_` and 12 hex digits","type":"string"}},"required":["id","body"],"type":"object"},"method":"POST","name":"settle_seal","path":"/v1/admin/seals/{id}/settle","requires_auth":true},{"description":"Admin only. The caller's own completed simulations (`quick_sim`, `deep_sim`, `portal_sim`, `story_sim`) not yet attached to any review — the pool `intake_review` draws from. `title` is a short title derived from the run's question (≤ 60 characters); `query` is the whole question.","inputSchema":{"properties":{},"required":[],"type":"object"},"method":"GET","name":"list_work","path":"/v1/admin/work","requires_auth":true},{"description":"Create a Checkout Session for a credit pack; the client redirects to `url`.","inputSchema":{"properties":{"body":{"description":"The JSON request body.","properties":{"pack":{"type":"string"}},"required":["pack"],"type":"object"}},"required":["body"],"type":"object"},"method":"POST","name":"checkout","path":"/v1/billing/checkout","requires_auth":true},{"description":"Server-side verification + idempotent grant. Safe to replay.","inputSchema":{"properties":{"body":{"description":"The JSON request body.","properties":{"items":{"description":"Only these held items (default: every held item of that record).","items":{"type":"string"},"type":["array","null"]},"reprice_id":{"description":"The `ledger.reprices` record whose held moves to apply.","format":"int64","type":"integer"}},"required":["reprice_id"],"type":"object"}},"required":["body"],"type":"object"},"method":"POST","name":"confirm","path":"/v1/billing/confirm","requires_auth":true},{"description":"Every provider budget layer the LLM gate consults, before anything fails, in the gate's own order (`order`: account, key, daily_budget): `{supply: metered | unlimited, gate, refusing_layer, order, account: {state: ok | no_headroom | unknown | not_configured, headroom_usd, floor_usd, spendable_usd}, key: {state: ok | no_headroom | no_key | unknown | unlimited, limit_remaining_usd, floor_usd, spendable_usd}, daily_budget: {state: ok | exhausted | unknown, spent_today_usd, budget_usd, remaining_usd}, note, credits: {account: present | none, balance, held, available}}`. Every layer is observed (none is skipped because an earlier one refuses); `gate` is the gate's own answer over them and `refusing_layer` the first layer that refuses, or null. `spendable_usd` is what a layer allows before the gate refuses (amount minus floor); a key `unlimited` has no limit of its own and the account governs. Under unlimited supply the layers are null and nothing is checked over the network. With `cap_usd` the response adds `step: {cap_usd, covered, short_layers, unknown_layers}`: a layer covers the step when its spendable amount is at least the cap; an unknown amount is never covered (`covered: null`); one short layer makes `covered: false`. `credits` is the caller's own ledger. Numbers and states only; no key is ever returned. Admin only.","inputSchema":{"properties":{"cap_usd":{"description":"Optional: what one planned step may cost at most, in USD (greater than\n0, at most 1000). The response then carries `step`.","format":"double","type":"number"}},"required":[],"type":"object"},"method":"GET","name":"get_budget_gates","path":"/v1/budget","requires_auth":true},{"description":"Write the human-confirmed attendees into the registry. — Write EXACTLY the attendees a human confirmed. Body: `{event: {id, title?, starts_at?, all_day_on?}, attendees: [{email?, display_name?, raw?, entity_id?}]}`. An attendee with `entity_id` is attached to that existing person (re-point); without one it is resolved or minted by name through `entities::resolve_or_mint`. Skipped attendees are simply absent from the list, and an EMPTY list writes nothing at all (`written: false`, not even a lineage row). Each confirmed person receives two GROUNDED assertions through the registry's own gate — `email` (which makes them matchable by address next time) and `calendar-invite` (the event id, title and time) — with `source = google_calendar:<event id>`, so the provenance names the calendar event itself. The human confirmation is recorded as lineage: the assertions ride an `onboarding` run row created by the confirming account. Idempotent: re-confirming the same event writes nothing new. Returns `{written, import_id, confirmed: [...], skipped: [...]}`.","inputSchema":{"properties":{"body":{"description":"The JSON request body.","properties":{"items":{"description":"Only these held items (default: every held item of that record).","items":{"type":"string"},"type":["array","null"]},"reprice_id":{"description":"The `ledger.reprices` record whose held moves to apply.","format":"int64","type":"integer"}},"required":["reprice_id"],"type":"object"}},"required":["body"],"type":"object"},"method":"POST","name":"calendar_confirm","path":"/v1/calendar/confirm","requires_auth":true},{"description":"Begin connecting a Google Calendar: mint a single-use consent link. — Mint a single-use Google consent link for THIS account. Returns `{authorize_url, expires_at, scope, redirect_uri}`; the browser navigates to `authorize_url`, Google returns to the callback, and the refresh token is stored server-side, ENCRYPTED AT REST under a key this deployment holds outside the database — it is never returned to the browser and never rides a URL. The requested scope is exactly one: `calendar.events.readonly`. When the deployment holds no Google OAuth client, or no encryption key for the stored credential, the answer is a typed 503 `calendar_not_configured` naming the unset variables — there is never a link that leads nowhere, and a deployment that cannot store the credential safely never asks for one.","inputSchema":{"properties":{},"required":[],"type":"object"},"method":"POST","name":"calendar_connect","path":"/v1/calendar/connect","requires_auth":true},{"description":"Disconnect the calendar: revoke at Google, then forget the token. — Disconnect this account's Google Calendar. The stored refresh token is revoked AT GOOGLE and then FORGOTTEN (the row's ciphertext and nonce are both nulled — a revoked connection never keeps a dormant standing capability). Idempotent: disconnecting when nothing is connected answers 200 with `{was_connected: false}` and still deletes any event copies and calendar summaries left behind. If the stored credential cannot be decrypted here it is still forgotten, and the response says `revoked_at_google: false` with the address to finish the withdrawal — forgetting a permission we cannot open beats keeping it. Entities and assertions already imported, and meeting previews already run, are NOT deleted — they are part of the record, and the response says so. The event copies saved for meeting previews (titles, descriptions, attendee names) and Trajectory's calendar summaries (aggregate snapshots of hours per category) ARE deleted with the permission, in the same transaction: if either delete fails the call fails and the calendar stays connected, so a retry finishes the job. Category rules the person saved in Trajectory stay with their profile (the note says so) until they forget their Trajectory.","inputSchema":{"properties":{},"required":[],"type":"object"},"method":"DELETE","name":"calendar_disconnect","path":"/v1/calendar/connection","requires_auth":true},{"description":"The owner's upcoming calendar events, normalized. — This account's upcoming primary-calendar events over a forward window (`days` 1..=30, default 7; `max` 1..=50, default 50), recurrences expanded into dated instances and ordered by start time. ALWAYS 200 with an envelope `{connected, state, reason, calendar_label, window, events}`; `state` is one of `ok` | `not_configured` (this deployment holds no Google OAuth client, or no key for the stored credential) | `not_connected` | `reauthorize` (the grant died) | `scope_missing` (connected, but the calendar permission was withheld — the UI must offer re-authorize, NOT show an empty list) | `token_unreadable` (a connection IS stored and this deployment cannot decrypt it — a missing or changed key; the UI must offer reconnect, NOT show an empty list) | `upstream_error`. Every non-`ok` state carries `events: []` AND a reason, so an empty list is never ambiguous. Each event carries `{id, title, description, start, end, location, all_day, duration_minutes, attendees:[{email, display_name, name_basis, organizer, is_self, response_status}]}`; `start`/`end` are `{kind: at, at}` for a real instant or `{kind: all_day, at: null, date}` for an all-day boundary — a bare date is never widened into an instant. An `ok` read SAVES each delivered event's id, title, start, length, description, and for each attendee the name the calendar gave (an attendee the calendar names only by address is saved unnamed: never the address, nor a name made from it) and whether they are the organizer or you, private to this account, and only while the calendar is connected, so a meeting preview can start from an event id; a copy no read refreshes for 30 days is deleted, and disconnecting deletes them all. It also records the last successful sync time. It writes nothing to the registry and nothing at Google.","inputSchema":{"properties":{"days":{"description":"Forward window in days, 1..=30 (default 7).","format":"int64","type":"integer"},"max":{"description":"Max events, 1..=50 (default 50).","format":"int32","minimum":0,"type":"integer"}},"required":[],"type":"object"},"method":"GET","name":"calendar_events","path":"/v1/calendar/events","requires_auth":true},{"description":"What one confirmed import wrote. — Read back one confirmed calendar import by its `import_id`: `{import_id, description, imported_at, people: [{entity_id, path, display_name, email, event}]}`. Owner-scoped — somebody else's import (or an unknown one) is the SAME 404, never a 403, which would confirm it exists.","inputSchema":{"properties":{"id":{"description":"The `import_id` from a confirm call.","type":"string"}},"required":["id"],"type":"object"},"method":"GET","name":"calendar_import","path":"/v1/calendar/imports/{id}","requires_auth":true},{"description":"Propose entity matches for a list of attendees — resolve-only, zero writes. — PROPOSE registry matches for a list of attendees. Body: `{attendees: [{email?, display_name?, raw?}]}` — `raw` accepts a typed token like `\"Jane Doe <jane@co.com>\"`. For each attendee the response carries `{email, display_name, name_basis, match, matched, alternatives, would_mint_path}`: `match` is `existing` or `new`, `matched` names the dossier it looks like and WHY (`basis`: `email` — this exact address is already on that person — or `name`), `alternatives` are other people you can re-point to, and `would_mint_path` states exactly what a confirm would create when nothing matched. Attendees that parse to nothing come back under `skipped` with a reason. THIS ENDPOINT WRITES NOTHING: no entity is minted and no assertion is stored until POST /v1/calendar/confirm names the ones a human accepted.","inputSchema":{"properties":{"body":{"description":"The JSON request body.","properties":{"attendees":{"items":{"properties":{"display_name":{"description":"The name, when the calendar supplied one.","type":["string","null"]},"email":{"description":"The address, when the calendar supplied one.","type":["string","null"]},"raw":{"description":"A raw token (`\"Jane Doe <jane@co.com>\"`) — used when neither of the\nabove is given, so typed-in text and calendar data share one parser.","type":["string","null"]}},"type":"object"},"type":"array"}},"required":["attendees"],"type":"object"}},"required":["body"],"type":"object"},"method":"POST","name":"calendar_match","path":"/v1/calendar/match","requires_auth":true},{"description":"Cast approvals, newest first (at most 200). An admin sees every account's requests in every state; any other caller sees their own account's. Each record carries `usable` (approved and unexpired), `expired`, and its append-only `history`.","inputSchema":{"properties":{},"required":[],"type":"object"},"method":"GET","name":"list_cast_approvals","path":"/v1/cast-approvals","requires_auth":true},{"description":"Request a cast approval for the CALLER's account: a deep_sim or portal_sim run may then admit a cast above the admin tier (20), up to `max_cast`, by sending `cast_approval_id`. Any operator may request; only an admin approves. The request is recorded append-only and starts in state `requested`.","inputSchema":{"properties":{"body":{"description":"The JSON request body.","properties":{"max_cast":{"description":"The largest cast (N) the approval should allow. Must be above the admin\ntier (`deep_sim_cast_entity_max`'s row max, 20) and at most\n`cast_approval_max_cast` (default 50).","example":25,"format":"int64","type":"integer"},"reason":{"description":"Why this account needs a cast this large. Non-empty.","example":"Sector-wide scenario naming every regional utility","type":"string"},"valid_days":{"description":"Days the approval stays usable once approved; default and maximum from\n`cast_approval_valid_days` (default 30, max 365).","format":"int64","type":["integer","null"]}},"required":["max_cast","reason"],"type":"object"}},"required":["body"],"type":"object"},"method":"POST","name":"create_cast_approval","path":"/v1/cast-approvals","requires_auth":true},{"description":"Admin only. Approve a `requested` cast approval: it becomes usable by its account's runs until `expires_at` (now + `valid_days`) or a revoke.","inputSchema":{"properties":{"body":{"description":"The JSON request body.","properties":{"note":{"description":"Optional note recorded with the decision (send `{}` for none).","type":["string","null"]}},"type":"object"},"id":{"description":"Approval id","format":"uuid","type":"string"}},"required":["id","body"],"type":"object"},"method":"POST","name":"approve_cast_approval","path":"/v1/cast-approvals/{id}/approve","requires_auth":true},{"description":"Admin only. Reject a `requested` cast approval. Final.","inputSchema":{"properties":{"body":{"description":"The JSON request body.","properties":{"note":{"description":"Optional note recorded with the decision (send `{}` for none).","type":["string","null"]}},"type":"object"},"id":{"description":"Approval id","format":"uuid","type":"string"}},"required":["id","body"],"type":"object"},"method":"POST","name":"reject_cast_approval","path":"/v1/cast-approvals/{id}/reject","requires_auth":true},{"description":"Admin only. Revoke an `approved` cast approval: no NEW run may use it. Runs already admitted under it keep their frozen cast cap.","inputSchema":{"properties":{"body":{"description":"The JSON request body.","properties":{"note":{"description":"Optional note recorded with the decision (send `{}` for none).","type":["string","null"]}},"type":"object"},"id":{"description":"Approval id","format":"uuid","type":"string"}},"required":["id","body"],"type":"object"},"method":"POST","name":"revoke_cast_approval","path":"/v1/cast-approvals/{id}/revoke","requires_auth":true},{"description":"Every telling of one claim BY THIS CALLER, chronological. The content hash\ncovers exactly `{label, occurs_at, participants}` (tt-core), so equal hashes\nare the same claim retold — both kept, linked by the hash itself. — Every moment sharing this `content_hash` — the same CLAIM, each row one TELLING — in chronological order. The hash is the link (tt-core's canonical hash over {label, occurs_at, participants}); there is no join table. 404 when no moment carries the hash.","inputSchema":{"properties":{"content_hash":{"description":"Full content hash, `sha256:<hex>`","type":"string"}},"required":["content_hash"],"type":"object"},"method":"GET","name":"get_claim","path":"/v1/claims/{content_hash}","requires_auth":true},{"description":"The caller's live ConsoleDoc, validated before serving. — Assemble and serve the caller's live ConsoleDoc (contract `console/0.1`, CONTRACTS §9): `{v, credits, running, results, feed, action, synths, news, schedule, pending_forecasts, record, prices}`. Humanized timestamps render in the zone named by the optional `X-User-Timezone` header (IANA name); missing/invalid degrades to UTC, labeled UTC. Validated against its own validator before serving — an invalid assembly is a typed 500, never served broken. `x-timepoint-workspace` scopes the `find_money` pre-read ONLY: named, it counts and lists the GROUNDED deal record of that workspace (every member's) that a run started there would source; every other section stays the caller's own personal record.","inputSchema":{"properties":{"X-User-Timezone":{"description":"IANA timezone name (e.g. America/Denver). Absent or unrecognized → times render UTC and say UTC.","type":["string","null"]},"x-timepoint-workspace":{"description":"Workspace uuid (W2) for the `find_money` pre-read only. Absent = the caller's personal space. A malformed id, an unknown workspace and one the caller is not a member of all answer 404 workspace_not_found.","format":"uuid","type":["string","null"]}},"required":[],"type":"object"},"method":"GET","name":"get_console","path":"/v1/console","requires_auth":true},{"description":"`GET /v1/delivered` — the runs delivered to the caller (P2c). Read-only,\nany authenticated identity, its own deliveries only. — The runs delivered to the caller, newest first: run id, title (the short title, ≤ 60 characters: the one written at intake, else derived from the question), `question` (the whole question as asked), kind, delivered_at and the documents available (`cockpit`, `report`, `portal`, `plan`). A run is listed exactly when its review is `delivered` and the caller is its client or a member of the workspace its engagement is linked to — the same rule every run read applies, so each listed id opens with `get_run`, `get_run_cockpit` and the other run reads. Paginated by `limit` (1–100, default 20) and `offset`; `next_offset` is null on the last page.","inputSchema":{"properties":{"limit":{"description":"Page size, 1–100 (default 20).","format":"int64","type":"integer"},"offset":{"description":"Rows to skip (default 0).","format":"int64","type":"integer"}},"required":[],"type":"object"},"method":"GET","name":"list_delivered_results","path":"/v1/delivered","requires_auth":true},{"description":"The active space's drill-down jobs, newest first (at most 200), optionally filtered to one kickoff. Each item carries its frozen `max_level` and `search_depth`, its committed research `spend_usd` (`{exa, openrouter}`), its `pending_proposals` count and the honest `research_enabled`/`research_note`. Any member of the space reads.","inputSchema":{"properties":{"kickoff_id":{"description":"Only jobs linked to this kickoff.","format":"uuid","type":"string"},"x-timepoint-workspace":{"description":"Workspace uuid to act in (W2). Absent = the caller's personal space. A malformed id, an unknown workspace and one the caller is not a member of all answer 404 workspace_not_found.","format":"uuid","type":["string","null"]}},"required":[],"type":"object"},"method":"GET","name":"list_drilldown_jobs","path":"/v1/drilldown/jobs","requires_auth":true},{"description":"Start a drill-down pass (D5, gate G11): a shallow territory pass (a scope named directly, or one of a kickoff's APPROVED scopes) or a pass rooted at an existing entity (org, role or person — its kind fixes the start level). Queued for the worker; `research_enabled` says whether anything researches it yet (false until D4's budget ledger and loop exist — see `research_note`). Administrators only (the same rule as approving research, `approvals::can_approve`): a provider or client member gets 403, an outsider or unknown kickoff/scope 404/422.","inputSchema":{"properties":{"Idempotency-Key":{"description":"M4: a caller-chosen key (≤200 chars). The same caller + key + method/path/body within 24h replays the first response (header idempotent-replay: true) instead of queuing a second pass; the same key with a different request answers 422 idempotency_key_reused; a concurrent duplicate answers 409 idempotency_in_progress (retryable). Absent = ordinary behavior.","type":["string","null"]},"body":{"additionalProperties":false,"description":"The JSON request body.","properties":{"dry_run":{"description":"Track E: `true` runs every check (the administrator rule, the scope or\nroot, levels, depth and templates) and answers 200 with `{dry_run,\nwould_admit, job, caps_usd, research_enabled, research_note, refusals,\nreserves: \"nothing\"}`: the job and the pass caps it would freeze, and\n`job.first_step`: the searches its first research call would run\n(every query of its plan) and their Exa estimate. No job, pass or\nbudget row is written.","type":"boolean"},"kickoff_id":{"description":"Link this pass to a kickoff in the same space (optional; required with\n`kickoff_scope_id`).","format":"uuid","type":["string","null"]},"kickoff_scope_id":{"description":"One of that kickoff's scope proposals, by id — must already be\nAPPROVED. Mutually exclusive with `scope_name` and `root_entity_id`.","type":["string","null"]},"max_level":{"description":"Deepest level, 0..=4 (L0..L4), frozen on the job (D6). Default: the\n`research_max_level_default` sim param (seeded 2, stopping before\npeople) for a territory pass; one level past the root's start for a\nrooted one, but never past that default without asking — a role or\nperson root needs an explicit 3 or 4.","format":"int64","type":["integer","null"]},"min_level":{"description":"A rooted pass only (`root_entity_id`): the level to start at, 0..=4\n(L0..L4), frozen on the job. Default: the root's own start (org 2,\nrole 3, person 4). A later start must still research the root's kind:\nan org may start at 2 or 4 (4 alone is its profile, policy and\nposition pass, without its roles or people), a role at 3 or 4, a\nperson at 4. Any other value, one past `max_level`, or one on a\nterritory pass is 422. A dry run echoes the start it resolved as\n`job.min_level`.","format":"int64","type":["integer","null"]},"profile_queries":{"description":"A rooted pass only: the Exa query templates for an org's L4 `profile`\nfacts, frozen on the job (a deepen of it inherits them). 1 to 8\ntemplates of at most 200 characters, each naming the org as `{name}`;\n`{place}` (the scope's state, when it names one) is the only other\nplaceholder, and no other brace is allowed. Default: generic built-in\ntemplates. Anything else, or templates on a territory pass, is 422.","items":{"type":"string"},"type":["array","null"]},"root_entity_id":{"description":"Drill an existing entity (org, role or person) instead of a territory;\nits kind fixes the start level (`drilldown::start_level_for`).","format":"uuid","type":["string","null"]},"scope_name":{"description":"A territory name, when not naming a kickoff scope. Mutually exclusive\nwith `root_entity_id`.","type":["string","null"]},"search_depth":{"description":"Search depth in result pages, 1 (the first page of results) to 100,\nbounded by the `research_search_depth_default` sim param's min/max;\ndefault that param (seeded 1). Frozen on the job; each input's pages\nare weighted by its centrality, and spend scales with it (D6).","format":"int64","type":["integer","null"]}},"type":"object"},"x-timepoint-workspace":{"description":"Workspace uuid to act in (W2). Absent = the caller's personal space. A malformed id, an unknown workspace and one the caller is not a member of all answer 404 workspace_not_found.","format":"uuid","type":["string","null"]}},"required":["body"],"type":"object"},"method":"POST","name":"create_drilldown_job","path":"/v1/drilldown/jobs","requires_auth":true},{"description":"One drill-down job in the active space: the job, its research steps, and a per-level proposal count summary (`{\"L0\": {\"pending\": .., \"approved\": .., \"rejected\": .., \"superseded\": ..}, ...}`). Unknown, in another space and not yours are the same 404. Any member of the space reads.","inputSchema":{"properties":{"id":{"description":"Drill-down job id","format":"uuid","type":"string"},"x-timepoint-workspace":{"description":"Workspace uuid to act in (W2). Absent = the caller's personal space. A malformed id, an unknown workspace and one the caller is not a member of all answer 404 workspace_not_found.","format":"uuid","type":["string","null"]}},"required":["id"],"type":"object"},"method":"GET","name":"get_drilldown_job","path":"/v1/drilldown/jobs/{id}","requires_auth":true},{"description":"Deepen from an approved entity or a pending/approved proposal off job `id` (D3/D5): one root calls a single deepen, several open one child job per root up to `drilldown_depth::MAX_FANOUT` (20) — a longer list is not refused, it is truncated, and `truncated` says so. Idempotent per (parent job, root, start level): re-deepening the same root at the same start (and the same `max_level` and `profile_queries`, when named) while its job is still queued/running returns that same job rather than a duplicate. Queued for the worker; `research_enabled`/`research_note` are the same honest state as create. Administrators only, same rule as `POST /v1/drilldown/jobs`.","inputSchema":{"properties":{"body":{"additionalProperties":false,"description":"The JSON request body.","properties":{"max_level":{"description":"Deepest level, 0..=4 (L0..L4), frozen on each child job. Default: D3's\ndepth policy (one level past the root's start) CAPPED at the parent\njob's max level (D6) — going deeper than the parent's max (e.g. an\norg's people, L3, under an L2 pass) takes an explicit value here.","format":"int64","type":["integer","null"]},"min_level":{"description":"The level each child job starts at, 0..=4 (L0..L4), frozen on it.\nDefault: each root's own start (org 2, role 3, person 4). A later\nstart must still research the root's kind: an org at 4 is its\nprofile, policy and position pass, without its roles or people (an\norg may start at 2 or 4, a role at 3 or 4, a person at 4). A start\nthat does not fit a root, or is past `max_level`, is 422 for a single\nroot and that root's entry in `errors` for several. An org's L4\ndeepen and its default deepen are separate child jobs.","format":"int64","type":["integer","null"]},"profile_queries":{"description":"The Exa query templates for an org's L4 `profile` facts, frozen on\neach child job. 1 to 8 templates of at most 200 characters, each\nnaming the org as `{name}`; `{place}` (the scope's state, when it\nnames one) is the only other placeholder, and no other brace is\nallowed; anything else is 422. Default: the parent job's, else\ngeneric built-in templates.","items":{"type":"string"},"type":["array","null"]},"roots":{"description":"1 to `drilldown_depth::MAX_FANOUT` roots, each naming exactly one of\n`entity_id`/`proposal_id`. One root deepens it directly; several open\none child job per root, up to the fan-out cap (`truncated` in the\nresponse says when the list was cut).","items":{"properties":{"entity_id":{"description":"Deepen a live entity, by id.","format":"uuid","type":["string","null"]},"proposal_id":{"description":"Deepen a pending or approved entity proposal, by id.","format":"uuid","type":["string","null"]}},"type":"object"},"type":"array"},"search_depth":{"description":"Search depth in result pages, 1–100 (bounded by the sim param);\ndefault the parent job's (D6).","format":"int64","type":["integer","null"]}},"required":["roots"],"type":"object"},"id":{"description":"The parent drill-down job to deepen","format":"uuid","type":"string"},"x-timepoint-workspace":{"description":"Workspace uuid to act in (W2). Absent = the caller's personal space. A malformed id, an unknown workspace and one the caller is not a member of all answer 404 workspace_not_found.","format":"uuid","type":["string","null"]}},"required":["id","body"],"type":"object"},"method":"POST","name":"deepen_drilldown_job","path":"/v1/drilldown/jobs/{id}/deepen","requires_auth":true},{"description":"A drill-down job's proposals in the active space, for approval (K4, PLAN policy 5): entity, edge, fact and merge proposals with `status` (pending | approved | rejected | superseded), level, a one-line `summary`, basis, sources, confidence, the `statement`, K2 lint `flags` (`[{lint, reasons}]`) and any decision already taken. Pending only unless `status=all`. Administrators only.","inputSchema":{"properties":{"id":{"description":"Drill-down job id","format":"uuid","type":"string"},"status":{"description":"`pending` (default) or `all`.","type":"string"},"x-timepoint-workspace":{"description":"Workspace uuid to act in (W2). Absent = the caller's personal space. A malformed id, an unknown workspace and one the caller is not a member of all answer 404 workspace_not_found.","format":"uuid","type":["string","null"]}},"required":["id"],"type":"object"},"method":"GET","name":"list_drilldown_proposals","path":"/v1/drilldown/jobs/{id}/proposals","requires_auth":true},{"description":"Approve or reject a batch of a drill-down job's proposals, all or nothing, in one transaction (K4, PLAN policy 5). Approve writes into the job's space with the proposed basis, never ACCUMULATED: an entity is resolved or minted (its id recorded on the proposal); an edge is stated through E2 (GROUNDED needs an https source); a GROUNDED fact becomes an assertion through W2's gate (a GENERATED fact is refused: no writer records one); a fact about the territory is recorded only. A merge is applied: its left entity is merged into its right one, which survives with the left's facts and edges (a still-pending entity proposal on one side is approved as the other side's entity instead of being minted). An edge or fact on an entity proposal needs that proposal approved first or in the same batch. Reject writes nothing to knowledge. Decisions are final: the same decision again is `unchanged`, the opposite one 409. Administrators only.","inputSchema":{"properties":{"body":{"additionalProperties":false,"description":"One decision over a batch of proposals.","properties":{"decision":{"enum":["approve","reject"],"type":"string"},"note":{"description":"Optional note recorded with the decision (at most 2000 characters).","type":["string","null"]},"proposal_ids":{"description":"The proposals to decide: kickoff proposal ids, or drill-down proposal\nuuids. 1 to 200, duplicates ignored.","items":{"type":"string"},"type":"array"}},"required":["decision","proposal_ids"],"type":"object"},"id":{"description":"Drill-down job id","format":"uuid","type":"string"},"x-timepoint-workspace":{"description":"Workspace uuid to act in (W2). Absent = the caller's personal space. A malformed id, an unknown workspace and one the caller is not a member of all answer 404 workspace_not_found.","format":"uuid","type":["string","null"]}},"required":["id","body"],"type":"object"},"method":"POST","name":"decide_drilldown_proposals","path":"/v1/drilldown/jobs/{id}/proposals/decide","requires_auth":true},{"description":"Decide a batch of a drill-down job's PENDING proposals by rule instead of by id (K4 \"smaller fixes\", PLAN policy 5): every proposal `rule` matches gets its own outcome, decided independently — one refusal never rolls back another (each proposal runs in its own SQL savepoint). `rule` filters on `kind` (entity | edge | fact | merge), `grounded_only`, `min_confidence`, `source_domains` (every source URL's host must be listed) and `no_lint_flags`, AND'd together; it must set at least one of these — an empty rule, which would match every pending proposal, is refused. At most 500 matches are decided per call; anything beyond that comes back `skipped` (re-run to continue, never silently dropped). `preview: true` runs the identical match-and-apply pass and reports it, but commits nothing; `preview: false` applies it. Approve NEVER approves a GENERATED proposal by rule — refused `generated_needs_individual_review`, since a rule note is one line for the whole batch, never the per-item note a GENERATED approval needs (decide it by id instead, `POST .../proposals/decide`) — and a merge only applies when both sides are already entities (a still-pending side needs its own approval by id first). Administrators only.","inputSchema":{"properties":{"body":{"additionalProperties":false,"description":"A `decide_by_rule` request (module docs): every PENDING proposal `rule`\nmatches gets `decision`, independently — `preview: true` reports it\nwithout writing anything.","properties":{"decision":{"enum":["approve","reject"],"type":"string"},"note":{"description":"Recorded on every proposal this call decides (at most 2000\ncharacters); never the per-item note a GENERATED approval needs —\nrule approval excludes GENERATED outright (module docs).","type":["string","null"]},"preview":{"description":"`true`: report exactly which proposals match and what would happen\nto each, and write nothing. `false`: apply it.","type":"boolean"},"rule":{"additionalProperties":false,"description":"A filter over PENDING drill-down proposals, for `decide_by_rule`. Every\nset field narrows the match (AND, never OR); at least one must be set —\n[`checked`](Self::checked) refuses the empty rule, which would otherwise\nmatch every pending proposal in the job.","properties":{"grounded_only":{"description":"Only `GROUNDED` proposals match when true. Approve never applies to a\n`GENERATED` proposal regardless of this flag (module docs, \"Rule\nnever approves GENERATED\"); this narrows the *match*, not just the\napply step, so a rule scoped to `GENERATED` work is honestly empty\nrather than reporting every match refused.","type":"boolean"},"kind":{"description":"Only these proposal kinds match: `entity`, `edge`, `fact`, `merge`.","items":{"type":"string"},"type":["array","null"]},"min_confidence":{"description":"Only proposals with confidence at or above this match, in (0, 1]. A\nproposal with no recorded confidence never matches when this is set.","format":"double","type":["number","null"]},"no_lint_flags":{"description":"Only proposals with no K2 lint flags match when true.","type":"boolean"},"source_domains":{"description":"Only proposals whose EVERY source URL's host is in this list match\n(case-insensitive, exact host match, e.g. `[\"example.org\"]`). A\nproposal with no sources never matches when this is set.","items":{"type":"string"},"type":["array","null"]}},"type":"object"}},"required":["rule","decision","preview"],"type":"object"},"id":{"description":"Drill-down job id","format":"uuid","type":"string"},"x-timepoint-workspace":{"description":"Workspace uuid to act in (W2). Absent = the caller's personal space. A malformed id, an unknown workspace and one the caller is not a member of all answer 404 workspace_not_found.","format":"uuid","type":["string","null"]}},"required":["id","body"],"type":"object"},"method":"POST","name":"decide_drilldown_proposals_by_rule","path":"/v1/drilldown/jobs/{id}/proposals/decide_by_rule","requires_auth":true},{"description":"Research spend for a drill-down job (D4, gate G11). A PASS is a root job plus every deepen job descended from it; they share one cap per provider (Exa, OpenRouter), frozen from `run.sim_params` when the root job was created. `pass.providers` gives each provider's cap, reserved (held by a live call), spent and remaining; `pass.jobs` each job's share; `job` this job's own share; `stop_reason` why it stopped (a `budget_stopped` job names the provider). Amounts in nanodollars and USD. Unknown, in another space and not yours are the same 404. Any member of the space reads.","inputSchema":{"properties":{"id":{"description":"Drill-down job id","format":"uuid","type":"string"},"x-timepoint-workspace":{"description":"Workspace uuid to act in (W2). Absent = the caller's personal space. A malformed id, an unknown workspace and one the caller is not a member of all answer 404 workspace_not_found.","format":"uuid","type":["string","null"]}},"required":["id"],"type":"object"},"method":"GET","name":"get_drilldown_job_spend","path":"/v1/drilldown/jobs/{id}/spend","requires_auth":true},{"description":"Set the research caps of job `id`'s pass (D4, gate G11): the root job and all its deepen jobs. Each value must lie within its `run.sim_params` bounds (`research_pass_cap_exa_usd`, `research_pass_cap_openrouter_usd`); the database logs every change with its actor in `run.research_cap_changes`. A raise does not restart a stopped job: deepen from it, and the child spends against the new cap. Administrators only, the same rule as starting a pass: a provider or client member gets 403, an outsider 404.","inputSchema":{"properties":{"body":{"additionalProperties":false,"description":"The JSON request body.","properties":{"exa_usd":{"description":"The pass's new Exa cap in USD, within `research_pass_cap_exa_usd`'s\nbounds. Omit to keep it.","format":"double","type":["number","null"]},"openrouter_usd":{"description":"The pass's new OpenRouter cap in USD, within\n`research_pass_cap_openrouter_usd`'s bounds. Omit to keep it.","format":"double","type":["number","null"]}},"type":"object"},"id":{"description":"Any drill-down job of the pass","format":"uuid","type":"string"},"x-timepoint-workspace":{"description":"Workspace uuid to act in (W2). Absent = the caller's personal space. A malformed id, an unknown workspace and one the caller is not a member of all answer 404 workspace_not_found.","format":"uuid","type":["string","null"]}},"required":["id","body"],"type":"object"},"method":"POST","name":"set_drilldown_pass_caps","path":"/v1/drilldown/jobs/{id}/spend/caps","requires_auth":true},{"description":"Create a typed, directed, scored, attributed edge between two entities the caller may write in the request's space (personal, or the workspace named by `x-timepoint-workspace`; both endpoints must be in that space). `vocabulary` is stamped by the server from the pinned TT relation; a symmetric pair is sorted into canonical order before the write. `basis` may be GROUNDED (needs >=1 https:// source) or GENERATED; ACCUMULATED is refused (hard policy 3 — that basis is written only by a run). Every other rule (relation exists, endpoint kinds allowed, attribute schema, tenure order, one live statement per scope) is enforced by the database and reported by constraint name.","inputSchema":{"properties":{"body":{"description":"`POST /v1/edges` request body.","properties":{"attributes":{"description":"TT's closed per-relation attribute schema (`valid_from`/`valid_to`/`subject`,\ndepending on the relation). Absent means `{}`."},"basis":{"description":"GROUNDED (needs `source_urls`) or GENERATED — never ACCUMULATED (that\nbasis is written only by a run, through a different door).","example":"GROUNDED","type":"string"},"from_entity_id":{"format":"uuid","type":"string"},"observed_at":{"description":"When the evidence was seen. Defaults to now when omitted.","format":"date-time","type":["string","null"]},"relation_kind":{"description":"A TT `tt-relations/1.0` relation id, e.g. `holds-office`, `knows`.","example":"holds-office","type":"string"},"score":{"description":"Belief that the edge holds as stated, in (0, 1].","example":0.9,"format":"float","type":"number"},"source_urls":{"items":{"type":"string"},"type":"array"},"to_entity_id":{"format":"uuid","type":"string"}},"required":["from_entity_id","to_entity_id","relation_kind","basis","score"],"type":"object"},"x-timepoint-workspace":{"description":"Workspace uuid to act in (W2). Absent = the caller's personal space. A malformed id, an unknown workspace and one the caller is not a member of all answer 404 workspace_not_found; a client member writing answers 403 workspace_read_only.","format":"uuid","type":["string","null"]}},"required":["body"],"type":"object"},"method":"POST","name":"create_edge","path":"/v1/edges","requires_auth":true},{"description":"\"Delete\" an edge: write a tombstone row — `supersedes` = this one, `retracted = true` — restating the SAME relation, endpoints and attributes under the SAME basis rules as any row (a GROUNDED retraction names its own source). `{id}` must be the current, non-retracted edge and the caller's own. The list endpoint stops showing it as soon as this commits.","inputSchema":{"properties":{"body":{"description":"`POST /v1/edges/{id}/retract` request body. Attributes are never restated\nhere — a retraction withdraws the predecessor's exact claim.","properties":{"basis":{"type":"string"},"observed_at":{"format":"date-time","type":["string","null"]},"score":{"format":"float","type":"number"},"source_urls":{"items":{"type":"string"},"type":"array"}},"required":["basis","score"],"type":"object"},"id":{"description":"The current edge id to retract","format":"uuid","type":"string"},"x-timepoint-workspace":{"description":"Workspace uuid to act in (W2). Absent = the caller's personal space. A malformed id, an unknown workspace and one the caller is not a member of all answer 404 workspace_not_found; a client member writing answers 403 workspace_read_only.","format":"uuid","type":["string","null"]}},"required":["id","body"],"type":"object"},"method":"POST","name":"retract_edge","path":"/v1/edges/{id}/retract","requires_auth":true},{"description":"\"Update\" an edge: insert a new row with `supersedes` = this one, restating the SAME relation and endpoints under a revised claim (new basis/score/sources/attributes). `{id}` must be the current (head, non-retracted) edge and the caller's own; superseding an already-superseded or retracted row is refused typed. Same basis rule as create: GROUNDED or GENERATED, never ACCUMULATED.","inputSchema":{"properties":{"body":{"description":"`POST /v1/edges/{id}/supersede` request body.","properties":{"attributes":{"description":"Revised attributes. Omitted = keep the predecessor's own (a score-only\ncorrection never silently blanks a tenure nobody meant to touch)."},"basis":{"type":"string"},"observed_at":{"format":"date-time","type":["string","null"]},"score":{"format":"float","type":"number"},"source_urls":{"items":{"type":"string"},"type":"array"}},"required":["basis","score"],"type":"object"},"id":{"description":"The current edge id to supersede","format":"uuid","type":"string"},"x-timepoint-workspace":{"description":"Workspace uuid to act in (W2). Absent = the caller's personal space. A malformed id, an unknown workspace and one the caller is not a member of all answer 404 workspace_not_found; a client member writing answers 403 workspace_read_only.","format":"uuid","type":["string","null"]}},"required":["id","body"],"type":"object"},"method":"POST","name":"supersede_edge","path":"/v1/edges/{id}/supersede","requires_auth":true},{"description":"Resolve-or-mint an entity by (kind, display_name) — the TOOLS gallery's way\ninto the registry. Thin by design: `entities::resolve_or_mint` is the ONE\nminter (this module's header contract); `frame_id` rides as `\"\"` so resolution\nis purely name-aware (slugify(display_name)) — a frame id here would make the\nname-blind step-1 lookup resolve the wrong entity. Idempotent: the same\n(kind, name) POSTed again returns the same `entity_id`. Awareness is NOT\nwritten here — `put_awareness` stays the one writer of `entity.awareness`. — Resolve-or-mint a registry entity by `{kind, display_name}` (kind ∈ person|org|market|role; name trimmed, nonempty, ≤200 chars). Resolution is name-aware and idempotent — POSTing the same (kind, name) again returns the SAME entity_id, never a duplicate row. Returns `{entity_id, kind, slug, path, display_name}`. Awareness is a separate write: PUT /v1/entities/{id}/awareness.","inputSchema":{"properties":{"body":{"description":"The creation request: a kind and a human name — resolution is by name only.","properties":{"display_name":{"description":"The display name; trimmed, nonempty, ≤200 chars. Resolution slugifies this\nname — POSTing the same (kind, name) again resolves to the SAME entity.","example":"Jerome Powell","maxLength":200,"type":"string"},"kind":{"description":"person | org | market | role (the schema's four legal kinds).","example":"person","type":"string"}},"required":["kind","display_name"],"type":"object"},"x-timepoint-workspace":{"description":"Workspace uuid to act in (W2). Absent = the caller's personal space. A malformed id, an unknown workspace and one the caller is not a member of all answer 404 workspace_not_found; a client member writing answers 403 workspace_read_only.","format":"uuid","type":["string","null"]}},"required":["body"],"type":"object"},"method":"POST","name":"create_entity","path":"/v1/entities","requires_auth":true},{"description":"The dossier by registry path `/kind/slug`. A kind outside person|org|market|role can\nhold no row (schema CHECK), so the lookup itself yields the typed 404 — no\nseparate validation branch to drift.","inputSchema":{"properties":{"kind":{"description":"person | org | market","type":"string"},"slug":{"description":"Registry slug (kebab-case)","type":"string"},"x-timepoint-workspace":{"description":"Workspace uuid to act in (W2). Absent = the caller's personal space. A malformed id, an unknown workspace and one the caller is not a member of all answer 404 workspace_not_found; a client member writing answers 403 workspace_read_only.","format":"uuid","type":["string","null"]}},"required":["kind","slug"],"type":"object"},"method":"GET","name":"get_entity_by_path","path":"/v1/entities/by-path/{kind}/{slug}","requires_auth":true},{"description":"Owner-scoped name/slug search for the cast picker. Write-scoped (the\ncaller's own records only) so a grant cannot put someone else's dossier\ninto a simulation that will accrue assertions. Empty `q` returns no hits\nrather than listing the registry.","inputSchema":{"properties":{"q":{"description":"Name or slug fragment, at most 160 characters","type":"string"},"x-timepoint-workspace":{"description":"Workspace uuid to act in (W2). Absent = the caller's personal space. A malformed id, an unknown workspace and one the caller is not a member of all answer 404 workspace_not_found; a client member writing answers 403 workspace_read_only.","format":"uuid","type":["string","null"]}},"required":[],"type":"object"},"method":"GET","name":"search_entities","path":"/v1/entities/search","requires_auth":true},{"description":"The dossier by stable registry id. Typed 404 for an unknown id.","inputSchema":{"properties":{"id":{"description":"Stable registry entity id","format":"uuid","type":"string"},"x-timepoint-workspace":{"description":"Workspace uuid to act in (W2). Absent = the caller's personal space. A malformed id, an unknown workspace and one the caller is not a member of all answer 404 workspace_not_found; a client member writing answers 403 workspace_read_only.","format":"uuid","type":["string","null"]}},"required":["id"],"type":"object"},"method":"GET","name":"get_entity","path":"/v1/entities/{id}","requires_auth":true},{"description":"Add ONE assertion to a dossier, as a person. Goes through\n`entities::commit_assertions` — the same gate a simulation goes through,\nnever around it. — Write ONE assertion to an entity's dossier on a person's authority: `{attribute, value, note?, source_url?}`. The row rides `entities::commit_assertions` — the SAME six-rule gate the simulation path uses (kebab attribute, bounded value, confidence in (0,1], the entity must exist, duplicates skipped) — so the dossier never writes around the gate. Provenance is honest and explicit: basis GROUNDED, source `console_manual` when the person typed it and `console_link <url>` when they approved it from a link they pasted, with the note appended after ` :: `; `run_id` is NULL because no run authored it. A gate skip (typically an identical assertion already on the dossier) is NOT an error: it answers 200 `{written: false, reason}` and says so.","inputSchema":{"properties":{"body":{"description":"One assertion a PERSON is adding: typed by hand, or approved from a link.","properties":{"attribute":{"description":"The property name — kebab-case, ≤64 chars (the gate's own rule).","example":"current-role","type":"string"},"note":{"description":"Optional note recorded alongside the provenance, ≤280 chars.","type":["string","null"]},"source_url":{"description":"The link this value was approved FROM, when it came from one. Present\n⇒ the row's provenance names that document; absent ⇒ the row records\nthat the person typed it themselves. Never inferred.","type":["string","null"]},"value":{"description":"The value, as the person states it. Nonempty, ≤500 chars.","example":"Chair of the Federal Reserve","type":"string"}},"required":["attribute","value"],"type":"object"},"id":{"description":"Stable registry entity id","format":"uuid","type":"string"},"x-timepoint-workspace":{"description":"Workspace uuid to act in (W2). Absent = the caller's personal space. A malformed id, an unknown workspace and one the caller is not a member of all answer 404 workspace_not_found; a client member writing answers 403 workspace_read_only.","format":"uuid","type":["string","null"]}},"required":["id","body"],"type":"object"},"method":"POST","name":"add_entity_assertion","path":"/v1/entities/{id}/assertions","requires_auth":true},{"description":"Flip the awareness switch for an entity: validate → 404 unknown entity →\nupsert with `updated_by` = the authenticated caller → return the new state. — Set the per-entity awareness switch (Media M1 + G1 research): `{enabled, level?, research?}` where level (1 daily · 2 six-hourly · 3 hourly) is required when enabling. `research` is TRI-STATE: omit the field to leave the stored research_source unchanged (a level-only PUT never wipes a live source); send explicit `null` to clear it; send a string to set it — closed set, currently only `\"openrouter_web\"` (typed 422 `invalid_research_source` otherwise). Upserts `entity.awareness` recording `updated_by` = the caller; disable keeps the row, its last level AND its research_source (the sweep gates on enabled); disabling a never-enabled entity is an idempotent no-op (a level alone never mints a row). Returns `{entity_id, awareness: {enabled, level|null, research|null}, updated_at, updated_by}`. The monitor sweep (B6.1) fires enabled+sourced rows on cadence at 1 credit per fired look, billed on use to `updated_by`; insufficient balance auto-clears the source. Monthly billing arrives with subscriptions (M3) — the `awareness_l1..3` price rows are served via /v1/prices meanwhile.","inputSchema":{"properties":{"body":{"description":"The awareness switch request. `level` is required when enabling; on disable a\nprovided level updates the kept row's cadence. `research` is tri-state (see\nits field doc) so a level-only PUT can never wipe a live research source.","properties":{"enabled":{"description":"Target switch state: monitoring on (true) or off (false).","type":"boolean"},"level":{"description":"Cadence level — 1 daily · 2 six-hourly · 3 hourly. Required when enabling\n(typed 422 `level_required` otherwise); must be 1..=3 (typed 422\n`invalid_level`).","example":2,"format":"int32","maximum":3,"minimum":1,"type":["integer","null"]},"research":{"description":"Research source, TRI-STATE (G1): field ABSENT = leave `research_source`\nunchanged (the TOOLS level-select PUTs `{enabled, level}` without it);\nexplicit `null` = clear it; a string = set it — closed set, currently only\n`\"openrouter_web\"` (typed 422 `invalid_research_source` otherwise). The\ndouble-Option carries the distinction: outer None = absent, `Some(None)` =\nexplicit null, `Some(Some(s))` = set.","example":"openrouter_web","type":["string","null"]}},"required":["enabled"],"type":"object"},"id":{"description":"Stable registry entity id","format":"uuid","type":"string"},"x-timepoint-workspace":{"description":"Workspace uuid to act in (W2). Absent = the caller's personal space. A malformed id, an unknown workspace and one the caller is not a member of all answer 404 workspace_not_found; a client member writing answers 403 workspace_read_only.","format":"uuid","type":["string","null"]}},"required":["id","body"],"type":"object"},"method":"PUT","name":"put_awareness","path":"/v1/entities/{id}/awareness","requires_auth":true},{"description":"Correct an entity's display name in place. — Correct an entity's display name (trimmed, nonempty, ≤200 chars). The SLUG and therefore the registry path are deliberately untouched — ids and paths are stable forever and every link already written to this dossier keeps resolving; only the name and the `content_hash` that names the row change. Returns `{entity_id, display_name, path, content_hash, path_changed: false, language_flags}` (K2's entity-language lint; `language_flags` is `[]` when clean).","inputSchema":{"properties":{"body":{"description":"A display-name correction. The path and id never move.","properties":{"display_name":{"description":"The corrected name; trimmed, nonempty, ≤200 chars.","example":"Jerome H. Powell","maxLength":200,"type":"string"}},"required":["display_name"],"type":"object"},"id":{"description":"Stable registry entity id","format":"uuid","type":"string"},"x-timepoint-workspace":{"description":"Workspace uuid to act in (W2). Absent = the caller's personal space. A malformed id, an unknown workspace and one the caller is not a member of all answer 404 workspace_not_found; a client member writing answers 403 workspace_read_only.","format":"uuid","type":["string","null"]}},"required":["id","body"],"type":"object"},"method":"PUT","name":"put_entity_display_name","path":"/v1/entities/{id}/display-name","requires_auth":true},{"description":"Current (live, unsuperseded, non-retracted) edges touching this entity from either side, direction-aware: `label` reads correctly whichever side the entity is on (outgoing uses the relation's own label, incoming uses its `inverse_label`, symmetric reads the same either way), and every row carries `nature` so a GENERATED `knows` edge never reads as part of a structural path. `?structural_only=true` filters to `nature = structural` only. Scoped to the request's space (`x-timepoint-workspace`; absent = personal).","inputSchema":{"properties":{"id":{"description":"Stable registry entity id","format":"uuid","type":"string"},"structural_only":{"description":"Filter to nature = structural","type":"boolean"},"x-timepoint-workspace":{"description":"Workspace uuid to act in (W2). Absent = the caller's personal space. A malformed id, an unknown workspace and one the caller is not a member of all answer 404 workspace_not_found; a client member writing answers 403 workspace_read_only.","format":"uuid","type":["string","null"]}},"required":["id"],"type":"object"},"method":"GET","name":"list_entity_edges","path":"/v1/entities/{id}/edges","requires_auth":true},{"description":"Read a link and PROPOSE assertions about this entity. Writes nothing. — Read one pasted link and PROPOSE candidate assertions about this entity. This endpoint WRITES NOTHING — it is a proposal a person approves one by one via POST /v1/entities/{id}/assertions, which is the only path to the record. The fetch is real (tp-llm's ChainFetcher: plain HTTP first, firecrawl only when the site walls it) and every failure is typed by name: 422 invalid_url | unsupported_url | nothing_to_read, 402 llm_no_headroom, 502 fetch_failed | llm_failed, 503 reading_unavailable. Each candidate must carry a quote that appears VERBATIM in the fetched page; ones that do not are dropped in code and counted in `dropped`, never repaired. Returns `{source: {url, final_url, title, fetched_at, chars, truncated}, candidates: [{attribute, value, quote}], dropped: [reason], cost_usd}`.","inputSchema":{"properties":{"body":{"description":"The link a person wants read for this dossier.","properties":{"url":{"description":"http/https, ≤2000 chars.","example":"https://en.wikipedia.org/wiki/Jerome_Powell","type":"string"}},"required":["url"],"type":"object"},"id":{"description":"Stable registry entity id","format":"uuid","type":"string"},"x-timepoint-workspace":{"description":"Workspace uuid to act in (W2). Absent = the caller's personal space. A malformed id, an unknown workspace and one the caller is not a member of all answer 404 workspace_not_found; a client member writing answers 403 workspace_read_only.","format":"uuid","type":["string","null"]}},"required":["id","body"],"type":"object"},"method":"POST","name":"entity_import_preview","path":"/v1/entities/{id}/import-preview","requires_auth":true},{"description":"The CRM export of this workspace's modeled entities.","inputSchema":{"properties":{"engagement_scope":{"description":"`all` (default): every research pass. `in`: leave out the passes a\nperson ruled out of scope for any engagement linked to this workspace\n(and their deepen children), the facts approved from them, the edges\nthey sourced and the entities they minted that nothing else supports.","type":["string","null"]},"format":{"description":"`json` (default) or `csv`. CSV needs `part`.","type":["string","null"]},"include_generated":{"description":"Include GENERATED facts (default false).","type":["boolean","null"]},"part":{"description":"One of accounts, contacts, roles, relationships, facts. Absent = all (JSON only).","type":["string","null"]},"since":{"description":"Delta export: an RFC 3339 instant, or `checkpoint` for the workspace's\nlast recorded export. Only records created or touched since, plus a\n`removed` part (relationships retracted or replaced since).","type":["string","null"]},"view":{"description":"`provider` (default): the full record. `client`: what the client's own\nread-delivered surfaces and wrap-up show — relationships follow the\nfacts rule, each fact's `source` is its first URL only, and\n`research_jobs` is left out.","type":["string","null"]},"x-timepoint-workspace":{"description":"The workspace to export (required). A malformed id, an unknown workspace and one the caller is not a member of all answer 404 workspace_not_found.","format":"uuid","type":"string"}},"required":["x-timepoint-workspace"],"type":"object"},"method":"GET","name":"export_entities","path":"/v1/export/entities","requires_auth":true},{"description":"`GET /v1/files` — the files delivered to the caller. — The documents delivered to the caller (briefs, playbooks, target lists, CRM exports…): file id, folder, name, title, content type, size, sha256 and delivery time. Listed once an admin has delivered it, to the engagement's client or a member of the workspace the engagement is linked to. Read one with `get_file`. A film (`storage: \"object\"`) names its `poster_file_id` and `captions_file_id` (not listed on their own) and its `duration_seconds`; open any of them with `stream_file`. A report deck (a PowerPoint file in `Report decks`) carries `deck: {pages, length, generated_at}`; download it with `get_file_link`.","inputSchema":{"properties":{},"required":[],"type":"object"},"method":"GET","name":"list_my_files","path":"/v1/files","requires_auth":true},{"description":"`GET /v1/files/{id}` — one delivered file, with its text. — One delivered document: its metadata and, for a text format (markdown, CSV, JSON, plain text), its full `text` (up to 2 MB). PDFs return embedded text when extractable with `text_source=extracted`; this is never OCR and failure is non-fatal. Stored media (a film, poster or captions file) carries no text either; open it with `stream_file`. A report deck (PowerPoint) carries no text; download it with `get_file_link`. Unknown and not-yours are the same 404.","inputSchema":{"properties":{"id":{"description":"file id (from `list_my_files`)","format":"uuid","type":"string"}},"required":["id"],"type":"object"},"method":"GET","name":"get_file","path":"/v1/files/{id}","requires_auth":true},{"description":"GET /v1/files/{id}/link","inputSchema":{"properties":{"id":{"format":"uuid","type":"string"}},"required":["id"],"type":"object"},"method":"GET","name":"get_file_link","path":"/v1/files/{id}/link","requires_auth":true},{"description":"Watch or open a film, poster or captions file delivered to the caller: a 302 to a presigned URL valid for 5 minutes, or with `format=json` the same URL as `{url, expires_at}`. Never cached. A poster or captions file opens only while its film is delivered. Unknown, not delivered, withdrawn and not-yours are the same 404.","inputSchema":{"properties":{"format":{"description":"`json` answers `{url, expires_at}` with 200 instead of a 302 (for a page\nthat authenticates with a bearer header, which `<video src>` cannot send).","type":"string"},"id":{"description":"File id (from list_my_files)","format":"uuid","type":"string"}},"required":["id"],"type":"object"},"method":"GET","name":"stream_file","path":"/v1/files/{id}/stream","requires_auth":true},{"description":"Tell Timepoint what happened: mint the evidence, never the verdict. — Attest what actually happened against one of the caller's UNRESOLVED forecasts (the YESTERDAY feedback loop). Takes a structured `outcome` (`confirmed` | `closed_differently` | `still_open`), a free-text `note`, or both. `confirmed`/`closed_differently`/note-only mint a `user_attested` GROUNDED moment on the forecast's run — participants = the predicate's `participants_any` registry paths, label `USER ATTESTED: {composed}` (structured outcomes compose `confirmed as forecast — {claim}` / `closed differently than forecast — {claim}`, with the note as ` · {note}`), provenance carries `forecast_id` + `attest_outcome` — then attempt a NORMAL classification (non-fatal; a closed LLM gate leaves the moment honestly untagged). `still_open` mints NOTHING: `run.forecasts` has no acknowledgement column, so the response is a typed no-op (`written: false`) stating that nothing was written. The forecast is NEVER resolved inline: the hourly sweep owns every verdict, and it judges only after the resolution date has fully elapsed.","inputSchema":{"properties":{"body":{"description":"Attest request: a structured outcome, the user's free-text account, or both\n(at least one; the structured chip and the detail line compose).","properties":{"note":{"description":"What happened, in the user's words (nonempty, ≤500 chars). Optional when\n`outcome` rides the request; required without it (the original contract,\nunchanged).","example":"the council approved the port expansion on Friday","type":["string","null"]},"outcome":{"description":"One-click structured outcome: `confirmed` (it happened as forecast),\n`closed_differently` (it resolved another way), or `still_open` (no\nobservation yet — mints NOTHING, returns a typed no-op). Any other\nstring is a typed 422 `invalid_outcome`.","example":"confirmed","type":["string","null"]}},"type":"object"},"id":{"description":"Forecast id","format":"uuid","type":"string"}},"required":["id","body"],"type":"object"},"method":"POST","name":"attest_forecast","path":"/v1/forecasts/{id}/attest","requires_auth":true},{"description":"Make a HIT possible: classify the stored claim and anchor the predicate. — Retrofit a matchable Lens-A event node onto one of the caller's UNRESOLVED forecasts whose predicate has `lens_a: null` (the quicksim-minted shape that can NEVER score a HIT). Classifies the stored claim through the TT classifier (one NORMAL protocol pass — real model, reject-never-repair); when a node lands at mass ≥ 0.35 (directly under lens_a, or via the Lens-B shadow bridge), writes `predicate ‖ {lens_a, min_mass: 0.35, lens_a_basis, matchable_at}` and mirrors the node into `taxonomy_id`. When the classifier abstains, rejects twice, lands only sub-threshold mass, or the LLM gate is closed, NOTHING is written and the refusal is typed with the honest reason. Idempotent on an already-matchable forecast: 200 with the standing node, `already_matchable: true`, no classifier call, no write. Never touches a resolved forecast.","inputSchema":{"properties":{"id":{"description":"Forecast id","format":"uuid","type":"string"}},"required":["id"],"type":"object"},"method":"POST","name":"make_forecast_matchable","path":"/v1/forecasts/{id}/matchable","requires_auth":true},{"description":"The caller's invites. Codes are NOT here — only their sha256 was ever stored. — The caller's own invites, newest first: `{invites: [{id, trial_cr, sponsor_cap_cr, created_at, expires_at, redeemed_at, agent_label, revoked_at}…]}`. The codes are NOT returned and cannot be — only their sha256 was ever stored. `redeemed_at`/`revoked_at` are null when the state has not happened: absence is labeled, never invented.","inputSchema":{"properties":{},"required":[],"type":"object"},"method":"GET","name":"list_invites","path":"/v1/invites","requires_auth":true},{"description":"Mint an agent invite. The code appears exactly once, in this response. — Mint an agent invite code. The plaintext `tpi_…` code is returned EXACTLY ONCE (only its sha256 is stored) together with a copy-pasteable `snippet` your agent can follow to connect. `trial_cr` (0..=100, default 25) is the free grant the redeeming agent receives; `sponsor_cap_cr` (default 0) is a CEILING on credits moved from your balance at redemption — nothing is held now, and if your balance is short at redeem time the transfer is what exists and the redemption response says so. Agent-scoped tokens are refused with 403 `agents_cannot_invite`: agents do not recruit agents.","inputSchema":{"properties":{"body":{"description":"The JSON request body.","properties":{"sponsor_cap_cr":{"description":"A CEILING on credits moved from YOUR balance to the agent at redemption\n(default 0 — sponsor nothing). Nothing is held now: the transfer is\nchecked against your real balance at redeem time, and a short balance\nsponsors what exists and says so.","format":"int64","type":["integer","null"]},"trial_cr":{"description":"Free trial credits the redeeming agent is granted, `0..=100`\n(default 25). Granted from nothing — this is the house's welcome, not\nyour balance.","format":"int64","type":["integer","null"]},"ttl_days":{"description":"Days until the code expires, `1..=30` (default 7).","format":"int64","type":["integer","null"]}},"type":"object"}},"required":["body"],"type":"object"},"method":"POST","name":"create_invite","path":"/v1/invites","requires_auth":true},{"description":"Revoke an invite. Before redemption: the code dies. After: the agent dies. — Revoke one of your invites — creator only. BEFORE redemption the code stops resolving. AFTER redemption this also kills the agent: its registry row is deleted (the next call it makes answers 401) and its remaining AVAILABLE balance is transferred back to you as a paired `transfer` entry. Credits already HELD against an in-flight run are not yanked — those runs settle or release normally, and the response says so in `held_cr_still_running`. An unknown invite and somebody else's invite are the same 404 — a refusal never confirms existence.","inputSchema":{"properties":{"id":{"description":"invite id","format":"uuid","type":"string"}},"required":["id"],"type":"object"},"method":"POST","name":"revoke_invite","path":"/v1/invites/{id}/revoke","requires_auth":true},{"description":"The active space's kickoffs, newest first (at most 100): `{items: [{id, workspace_id, title, question, status, compiled_run_id, created_by, created_at, updated_at}]}`. Any member reads, a client member included. A kickoff a retraction withdrew is left out unless `include_withdrawn=true` (never for a client member).","inputSchema":{"properties":{"include_withdrawn":{"description":"Also list kickoffs a retraction withdrew, as tombstones (no question,\ntitle \"Withdrawn kickoff\"). Never for a client member. Default false.","type":"boolean"},"x-timepoint-workspace":{"description":"Workspace uuid to act in (W2). Absent = the caller's personal space. A malformed id, an unknown workspace and one the caller is not a member of all answer 404 workspace_not_found.","format":"uuid","type":["string","null"]}},"required":[],"type":"object"},"method":"GET","name":"list_kickoffs","path":"/v1/kickoffs","requires_auth":true},{"description":"Start a kickoff (status `draft`) in the active space: the `x-timepoint-workspace` workspace, or the caller's personal space without the header. Provider and admin members write; a client member is refused. Optional `extracted_entities` / `proposed_edges` / `scopes` hand-author proposals exactly as `POST /v1/kickoffs/{id}/proposals` does (administrators only, same checks; the kickoff then starts `extracted`); without them nothing changes.","inputSchema":{"properties":{"Idempotency-Key":{"description":"M4: a caller-chosen key (≤200 chars). The same caller + key + method/path/body within 24h replays the first response (header idempotent-replay: true) instead of re-running; the same key with a different request answers 422 idempotency_key_reused; a concurrent duplicate answers 409 idempotency_in_progress (retryable). Absent = ordinary behavior.","type":["string","null"]},"body":{"description":"The JSON request body.","properties":{"dry_run":{"description":"Track E: `true` runs every check, including the proposal checks, inside\na transaction that is rolled back, and answers 200 with `{dry_run,\nwould_admit, kickoff, refusals, reserves: \"nothing\"}`: the document\nthat would be created. Nothing is saved.","type":"boolean"},"extracted_entities":{"description":"Optional admin-authored proposals (Part E): the same items and checks\nas `POST /v1/kickoffs/{id}/proposals`. Any present requires the\nadministrator rule (`approvals::can_approve`), and the kickoff then\nstarts `extracted`.","items":{"additionalProperties":false,"properties":{"basis":{"description":"`docs/kickoff/kickoff.schema.json` `#/$defs/basis`. Deliberately only the two\nbases a kickoff may propose (PLAN.md policy 3: a kickoff never writes\n`ACCUMULATED`, so that variant does not exist here — an incoming `\"ACCUMULATED\"`\nvalue fails to deserialize instead of needing a runtime check).","enum":["GROUNDED","GENERATED"],"type":"string"},"confidence":{"description":"A confidence value in `[0, 1]`, or absent. `#/$defs/confidence`.\n\nPlain `Option<f64>` cannot express the schema's `minimum`/`maximum`, so this\nwrapper validates on the way in: `serde_json::from_str` rejects an out-of-range\nvalue with a `serde::de::Error`, the same failure mode as every other schema\nviolation in this module, rather than needing a second validation pass.","format":"double","type":["number","null"]},"entity_id":{"format":"uuid","type":["string","null"]},"id":{"type":"string"},"kind":{"type":"string"},"name":{"type":"string"},"notes":{"type":["string","null"]},"sources":{"items":{"type":"string"},"type":"array"}},"required":["id","kind","name","basis","sources","confidence"],"type":"object"},"type":"array"},"notes":{"description":"Raw notes, one entry each; stored with the caller as author.","items":{"type":"string"},"type":"array"},"proposed_edges":{"items":{"additionalProperties":false,"description":"A proposed relation between two `ExtractedEntity` proposals, or an\nalready-aligned `entity.entities` id. T1's kebab-case vocabulary\n(`docs/proposals/ENTITY-RELATIONS.md`); `basis`/`score`/`sources` are beta-side\ncolumns T1 deliberately leaves to the consumer. `attributes` carries relation\nattributes such as `holds-office`'s `valid_from`/`valid_to` tenure, as plain\nstrings (T1's closed attribute-type set is `date` and `text`, both strings).","properties":{"attributes":{"additionalProperties":{"type":"string"},"propertyNames":{"type":"string"},"type":"object"},"basis":{"description":"`docs/kickoff/kickoff.schema.json` `#/$defs/basis`. Deliberately only the two\nbases a kickoff may propose (PLAN.md policy 3: a kickoff never writes\n`ACCUMULATED`, so that variant does not exist here — an incoming `\"ACCUMULATED\"`\nvalue fails to deserialize instead of needing a runtime check).","enum":["GROUNDED","GENERATED"],"type":"string"},"from":{"type":"string"},"id":{"type":"string"},"relation":{"description":"A relation id in T1's kebab-case vocabulary (`docs/proposals/ENTITY-RELATIONS.md`\n2.3): lowercase ascii words joined by single hyphens. Kept as a validated string,\nnot a closed enum, because the vocabulary is not yet pinned into beta (T2/T3);\nD2/K3 check the value against the pinned vocabulary at write/compile time.","type":"string"},"score":{"description":"A proposed-edge score in `(0, 1]`: belief the edge holds as stated (T1 draft,\n`docs/proposals/ENTITY-RELATIONS.md` 2.6 — zero is absence, not a low score, so the\nlower bound is exclusive, unlike [`Confidence`]'s inclusive `[0, 1]`).","format":"double","type":"number"},"sources":{"items":{"type":"string"},"type":"array"},"to":{"type":"string"}},"required":["id","relation","from","to","basis","score","sources","attributes"],"type":"object"},"type":"array"},"question":{"description":"The objective in the client's own words. Required before compiling.","type":["string","null"]},"scopes":{"items":{"additionalProperties":false,"description":"A territory/scope discovered during kickoff (WORKGRAPH D1's L0 drill-down root),\ne.g. \"Synthetic State A\". Deliberately NOT an entity: T1's `market` entity kind is\na priced tradable, not a sales territory (`docs/proposals/ENTITY-RELATIONS.md`\nsection 11, open question 2 — its recommended default, pending Sean's\nconfirmation, is that a territory stays a query scope this wave). No `entity_id`.","properties":{"basis":{"description":"`docs/kickoff/kickoff.schema.json` `#/$defs/basis`. Deliberately only the two\nbases a kickoff may propose (PLAN.md policy 3: a kickoff never writes\n`ACCUMULATED`, so that variant does not exist here — an incoming `\"ACCUMULATED\"`\nvalue fails to deserialize instead of needing a runtime check).","enum":["GROUNDED","GENERATED"],"type":"string"},"confidence":{"description":"A confidence value in `[0, 1]`, or absent. `#/$defs/confidence`.\n\nPlain `Option<f64>` cannot express the schema's `minimum`/`maximum`, so this\nwrapper validates on the way in: `serde_json::from_str` rejects an out-of-range\nvalue with a `serde::de::Error`, the same failure mode as every other schema\nviolation in this module, rather than needing a second validation pass.","format":"double","type":["number","null"]},"id":{"type":"string"},"name":{"type":"string"},"notes":{"type":["string","null"]},"sources":{"items":{"type":"string"},"type":"array"}},"required":["id","name","basis","sources","confidence"],"type":"object"},"type":"array"},"title":{"description":"Short human label. Non-empty; no client names.","example":"Q3 go-to-market, synthetic state A","type":"string"}},"required":["title"],"type":"object"},"x-timepoint-workspace":{"description":"Workspace uuid to act in (W2). Absent = the caller's personal space. A malformed id, an unknown workspace and one the caller is not a member of all answer 404 workspace_not_found.","format":"uuid","type":["string","null"]}},"required":["body"],"type":"object"},"method":"POST","name":"create_kickoff","path":"/v1/kickoffs","requires_auth":true},{"description":"One kickoff document (`kickoff.schema.json`) in the active space. Unknown, in another space and not yours are the same 404.","inputSchema":{"properties":{"id":{"description":"Kickoff id","format":"uuid","type":"string"},"x-timepoint-workspace":{"description":"Workspace uuid to act in (W2). Absent = the caller's personal space. A malformed id, an unknown workspace and one the caller is not a member of all answer 404 workspace_not_found.","format":"uuid","type":["string","null"]}},"required":["id"],"type":"object"},"method":"GET","name":"get_kickoff","path":"/v1/kickoffs/{id}","requires_auth":true},{"description":"Light alignment (K5, gate G12; D2b's fuzzy dedupe is a separate, later node): `extracted` -> `aligned`. Sets `entity_id` on every `extracted_entities` proposal that matches an existing entity in this kickoff's own space by EXACT kind + slug; no match leaves it null (a new-entity candidate, for K4's approval to mint). Provider or admin.","inputSchema":{"properties":{"id":{"description":"Kickoff id","format":"uuid","type":"string"},"x-timepoint-workspace":{"description":"Workspace uuid to act in (W2). Absent = the caller's personal space. A malformed id, an unknown workspace and one the caller is not a member of all answer 404 workspace_not_found.","format":"uuid","type":["string","null"]}},"required":["id"],"type":"object"},"method":"POST","name":"align_kickoff","path":"/v1/kickoffs/{id}/align","requires_auth":true},{"description":"Compile an `approved` kickoff into the Boss's deep_sim request and admit it through `POST /v1/runs`' own path, in the kickoff's space: cast = the approved, aligned entities (knowledge in this space) with up to 3 GROUNDED facts each, relationships = the current edges among the cast, query = the question plus approved scopes. `sim_params`, `models` and `cast_approval_id` follow `POST /v1/runs` exactly; a cast above the default cap asks for a cap equal to the cast (admin only; above 20 with an approval). Nothing is confirmed or reserved: the quote is indicative (a roster the size of the cast); the binding quote is the run's confirm_plan card. In the same transaction the kickoff moves `approved` → `compiled` → `planned` and records `compiled_run_id`; a refusal leaves it `approved`. Idempotent: a `planned` kickoff answers 200 with its existing run and `replayed: true`, and the replay ignores the request body (`parent_run_id` included). Send `{}` for no overrides. Lineage: `parent_run_id` names the run this one follows from; it must be a run that is not withdrawn, in the same space as the kickoff (a workspace kickoff: a run of that workspace; a personal kickoff: your own personal run), else `422 invalid_parent_run` (unknown, another space's and withdrawn ids are one answer). It is frozen as the run's `parent_run_id` (`run.parent_run_id` in the answer) and recorded on its `run.accepted` event as `lineage: {parent_run_id, relation: \"child\"}`. A kickoff freezes its workspace at creation: with no `x-timepoint-workspace` header this compiles in the kickoff's OWN workspace (you must still be a member); a header naming a DIFFERENT workspace than the kickoff's own is refused, `409 workspace_mismatch`, never silently `not_found`. A personal kickoff is unaffected — send no header.","inputSchema":{"properties":{"Idempotency-Key":{"description":"M4: a caller-chosen key (≤200 chars), on top of this operation's own `replayed: true` idempotency. The same caller + key + method/path/body within 24h replays the exact first response (header idempotent-replay: true) instead of re-running the compile; the same key with a different request answers 422 idempotency_key_reused; a concurrent duplicate answers 409 idempotency_in_progress (retryable). Absent = ordinary behavior.","type":["string","null"]},"body":{"description":"The Boss knobs a compile may set, with `POST /v1/runs`' own semantics.\nConstructible in the crate (`CompileReq { parent_run_id: Some(p),\n..Default::default() }`) so other code compiles a kickoff through\n[`compile`], the one entry point.","properties":{"cast_approval_id":{"description":"A usable C1 cast approval of the caller's account; needed when the\napproved cast is above the admin tier.","format":"uuid","type":["string","null"]},"dry_run":{"description":"Track E: `true` runs every compile and admission check and answers 200\nwith the run it would admit (`POST /v1/runs {dry_run}`'s preview plus\n`kickoff_id`); the kickoff stays as it is, no run is minted and nothing\nis reserved. A refusal is listed in `refusals`. Compile only; a\nrecompile refuses it.","type":["boolean","null"]},"models":{"description":"Model picks per role, exactly as `POST /v1/runs {models}` (admin only)."},"parent_run_id":{"description":"Lineage: the run this compile follows from. It must be a run that is\nnot withdrawn, in the SAME space this compile acts in (a workspace\nkickoff: a run of that workspace; a personal kickoff: your own personal\nrun). Unknown, another space's and withdrawn ids are one typed 422\n`invalid_parent_run`. Frozen as the run's `parent_run_id` and recorded on\nits `run.accepted` event as `lineage: {parent_run_id, relation: \"child\"}`.","format":"uuid","type":["string","null"]},"sim_params":{"additionalProperties":{"format":"double","type":"number"},"description":"Per-run simulation parameters, exactly as `POST /v1/runs {sim_params}`\n(bounded; `admin_only` keys need the `admin` capability).","propertyNames":{"type":"string"},"type":["object","null"]}},"type":"object"},"id":{"description":"Kickoff id","format":"uuid","type":"string"},"x-timepoint-workspace":{"description":"Workspace uuid to act in (W2). Absent = the kickoff's own workspace (personal, if it has none) — you must still be a member of it. Naming a workspace other than the kickoff's own is refused (409 workspace_mismatch). A malformed id, an unknown workspace and one you are not a member of all answer 404 workspace_not_found.","format":"uuid","type":["string","null"]}},"required":["id","body"],"type":"object"},"method":"POST","name":"compile_kickoff","path":"/v1/kickoffs/{id}/compile","requires_auth":true},{"description":"Extract candidate entities/edges/scopes from a kickoff's own notes (K5, gate G12): `draft`/`extracted` -> `extracted`. Renders a prompt over the kickoff's question and notes, calls the shallow research model with a bounded token limit and a pre-call cost ceiling against a kickoff-scoped research budget (migration 0072; hard stop, same ledger as drill-down passes), maps the result into `extracted_entities`/`proposed_edges`/ `scopes` proposals. Nothing is written to entity.*. Refuses a contact detail in any field (PLAN.md policy 1) and an entity-language `Reject` (K2) as a whole-batch typed error; a `Flag` is recorded on the kickoff's `decisions`. Off by default (`TP_KICKOFF_EXTRACT_ENABLED=1` required) and administrators only.","inputSchema":{"properties":{"id":{"description":"Kickoff id","format":"uuid","type":"string"},"x-timepoint-workspace":{"description":"Workspace uuid to act in (W2). Absent = the caller's personal space. A malformed id, an unknown workspace and one the caller is not a member of all answer 404 workspace_not_found.","format":"uuid","type":["string","null"]}},"required":["id"],"type":"object"},"method":"POST","name":"extract_kickoff","path":"/v1/kickoffs/{id}/extract","requires_auth":true},{"description":"A kickoff's independent reviews, oldest first, with a summary: `{subject: {kind, id}, workspace_id, summary: {count, stages: {pre_spend}}, items}`; the stage carries `{count, approve, reject, latest: {id, verdict, created_at} | null}`. The kickoff must be visible in the active space. Provider and admin members read; a client member is refused.","inputSchema":{"properties":{"id":{"description":"Kickoff id","format":"uuid","type":"string"},"x-timepoint-workspace":{"description":"Workspace uuid to act in (W2). Absent = the caller's personal space. A malformed id, an unknown workspace and one the caller is not a member of all answer 404 workspace_not_found.","format":"uuid","type":["string","null"]}},"required":["id"],"type":"object"},"method":"GET","name":"list_kickoff_independent_reviews","path":"/v1/kickoffs/{id}/independent-reviews","requires_auth":true},{"description":"Record an independent pre-spend review of a kickoff (stage `pre_spend`), by the caller's own login (never named in the body) and its sign-in method (`token`, `magic_link` or `google`; an MCP call is `token`). The reviewer must not be the kickoff's proposer (its `created_by`). A cancelled kickoff takes no review. A personal kickoff is visible to its owner only, so every review of one is refused as `own_proposal`: independent review is a workspace feature. Name the kickoff's workspace in `x-timepoint-workspace`; a provider or admin member writes, a client member is refused. Reviews are append-only.","inputSchema":{"properties":{"body":{"additionalProperties":false,"description":"One independent review. The reviewer is never named here: it is the\ncaller's own login.","properties":{"reasons":{"description":"1 to 20 non-empty reasons, each at most 2000 characters (trimmed).","items":{"type":"string"},"type":"array"},"stage":{"description":"`pre_spend` for a kickoff; `plan` or `terminal` for a run.","type":"string"},"subject_sha256":{"description":"Optional: `sha256:<64 hex>`, the hash of what the reviewer read (a plan\nor report hash).","type":["string","null"]},"verdict":{"description":"`approve` or `reject`.","type":"string"}},"required":["stage","verdict","reasons"],"type":"object"},"id":{"description":"Kickoff id","format":"uuid","type":"string"},"x-timepoint-workspace":{"description":"Workspace uuid to act in (W2). Absent = the caller's personal space. A malformed id, an unknown workspace and one the caller is not a member of all answer 404 workspace_not_found.","format":"uuid","type":["string","null"]}},"required":["id","body"],"type":"object"},"method":"POST","name":"record_kickoff_independent_review","path":"/v1/kickoffs/{id}/independent-reviews","requires_auth":true},{"description":"A kickoff's research proposals in the active space, for approval (K4, PLAN policy 5): its extracted entities, proposed edges and scopes, each with `status` (pending | approved | rejected), a one-line `summary`, basis, sources, confidence, the `statement` itself and K2 lint `flags` (always `[]` for kickoff proposals), plus `counts` and the drill-down jobs serving it with their pending counts. Pending only unless `status=all`. Administrators only.","inputSchema":{"properties":{"id":{"description":"Kickoff id","format":"uuid","type":"string"},"status":{"description":"`pending` (default) or `all`.","type":"string"},"x-timepoint-workspace":{"description":"Workspace uuid to act in (W2). Absent = the caller's personal space. A malformed id, an unknown workspace and one the caller is not a member of all answer 404 workspace_not_found.","format":"uuid","type":["string","null"]}},"required":["id"],"type":"object"},"method":"GET","name":"list_kickoff_proposals","path":"/v1/kickoffs/{id}/proposals","requires_auth":true},{"description":"Hand-author kickoff proposals (K5 follow-up for X1): append `extracted_entities` / `proposed_edges` / `scopes` in K1's item types, marked admin-authored on `decisions`. Administrators only (`approvals::can_approve`). Allowed while the kickoff is `draft`, `extracted`, `aligned` or `researching`; a `draft` becomes `extracted`. 1 to 200 items; ids new to the kickoff; entity kind person/org/role only; GROUNDED needs an https source; edges use the E1 vocabulary between entity ids in this request or the kickoff. A contact detail or an entity-language `Reject` refuses the whole batch; a `Flag` is recorded. No spend, no model call, nothing written to entity.* (approval, K4, is the only write).","inputSchema":{"properties":{"body":{"additionalProperties":false,"description":"Hand-authored proposals in K1's own item types.","properties":{"extracted_entities":{"description":"Entity proposals (kind `person`, `org` or `role`; `entity_id` null —\n`/align` sets it). `id` must be new to the kickoff.","items":{"additionalProperties":false,"properties":{"basis":{"description":"`docs/kickoff/kickoff.schema.json` `#/$defs/basis`. Deliberately only the two\nbases a kickoff may propose (PLAN.md policy 3: a kickoff never writes\n`ACCUMULATED`, so that variant does not exist here — an incoming `\"ACCUMULATED\"`\nvalue fails to deserialize instead of needing a runtime check).","enum":["GROUNDED","GENERATED"],"type":"string"},"confidence":{"description":"A confidence value in `[0, 1]`, or absent. `#/$defs/confidence`.\n\nPlain `Option<f64>` cannot express the schema's `minimum`/`maximum`, so this\nwrapper validates on the way in: `serde_json::from_str` rejects an out-of-range\nvalue with a `serde::de::Error`, the same failure mode as every other schema\nviolation in this module, rather than needing a second validation pass.","format":"double","type":["number","null"]},"entity_id":{"format":"uuid","type":["string","null"]},"id":{"type":"string"},"kind":{"type":"string"},"name":{"type":"string"},"notes":{"type":["string","null"]},"sources":{"items":{"type":"string"},"type":"array"}},"required":["id","kind","name","basis","sources","confidence"],"type":"object"},"type":"array"},"proposed_edges":{"description":"Edges between entity proposals: `from`/`to` name entity ids in this\nrequest or already in the kickoff; `relation` from the E1 vocabulary.","items":{"additionalProperties":false,"description":"A proposed relation between two `ExtractedEntity` proposals, or an\nalready-aligned `entity.entities` id. T1's kebab-case vocabulary\n(`docs/proposals/ENTITY-RELATIONS.md`); `basis`/`score`/`sources` are beta-side\ncolumns T1 deliberately leaves to the consumer. `attributes` carries relation\nattributes such as `holds-office`'s `valid_from`/`valid_to` tenure, as plain\nstrings (T1's closed attribute-type set is `date` and `text`, both strings).","properties":{"attributes":{"additionalProperties":{"type":"string"},"propertyNames":{"type":"string"},"type":"object"},"basis":{"description":"`docs/kickoff/kickoff.schema.json` `#/$defs/basis`. Deliberately only the two\nbases a kickoff may propose (PLAN.md policy 3: a kickoff never writes\n`ACCUMULATED`, so that variant does not exist here — an incoming `\"ACCUMULATED\"`\nvalue fails to deserialize instead of needing a runtime check).","enum":["GROUNDED","GENERATED"],"type":"string"},"from":{"type":"string"},"id":{"type":"string"},"relation":{"description":"A relation id in T1's kebab-case vocabulary (`docs/proposals/ENTITY-RELATIONS.md`\n2.3): lowercase ascii words joined by single hyphens. Kept as a validated string,\nnot a closed enum, because the vocabulary is not yet pinned into beta (T2/T3);\nD2/K3 check the value against the pinned vocabulary at write/compile time.","type":"string"},"score":{"description":"A proposed-edge score in `(0, 1]`: belief the edge holds as stated (T1 draft,\n`docs/proposals/ENTITY-RELATIONS.md` 2.6 — zero is absence, not a low score, so the\nlower bound is exclusive, unlike [`Confidence`]'s inclusive `[0, 1]`).","format":"double","type":"number"},"sources":{"items":{"type":"string"},"type":"array"},"to":{"type":"string"}},"required":["id","relation","from","to","basis","score","sources","attributes"],"type":"object"},"type":"array"},"scopes":{"description":"Territories (query scopes, never entities).","items":{"additionalProperties":false,"description":"A territory/scope discovered during kickoff (WORKGRAPH D1's L0 drill-down root),\ne.g. \"Synthetic State A\". Deliberately NOT an entity: T1's `market` entity kind is\na priced tradable, not a sales territory (`docs/proposals/ENTITY-RELATIONS.md`\nsection 11, open question 2 — its recommended default, pending Sean's\nconfirmation, is that a territory stays a query scope this wave). No `entity_id`.","properties":{"basis":{"description":"`docs/kickoff/kickoff.schema.json` `#/$defs/basis`. Deliberately only the two\nbases a kickoff may propose (PLAN.md policy 3: a kickoff never writes\n`ACCUMULATED`, so that variant does not exist here — an incoming `\"ACCUMULATED\"`\nvalue fails to deserialize instead of needing a runtime check).","enum":["GROUNDED","GENERATED"],"type":"string"},"confidence":{"description":"A confidence value in `[0, 1]`, or absent. `#/$defs/confidence`.\n\nPlain `Option<f64>` cannot express the schema's `minimum`/`maximum`, so this\nwrapper validates on the way in: `serde_json::from_str` rejects an out-of-range\nvalue with a `serde::de::Error`, the same failure mode as every other schema\nviolation in this module, rather than needing a second validation pass.","format":"double","type":["number","null"]},"id":{"type":"string"},"name":{"type":"string"},"notes":{"type":["string","null"]},"sources":{"items":{"type":"string"},"type":"array"}},"required":["id","name","basis","sources","confidence"],"type":"object"},"type":"array"}},"type":"object"},"id":{"description":"Kickoff id","format":"uuid","type":"string"},"x-timepoint-workspace":{"description":"Workspace uuid to act in (W2). Absent = the caller's personal space. A malformed id, an unknown workspace and one the caller is not a member of all answer 404 workspace_not_found.","format":"uuid","type":["string","null"]}},"required":["id","body"],"type":"object"},"method":"POST","name":"author_kickoff_proposals","path":"/v1/kickoffs/{id}/proposals","requires_auth":true},{"description":"Approve or reject a batch of a kickoff's proposals, all or nothing, in one transaction (K4, PLAN policy 5). Approve writes into the kickoff's space: an entity is resolved or minted and its id written back to `extracted_entities[].entity_id` (so K3 can compile); an edge is stated through E2 with its proposed basis (GROUNDED needs an https source); a scope is recorded only. Reject writes nothing to knowledge. The decision is recorded in the kickoff's `approvals` / `decisions`; when nothing is pending and something is approved the kickoff moves to `approved`. Decisions are final: the same decision again is `unchanged`, the opposite one 409. The kickoff must be `awaiting_approval`. Administrators only.","inputSchema":{"properties":{"body":{"additionalProperties":false,"description":"One decision over a batch of proposals.","properties":{"decision":{"enum":["approve","reject"],"type":"string"},"note":{"description":"Optional note recorded with the decision (at most 2000 characters).","type":["string","null"]},"proposal_ids":{"description":"The proposals to decide: kickoff proposal ids, or drill-down proposal\nuuids. 1 to 200, duplicates ignored.","items":{"type":"string"},"type":"array"}},"required":["decision","proposal_ids"],"type":"object"},"id":{"description":"Kickoff id","format":"uuid","type":"string"},"x-timepoint-workspace":{"description":"Workspace uuid to act in (W2). Absent = the caller's personal space. A malformed id, an unknown workspace and one the caller is not a member of all answer 404 workspace_not_found.","format":"uuid","type":["string","null"]}},"required":["id","body"],"type":"object"},"method":"POST","name":"decide_kickoff_proposals","path":"/v1/kickoffs/{id}/proposals/decide","requires_auth":true},{"description":"RX5: compile a `planned` kickoff again after its run `failed`. `compile` replays a planned kickoff's run whatever its state; this returns the kickoff to `approved` (its `decisions` record the failed run's id) and admits a fresh run exactly as `compile` does. Only while the recorded run is `failed`: a `created` or `running` run is refused, and a `complete` one keeps replaying through `compile`. The research approver only (`approvals::can_approve`). Body as `compile`; send `{}` for no overrides.","inputSchema":{"properties":{"body":{"description":"The Boss knobs a compile may set, with `POST /v1/runs`' own semantics.\nConstructible in the crate (`CompileReq { parent_run_id: Some(p),\n..Default::default() }`) so other code compiles a kickoff through\n[`compile`], the one entry point.","properties":{"cast_approval_id":{"description":"A usable C1 cast approval of the caller's account; needed when the\napproved cast is above the admin tier.","format":"uuid","type":["string","null"]},"dry_run":{"description":"Track E: `true` runs every compile and admission check and answers 200\nwith the run it would admit (`POST /v1/runs {dry_run}`'s preview plus\n`kickoff_id`); the kickoff stays as it is, no run is minted and nothing\nis reserved. A refusal is listed in `refusals`. Compile only; a\nrecompile refuses it.","type":["boolean","null"]},"models":{"description":"Model picks per role, exactly as `POST /v1/runs {models}` (admin only)."},"parent_run_id":{"description":"Lineage: the run this compile follows from. It must be a run that is\nnot withdrawn, in the SAME space this compile acts in (a workspace\nkickoff: a run of that workspace; a personal kickoff: your own personal\nrun). Unknown, another space's and withdrawn ids are one typed 422\n`invalid_parent_run`. Frozen as the run's `parent_run_id` and recorded on\nits `run.accepted` event as `lineage: {parent_run_id, relation: \"child\"}`.","format":"uuid","type":["string","null"]},"sim_params":{"additionalProperties":{"format":"double","type":"number"},"description":"Per-run simulation parameters, exactly as `POST /v1/runs {sim_params}`\n(bounded; `admin_only` keys need the `admin` capability).","propertyNames":{"type":"string"},"type":["object","null"]}},"type":"object"},"id":{"description":"Kickoff id","format":"uuid","type":"string"},"x-timepoint-workspace":{"description":"Workspace uuid to act in (W2). Absent = the caller's personal space. A malformed id, an unknown workspace and one the caller is not a member of all answer 404 workspace_not_found.","format":"uuid","type":["string","null"]}},"required":["id","body"],"type":"object"},"method":"POST","name":"recompile_kickoff","path":"/v1/kickoffs/{id}/recompile","requires_auth":true},{"description":"Research spend of a kickoff's extraction pass (K5, gate G12; migration 0072, the same `run.research_spend` ledger D4 built for drill-down passes): cap, committed, remaining, OpenRouter only. `null` `pass` when extraction was never called. Any member of the space reads (D5's own rule for a job's spend view).","inputSchema":{"properties":{"id":{"description":"Kickoff id","format":"uuid","type":"string"},"x-timepoint-workspace":{"description":"Workspace uuid to act in (W2). Absent = the caller's personal space. A malformed id, an unknown workspace and one the caller is not a member of all answer 404 workspace_not_found.","format":"uuid","type":["string","null"]}},"required":["id"],"type":"object"},"method":"GET","name":"get_kickoff_spend","path":"/v1/kickoffs/{id}/spend","requires_auth":true},{"description":"Freeze the proposal set for review (K5, gate G12; K4's gap): `extracted`/`aligned`/`researching` -> `awaiting_approval`. No more automatic research writes to this kickoff after this call. Provider or admin.","inputSchema":{"properties":{"id":{"description":"Kickoff id","format":"uuid","type":"string"},"x-timepoint-workspace":{"description":"Workspace uuid to act in (W2). Absent = the caller's personal space. A malformed id, an unknown workspace and one the caller is not a member of all answer 404 workspace_not_found.","format":"uuid","type":["string","null"]}},"required":["id"],"type":"object"},"method":"POST","name":"submit_kickoff","path":"/v1/kickoffs/{id}/submit","requires_auth":true},{"description":"POST /v1/ladders","inputSchema":{"properties":{"Idempotency-Key":{"type":["string","null"]},"body":{"description":"The JSON request body."},"x-timepoint-workspace":{"format":"uuid","type":["string","null"]}},"required":["body"],"type":"object"},"method":"POST","name":"launch_ladder","path":"/v1/ladders","requires_auth":true},{"description":"GET /v1/ladders/{id}","inputSchema":{"properties":{"id":{"format":"uuid","type":"string"}},"required":["id"],"type":"object"},"method":"GET","name":"get_ladder","path":"/v1/ladders/{id}","requires_auth":true},{"description":"POST /v1/ladders/{id}/spend/{decision_id}","inputSchema":{"properties":{"body":{"description":"The JSON request body."},"decision_id":{"format":"uuid","type":"string"},"id":{"format":"uuid","type":"string"}},"required":["id","decision_id","body"],"type":"object"},"method":"POST","name":"decide_ladder_spend","path":"/v1/ladders/{id}/spend/{decision_id}","requires_auth":true},{"description":"POST /v1/ladders/{id}/stop","inputSchema":{"properties":{"id":{"format":"uuid","type":"string"}},"required":["id"],"type":"object"},"method":"POST","name":"stop_ladder","path":"/v1/ladders/{id}/stop","requires_auth":true},{"description":"`GET /v1/me/assets` — everything delivered to the caller. — Everything delivered to the caller, in one document: delivered simulation results (with a reader's title and the documents each has: `cockpit`, `report`, `portal`, `plan`), delivered engagement wrap-up reports, delivered documents (`list_my_files`/`get_file`), the engagements whose record the caller may read (`engagements`, each opened with `get_engagement_record`), the workspaces the caller belongs to with the number of shared research records in each, and how to connect an AI assistant (the MCP URL and the caller's own access keys, never their secrets). Each part follows the same read rule as its own tool: open a result with `get_run_report`, a wrap-up with `get_wrapup_report`, research with `search_entities`.","inputSchema":{"properties":{},"required":[],"type":"object"},"method":"GET","name":"list_my_assets","path":"/v1/me/assets","requires_auth":true},{"description":"`GET /v1/me/engagements/{id}/grant-book` — the client's grant book. — The grant book of one engagement the caller is the client of (or a member of the workspace it is linked to; the record's rule): one row per grant its searches found, with its decision, how far it went and the next step (`status_word`: found, not eligible, set aside, read: likely win, read: likely loss, simulating, simulated: won, simulated: lost). No internal id but the `run_id` of a delivered run: `grant_id` is a key made from the funder and program, `also_found_in` and `searches` are empty. A quick read, and a simulation's result, are shown once its run was delivered, or while the engagement's switch shows unfinished work (`unfinished_shown` on the record, on by default); with the switch off a simulation not delivered is left out and only grants with a delivered read or simulation are listed. As CSV: `/v1/me/engagements/{id}/crm-export?part=grants`. Unknown and not-yours are the same 404.","inputSchema":{"properties":{"id":{"description":"Engagement id (from `list_my_assets`, `engagements`)","format":"uuid","type":"string"}},"required":["id"],"type":"object"},"method":"GET","name":"get_my_engagement_grant_book","path":"/v1/me/engagements/{id}/grant-book","requires_auth":true},{"description":"`GET /v1/me/engagements/{id}/record` — everything from one engagement. — Everything from one engagement the caller is the client of (or a member of the workspace it is linked to), in one document: every run attached to it or created in its workspace for it, newest first (a delivered one with its `run_id` and the readings it has, `docs`; one that was not delivered with no id, only its title, dates, `status` (did_not_finish, in_progress or not_delivered), a coarse `reason` category and a plain `note`; never the recorded error text); a grant search's counts (`grants: {found, eligible, approved, greyed}`); the delivered wrap-up; the delivered files, films and report decks (as `list_my_files` lists them); and, when the engagement is linked to a workspace the caller belongs to, the CRM export's parts in the client view with their row counts (`crm_export`, made when downloaded). Runs not delivered are listed only while the engagement's switch says so (`unfinished_shown`, on by default). Unknown and not-yours are the same 404.","inputSchema":{"properties":{"id":{"description":"Engagement id (from `list_my_assets`, `engagements`)","format":"uuid","type":"string"}},"required":["id"],"type":"object"},"method":"GET","name":"get_engagement_record","path":"/v1/me/engagements/{id}/record","requires_auth":true},{"description":"GET /v1/me/workspaces — the caller's own memberships, no `admin` capability\nrequired: this is how the UI and P2 learn which `x-timepoint-workspace`\nheader to send, so any authenticated caller must be able to read their own\nrow of it (not another identity's — `name = $1` binds the caller alone). — Any authenticated caller. The caller's OWN workspace memberships (never another identity's) — how the UI and a provider's agent learn which `x-timepoint-workspace` header to send. No admin capability required.","inputSchema":{"properties":{},"required":[],"type":"object"},"method":"GET","name":"list_my_workspaces","path":"/v1/me/workspaces","requires_auth":true},{"description":"MP1: preview a meeting. Starts the simulation and the walk-back and returns\nthe preview, `pending` until both runs end (read it at `GET /v1/meetings/{id}`). — Preview one meeting, privately: the odds of what you want out of it, each other attendee's stance and objections, the turning point, and a walk-back from what you want to three moves (or a no-route with the nearest reachable outcome), plus a rehearse link to Play on the same cast. Give the meeting, pasted invite text, or a `calendar_event_id` from your own connected calendar (an operator token lists them with `calendar_events`; a connector cannot, so give the meeting or paste the invite); another account's event, or an unknown one, is a 404. Starts two runs whose credits the ledger holds; the answer is `pending` until both runs end, then read it at `get_meeting_preview`: `complete` when both delivered, `partial` when one delivered and the other ended without a reading (only the delivered half is charged; `unavailable` says which part is missing and why), `failed` when neither delivered (nothing charged). Private to you: attendee names are in this view only. A new account's first quick previews are free (the house pays, under a daily cap; `funding: free`): the odds and stances run free, and the walk-back runs on credits when the account has them, else it is left out (`walk_back.verdict: not_included`). Once today's free previews are used up, an account without credits gets 402 `free_previews_closed_today`.","inputSchema":{"properties":{"body":{"description":"The meeting to preview and what you want out of it.","properties":{"calendar_event_id":{"type":["string","null"]},"invite_text":{"type":["string","null"]},"meeting":{"oneOf":[{"type":"null"},{"description":"The meeting, spelled out. Exactly one of `meeting`, `calendar_event_id`\nand `invite_text`.","properties":{"agenda":{"type":["string","null"]},"attendees":{"items":{"properties":{"name":{"description":"Shown only to the account that made this preview. Timepoint's own models\nnever receive it, and no shareable form includes it.","type":["string","null"]},"notes":{"type":["string","null"]},"role":{"type":"string"}},"required":["role"],"type":"object"},"type":"array"},"title":{"type":"string"},"when":{"description":"When it happens (an ISO date-time or a plain date).","type":"string"}},"required":["title","when"],"type":"object"}]},"want":{"description":"What you want out of it (at most 500 characters).","type":"string"}},"required":["want"],"type":"object"}},"required":["body"],"type":"object"},"method":"POST","name":"preview_meeting","path":"/v1/meetings/preview","requires_auth":true},{"description":"Read a meeting preview again (the poll). Owner only; anyone else is a 404. — Read one of your meeting previews (`preview_meeting`) again; it fills in as its runs deliver. `status` is `pending` until both runs end, then `complete`, `partial` (one half delivered; only it is charged; see `unavailable`) or `failed` (nothing delivered, nothing charged). `walk_back.verdict` is `route` only when at least one move held on its blind replay, `unjudged` when a route was drawn but none held and at least one could not be judged, `not_held` when every move was judged and none held, else `no_route` or `failed`. A drawn route carries `walk_back.checks: {held, unjudged, missed}` (null otherwise); an unjudged move is never counted as missed. Private: another account's id is a 404.","inputSchema":{"properties":{"id":{"description":"Meeting preview id (mp_…)","type":"string"}},"required":["id"],"type":"object"},"method":"GET","name":"get_meeting_preview","path":"/v1/meetings/{id}","requires_auth":true},{"description":"GET /v1/models","inputSchema":{"properties":{},"required":[],"type":"object"},"method":"GET","name":"list_models","path":"/v1/models","requires_auth":true},{"description":"MP3: record what happened. — Record what happened for one of your forecasts (a meeting preview `mp_…` or a pin `pin_…`). Settles it once: computes the Brier score, seals a sha256 hash (only the hash is sealed; visibility private_hash_only; the content stays private) and returns your private record: settled count, mean Brier and calibration bins. `/v1/meetings/{id}/outcome` and `/v1/pins/{id}/outcome` are the same call by route. Owner only: another account's id is a 404.","inputSchema":{"properties":{"body":{"description":"The JSON request body.","properties":{"happened":{"description":"What happened: free text, a short code, or `true`/`false`. Text that\nsays it did not happen (\"no\", \"did not happen\", \"no_term_sheet\", …)\nsettles the forecast as not having occurred.","type":"object"},"id":{"description":"The meeting preview (`mp_…`) or pin (`pin_…`) to settle.","type":"string"},"occurred_at":{"description":"When it happened (RFC 3339), if known.","type":["string","null"]}},"required":["id","happened"],"type":"object"}},"required":["body"],"type":"object"},"method":"POST","name":"record_outcome","path":"/v1/outcomes","requires_auth":true},{"description":"MP2: pin an outcome by a date. — Pin an outcome by a date, privately. Walks backward from it: a route of dated milestones, each checked blind, or a no-route with the nearest reachable outcome and the candidates tried. Creates a watch (the monitors mechanism) that alerts when the route changes; the watch spends nothing until you choose a research source. Starts a run whose credits the ledger holds; `pending` until it delivers, then read it at `get_pin`. A walk that cannot try a route ends `failed` with a fixed `reason` and releases its credits.","inputSchema":{"properties":{"body":{"description":"The JSON request body.","properties":{"by":{"description":"The date to reach it by, `YYYY-MM-DD`, after today.","type":"string"},"outcome":{"description":"The outcome to reach (at most 500 characters).","type":"string"},"start":{"description":"Where you are starting from (optional, at most 500 characters).","type":["string","null"]}},"required":["outcome","by"],"type":"object"}},"required":["body"],"type":"object"},"method":"POST","name":"pin_outcome","path":"/v1/pins","requires_auth":true},{"description":"Read a pin again (the poll). Owner only; anyone else is a 404. — Read one of your pins (`pin_outcome`) again; it fills in when its walk delivers. `verdict` is `pending`, `route` (at least one milestone held on its blind replay), `unjudged` (a route was drawn but none held and at least one could not be judged), `not_held` (a route was drawn, every milestone was judged and none held), `no_route` or `failed`. A drawn route (`route`, `unjudged`, `not_held`) carries `checks: {held, unjudged, missed}`, null otherwise; an unjudged milestone is never counted as missed. Each milestone is a dated event on the route; the walk's suggested test for it is `next_action`. Each milestone's `check.judged: false` means its replay could not be judged: unscored, never a miss. A `failed` pin carries `reason`, a fixed sentence (the goal could not be read as a dated event, a spending ceiling, or the walk stopped before proposing a route) and was not charged. Private: another account's id is a 404.","inputSchema":{"properties":{"id":{"description":"Pin id (pin_…)","type":"string"}},"required":["id"],"type":"object"},"method":"GET","name":"get_pin","path":"/v1/pins/{id}","requires_auth":true},{"description":"Your free Play turns and the turn price — What Play costs you right now: free turns left on this account, whether the house still pays for free turns today, the price of one turn once the free turns are used, and your available credits.","inputSchema":{"properties":{},"required":[],"type":"object"},"method":"GET","name":"get_play_allowance","path":"/v1/play/allowance","requires_auth":true},{"description":"List your Play attempts — Your Play attempts, newest first (at most 100).","inputSchema":{"properties":{},"required":[],"type":"object"},"method":"GET","name":"list_play_attempts","path":"/v1/play/attempts","requires_auth":true},{"description":"Start a Play attempt on a challenge — Take the seat in a Play challenge and start an attempt. Give exactly one of `challenge_id`, `run_id` (a run with a playable challenge) or `preview_id` (one of your meeting previews with a challenge). A live public or unlisted challenge, or one of your own, can be played. Starting is free; each turn uses a free turn first, then credits. Private to you.","inputSchema":{"properties":{"body":{"additionalProperties":false,"description":"`POST /v1/play/attempts`: exactly one of `challenge_id`, `run_id`,\n`preview_id`.","properties":{"challenge_id":{"description":"A challenge id (`ch_…`).","type":["string","null"]},"preview_id":{"description":"One of your meeting previews (`mp_…`) with a challenge compiled from it.","type":["string","null"]},"run_id":{"description":"A run with a playable challenge compiled from it.","format":"uuid","type":["string","null"]},"seat":{"description":"The seat to take. A v1 challenge has one seat; when given it must match.","type":["string","null"]}},"type":"object"}},"required":["body"],"type":"object"},"method":"POST","name":"create_play_attempt","path":"/v1/play/attempts","requires_auth":true},{"description":"Read one of your Play attempts — One of your Play attempts: the challenge, the table's support now, every turn with the replies, and the score. Another account's attempt is a 404.","inputSchema":{"properties":{"id":{"description":"Attempt id (at_…)","type":"string"}},"required":["id"],"type":"object"},"method":"GET","name":"get_play_attempt","path":"/v1/play/attempts/{id}","requires_auth":true},{"description":"Abandon a Play attempt — Give up one of your active Play attempts. A finished attempt is a 409.","inputSchema":{"properties":{"id":{"description":"Attempt id (at_…)","type":"string"}},"required":["id"],"type":"object"},"method":"POST","name":"abandon_play_attempt","path":"/v1/play/attempts/{id}/abandon","requires_auth":true},{"description":"Put a Play attempt on the leaderboard under a handle — Put one of your finished Play attempts on its challenge's leaderboard under a handle: 3 to 24 letters, digits, `_` or `-`, unique on that board, never your account name. Optional; without a handle an attempt stays off the board.","inputSchema":{"properties":{"body":{"additionalProperties":false,"description":"`POST /v1/play/attempts/{id}/handle`.","properties":{"handle":{"description":"3 to 24 characters of letters, digits, `_` and `-`; unique on the board.","type":"string"}},"required":["handle"],"type":"object"},"id":{"description":"Attempt id (at_…)","type":"string"}},"required":["id","body"],"type":"object"},"method":"POST","name":"set_play_attempt_handle","path":"/v1/play/attempts/{id}/handle","requires_auth":true},{"description":"Take a turn in a Play attempt — Say something to the table. Each person replies in their own voice, and the judge credits only what your words actually address; the engine caps how much any one turn can move anyone. Uses a free turn while you have one and the house is open, otherwise holds one turn's price in credits and charges it only when the turn is stored (a failed turn charges nothing). A turn is never charged twice.","inputSchema":{"properties":{"body":{"additionalProperties":false,"description":"`POST /v1/play/attempts/{id}/turns`.","properties":{"say":{"description":"What the player says to the table (at most the challenge's `max_chars`).","type":"string"}},"required":["say"],"type":"object"},"id":{"description":"Attempt id (at_…)","type":"string"}},"required":["id","body"],"type":"object"},"method":"POST","name":"post_play_turn","path":"/v1/play/attempts/{id}/turns","requires_auth":true},{"description":"`{version, items: {<item>: credits…}, effective_from}` from the max-version\nprice_list rows — served from the database, never compiled in. — Current price list: `{version, items: {<item>: credits…}, effective_from}` from the max-version `ledger.price_list` rows — served from the database, never compiled in.","inputSchema":{"properties":{},"required":[],"type":"object"},"method":"GET","name":"get_prices","path":"/v1/prices","requires_auth":true},{"description":"GET /v1/publications","inputSchema":{"properties":{},"required":[],"type":"object"},"method":"GET","name":"list_publications","path":"/v1/publications","requires_auth":true},{"description":"POST /v1/publications","inputSchema":{"properties":{"body":{"description":"The JSON request body."}},"required":["body"],"type":"object"},"method":"POST","name":"create_publication","path":"/v1/publications","requires_auth":true},{"description":"GET /v1/publications/{id}","inputSchema":{"properties":{"id":{"format":"uuid","type":"string"}},"required":["id"],"type":"object"},"method":"GET","name":"get_publication","path":"/v1/publications/{id}","requires_auth":true},{"description":"PUT /v1/publications/{id}","inputSchema":{"properties":{"body":{"description":"The JSON request body."},"id":{"format":"uuid","type":"string"}},"required":["id","body"],"type":"object"},"method":"PUT","name":"save_publication","path":"/v1/publications/{id}","requires_auth":true},{"description":"POST /v1/publications/{id}/actions","inputSchema":{"properties":{"body":{"description":"The JSON request body."},"id":{"format":"uuid","type":"string"}},"required":["id","body"],"type":"object"},"method":"POST","name":"publication_action","path":"/v1/publications/{id}/actions","requires_auth":true},{"description":"`POST /v1/responses` — a comment or a follow-up on a delivered item. — Post a comment or a follow-up request on an item delivered to the caller (`item_kind` `result` or `wrapup`, and its id). `kind` is `comment` (a note on the item) or `follow_up` (a question the caller wants answered next; it stays `open` until the provider answers or closes it). Plain text, 1 to 2000 characters. Allowed exactly where the item is readable; unknown, not delivered and not yours are the same 404. Nothing is sent anywhere: the provider sees it in the app.","inputSchema":{"properties":{"body":{"description":"The JSON request body.","properties":{"body":{"description":"Plain text, 1 to 2000 characters.","type":"string"},"item_id":{"description":"Run id (result) or wrap-up id (wrapup).","format":"uuid","type":"string"},"item_kind":{"description":"`result` or `wrapup`.","type":"string"},"kind":{"description":"`comment` (a note on the item) or `follow_up` (a question to be answered next).","type":"string"}},"required":["item_kind","item_id","kind","body"],"type":"object"}},"required":["body"],"type":"object"},"method":"POST","name":"post_item_response","path":"/v1/responses","requires_auth":true},{"description":"`GET /v1/responses/{item_kind}/{item_id}` — the thread on one delivered item. — The comments and follow-up requests on one item delivered to the caller, oldest first, each with its replies. `item_kind` is `result` (a delivered simulation, by run id from `list_delivered_results`) or `wrapup` (a delivered wrap-up, by wrap-up id from `list_delivered_wrapups`). A follow-up carries a `state`: `open`, `answered` or `closed`. Readable exactly where the item itself is; unknown, not delivered and not yours are the same 404. Post with `post_item_response`.","inputSchema":{"properties":{"item_id":{"description":"run id (result) or wrap-up id (wrapup)","format":"uuid","type":"string"},"item_kind":{"description":"`result` or `wrapup`","type":"string"}},"required":["item_kind","item_id"],"type":"object"},"method":"GET","name":"list_item_responses","path":"/v1/responses/{item_kind}/{item_id}","requires_auth":true},{"description":"Admission: auth → validate → balance gate → reserve → one tx {run + event + queue}. — Admit a run. Order is law: validate (`quick_sim` requires `query` ≤500 chars; `story_sim` requires `url` — http/https, ≤2000 chars; `news_scan` takes neither — CONTRACTS §11.9; `deep_sim` requires `query` ≤2000 chars — §11.11; `find_money` takes an optional `query` ≤500 chars, defaulting to its self-description; `find_grants` is admin-only (it spends research budget), requires a workspace, and takes as `query` (≤500 chars) the applicant's OWN project for this search — one of the client's projects; a slate never mixes them — while the funder programs are what its slate lists; it also takes `applicant_entity_id`, `applicant_paths` and `geography`) → LLM balance gate (a closed gate refuses with 402 `llm_no_headroom` and reserves NOTHING) → reserve credits at the kind's served price (1h hold TTL) — EXCEPT `deep_sim`, whose admission reserves NOTHING (the quote is composed at the PLAN phase and reserved only on the confirm_plan `proceed` decision; `reservation_id` is null), and `find_money` and `find_grants`, zero-hold parents whose spends are their children's (quick_sims hold at fan-out; each promoted deep_sim prices at its own confirm; find_grants' one sourcing pass spends against its provider caps, frozen at admission from the sim_params defaults and reported on `GET /v1/runs/{id}/spend`) → one tx {run row + `run.accepted` event + queue row}. Returns 202 with the run id and its SSE events URL; the run itself executes async on the worker. E3 (W2): named `x-timepoint-workspace`, the run is admitted in that workspace (provider/admin members only — a client member is refused); absent, the caller's personal space, exactly as before workspaces existed. `find_money` in a workspace sources that workspace's GROUNDED deal record (every member's, never the caller's personal one) and its children run there too; `find_grants` has no personal form and requires one. `news_scan` and a public research run (`publication_id`) have no workspace form and refuse one.","inputSchema":{"properties":{"Idempotency-Key":{"description":"M4: a caller-chosen key (≤200 chars). The same caller + key + method/path/body within 24h replays the first response (header idempotent-replay: true) instead of re-running admission; the same key with a different request answers 422 idempotency_key_reused; a concurrent duplicate answers 409 idempotency_in_progress (retryable). Absent = ordinary behavior.","type":["string","null"]},"body":{"description":"The JSON request body.","properties":{"applicant_entity_id":{"description":"find_grants only: the workspace entity that applies (the client whose\nproject the query describes). It must be an entity of the named\nworkspace (`422 applicant_not_in_workspace`).","format":"uuid","type":["string","null"]},"applicant_paths":{"description":"find_grants only: how the applicant may apply, `direct` (as itself)\nand/or `sponsored` (as a fiscally sponsored project). Default\n`[\"direct\"]`; anything else is `422 invalid_applicant_paths`.","example":["direct","sponsored"],"items":{"type":"string"},"type":["array","null"]},"cast":{"description":"Owner-scoped actors to include. Private Pro/Portal only; never with\n`publication_id`. At most `deep_sim_cast_entity_max` entities (default\n8; an admin may set up to 20; above 20 needs `cast_approval_id`) and\n`deep_sim_cast_fact_max` facts (3 per entity unless set).","items":{"properties":{"assertion_ids":{"items":{"format":"uuid","type":"string"},"type":"array"},"entity_id":{"format":"uuid","type":"string"}},"required":["entity_id"],"type":"object"},"type":["array","null"]},"cast_approval_id":{"description":"C1: an approved, unexpired, unrevoked cast approval of the caller's\naccount (`POST /v1/cast-approvals`). Raises this run's entity cap to its\nN (facts to 3 × N); required for a cast above 20. Frozen into the run as\n`cast_approval_id` + `cast_approval_max_cast`. deep_sim/portal_sim only.","format":"uuid","type":["string","null"]},"depth":{"description":"conference_sim only: `\"quick\"` (default), `\"standard\"`, or `\"deep\"` —\na named preset over deep_sim's existing `deep_sim_t_count`/\n`deep_sim_dialog_lines` sim_params, never a raw numeric dial. This\nkind is admin-only (an internal marketing-demo tool), so a non-`quick`\ndepth's admin-only sim_param overrides are never refused for it.","example":"standard","type":["string","null"]},"dry_run":{"description":"Track E: `true` runs every admission check (validation, cast and\nparams, the balance gate, the price and the credit balance) and answers\n200 with what the run would be, `{dry_run, would_admit, kind,\nresolved, caps, quote, balance, refusals, reserves: \"nothing\"}`. Nothing\nis reserved or written, and a refusal is listed in `refusals` with the\nstatus and body the real call would give.","type":["boolean","null"]},"geography":{"oneOf":[{"type":"null"},{"description":"find_grants only: where to look, `{city?, county?, region?, state?,\nfederal?}` inside ONE US state (a second state is a separate run). The\npass searches local first: city, county, region, state, multi_state,\nthen federal (default true). Absent: the federal level only. An unknown\nlevel, a non-state, or a place without its state is `422\ninvalid_geography`.","properties":{"city":{"description":"A city in `state`.","type":["string","null"]},"county":{"description":"A county in `state`.","type":["string","null"]},"federal":{"description":"Search the federal level too (default true).","type":["boolean","null"]},"region":{"description":"A named region of `state` (a metro area, a valley).","type":["string","null"]},"state":{"description":"One US state, by name or postal code (stored by name).","type":["string","null"]}},"type":"object"}]},"kind":{"description":"Run kind — `quick_sim`, `story_sim`, `news_scan`, `deep_sim`,\n`find_money`, `find_grants`, or `conference_sim`; anything else is a\ntyped 422.","example":"quick_sim","type":"string"},"models":{"description":"Private model picks (MR2): `{planning?, simulation?, dialog?, synthesis?,\nresearch_shallow?, research_deep?, interpretation?, synthesis_mode?:\nsingle|alternate|ab, synthesis_pair?: [a, b]}`; omitted roles take the\ndeployment defaults (`GET /v1/models`). `ab` and `synthesis_*` are deep_sim\nonly; `ab` is priced (see `model_roles`). Frozen into the run as\n`resolved_models` + `resolved_synthesis`. Never with `publication_id`. **Editor\ncapability** — an account without `admin` is refused 403 `forbidden`\n(T2.0): the credit price is flat per run, so an open model pick would let\nthe caller choose our cost. `cast` carries no such gate; it is priced per\nactor and owner-scoped."},"original_query":{"description":"Original user text before work context was appended. Pro only.","type":["string","null"]},"parent_run_id":{"description":"quick_sim: the what-if question — nonempty, at most 500 characters.\ndeep_sim: the deep question — nonempty, at most 2000 characters\n(CONTRACTS §11.11 B4.1). find_grants: the administrator's eligibility\nscope (applicant type, field, geography, award size) — nonempty, at\nmost 500 characters.\nOptional lineage: the run this one supersedes (re-run with today's world).","format":"uuid","type":["string","null"]},"publication_id":{"description":"Editor-owned public research project. Private account memory is excluded.","format":"uuid","type":["string","null"]},"query":{"example":"What if BeachSat launches a subscription tier next quarter?","type":["string","null"]},"scenario_requirements":{"description":"Explicit constraints retained independently of model extraction. Pro only.","items":{"additionalProperties":false,"description":"Uniform wire shape supported by the provider's strict object schema.\n`number` means count for Count and elapsed days for MinimumDuration.","properties":{"clause":{"type":"string"},"date":{"format":"date","type":["string","null"]},"from_t":{"format":"int64","type":["integer","null"]},"kind":{"enum":["end_date","count","milestone","minimum_duration","unresolved"],"type":"string"},"number":{"format":"int64","type":["integer","null"]},"to_t":{"format":"int64","type":["integer","null"]}},"required":["clause","kind"],"type":"object"},"type":["array","null"]},"sim_params":{"additionalProperties":{"format":"double","type":"number"},"description":"Per-run simulation parameters (G8): `{key: number}` over the\n`run.sim_params` keys. `deep_sim` takes the `deep_sim_*` keys;\n`portal_sim` takes the `portal_*` keys and the two cast caps. Any other\nkind, an unknown key, a non-number, an out-of-bounds value or a\nfractional int is a typed 422 `invalid_sim_params`; a key whose row is\n`admin_only` from an account without `admin` is 403 `forbidden`. Nothing\nis reserved on either refusal. The resolved set (defaults + overrides)\nis frozen into the run at admission; a larger shape prices higher\n(`deep_sim`: the plan-time quote; `portal_sim`: the admission hold).","example":{"deep_sim_t_count":8},"propertyNames":{"type":"string"},"type":["object","null"]},"url":{"description":"story_sim/conference_sim: the URL to fetch and simulate (CONTRACTS\n§11.8; conference_sim requires an Article-shaped page, never a tweet\nor a prediction market). Required for these kinds; http/https, at\nmost 2000 characters.","example":"https://en.wikipedia.org/wiki/OpenAI","type":["string","null"]}},"required":["kind"],"type":"object"},"x-timepoint-workspace":{"description":"Workspace uuid to act in (W2). Absent = the caller's personal space. A malformed id, an unknown workspace and one the caller is not a member of all answer 404 workspace_not_found.","format":"uuid","type":["string","null"]}},"required":["body"],"type":"object"},"method":"POST","name":"create_run","path":"/v1/runs","requires_auth":true},{"description":"Most recent run in ANY state (CONTRACTS §11.4). — The CALLER's most recent run in ANY state (CONTRACTS §11.4): `{id, state}`. Caller-scoped — this reads the latest run YOU created, never the deployment's global latest.","inputSchema":{"properties":{},"required":[],"type":"object"},"method":"GET","name":"get_latest_run","path":"/v1/runs/latest","requires_auth":true},{"description":"Cockpit default: the most recent COMPLETE run with a cockpit_doc. — The cockpit default (CONTRACTS §11.4): the `cockpit_doc` of the CALLER's most recent COMPLETE run that has one, served verbatim. Caller-scoped — never the deployment's global latest.","inputSchema":{"properties":{},"required":[],"type":"object"},"method":"GET","name":"get_latest_cockpit","path":"/v1/runs/latest/cockpit","requires_auth":true},{"description":"Run row + reservation state; absence is labeled with explicit nulls. — One run row plus its reservation: `{id, kind, state, phase, parent_run_id, price_list_version, created_by, query, created_at, terminal_at, reservation_state, reservation, models, funding}`. A run without a reservation serves explicit nulls — absence is labeled, never invented. `models` is the run's frozen admission record of the model slug per role (`planning`, `simulation`, `dialog`, `synthesis`, …), the disclosure of what ran it; null when the run froze none. `funding` is `free` when the freemium house budget paid for it (a new account's first quick previews; it then holds no reservation), else `credits`.","inputSchema":{"properties":{"id":{"description":"Run id","format":"uuid","type":"string"}},"required":["id"],"type":"object"},"method":"GET","name":"get_run","path":"/v1/runs/{id}","requires_auth":true},{"description":"Converse about a delivered run — record-grounded, credit-free, spend-tracked. — Converse ABOUT a delivered run (CONTRACTS §11.7). The server assembles the context fresh per call — the run row, the stored CockpitDoc (404 when absent), the per-moment dialog, the analysis contract, and the client-held history (≤10 turns, each ≤2k chars) — under the versioned `run_chat` honesty prompt: answers come ONLY from the simulation record, absences say so, speculation is labeled, staged-and-synthetic framing is maintained. Credit-free but spend-tracked (≤ $0.02/message, `llm.spend` tagged with the run id) and balance-gated. The server holds no conversation state: send the history back with each call.","inputSchema":{"properties":{"body":{"description":"The JSON request body.","properties":{"history":{"description":"Client-held history (the server stores no conversation state): at most\nthe last 10 turns, each at most 2000 characters, roles user|assistant.","items":{"description":"One client-held conversation turn.","properties":{"content":{"description":"The turn's text, at most 2000 characters.","type":"string"},"role":{"description":"`user` or `assistant` — anything else is a typed 422.","example":"user","type":"string"}},"required":["role","content"],"type":"object"},"type":"array"},"message":{"description":"The user's question about this run. Nonempty, at most 2000 characters.","example":"what happened in this case?","type":"string"}},"required":["message"],"type":"object"},"id":{"description":"Run id","format":"uuid","type":"string"}},"required":["id","body"],"type":"object"},"method":"POST","name":"run_chat","path":"/v1/runs/{id}/chat","requires_auth":true},{"description":"The stored cockpit_doc artifact for a run, verbatim — with ONE stated\nexception: the volatile appendix parts (forecast rows that resolve later,\nappend-only verdicts) attach at read, the `with_taxonomy` discipline. — The run's stored `cockpit_doc` artifact (contract `cockpit/0.1`), served VERBATIM from `run.artifacts` — never re-assembled on read — except the appendix's VOLATILE parts (forecast rows, verdicts), which attach at read because they change after the run completes — and `models`, the run's frozen record of the model slug per role, attached at read as disclosure.","inputSchema":{"properties":{"id":{"description":"Run id","format":"uuid","type":"string"}},"required":["id"],"type":"object"},"method":"GET","name":"get_run_cockpit","path":"/v1/runs/{id}/cockpit","requires_auth":true},{"description":"Answer a run's open decision: validates owner (non-owners see 404 — existence\nunleaked) / undecided (409) / unexpired (410) / picks (422), then ONE tx\n{guarded decided_at flip + `run.decision` event + the run's job RE-QUEUED in\nits original queue env} — the resume. The pipeline reads the decided marker\non re-dispatch and proceeds over the approved picks. — Answer an open decision request on a paused run (B4.0, CONTRACTS §11.11): `{picks: [option ids]}` — between `min` and `max` of the request's `options[].id`, no duplicates. Only the run's creator may answer (others see 404). Records the decision + `run.decision` event and RESUMES the run by re-queueing its job; the pipeline proceeds over the approved picks (e.g. the J4 editorial gate admits story_sim children only for approved picks). Answers are once-only.","inputSchema":{"properties":{"body":{"description":"The answer body: the chosen option ids.","properties":{"picks":{"description":"Option ids from the request's `options[].id`, `min..=max` of them, no\nduplicates.","items":{"type":"string"},"type":"array"}},"required":["picks"],"type":"object"},"decision_id":{"description":"Decision request id","format":"uuid","type":"string"},"id":{"description":"Run id","format":"uuid","type":"string"}},"required":["id","decision_id","body"],"type":"object"},"method":"POST","name":"answer_decision","path":"/v1/runs/{id}/decisions/{decision_id}","requires_auth":true},{"description":"SSE ≡ the log: persisted `run_events` rows in seq order, ending after `run.terminal`. — Server-Sent Events stream of the run's persisted `run_events` rows in seq order (the SSE stream IS the log — nothing synthesized). Each frame's `id` is the row seq, `event` is the row type, and `data` is `{seq, type, payload, created_at}`. The stream ends only AFTER the `run.terminal` row is streamed; completion is never inferable from stream closure. Supports resume via `Last-Event-ID` header or `from` query param (strictly-after seq); `:hb` heartbeat comments while polling.","inputSchema":{"properties":{"from":{"description":"Resume strictly after this seq (same semantics as `Last-Event-ID`).","format":"int64","type":"integer"},"id":{"description":"Run id","format":"uuid","type":"string"}},"required":["id"],"type":"object"},"method":"GET","name":"run_events_sse","path":"/v1/runs/{id}/events","requires_auth":true},{"description":"A run's independent reviews, oldest first, with a summary: `{subject: {kind, id}, workspace_id, summary: {count, stages: {plan, terminal}}, items}`; each stage carries `{count, approve, reject, latest: {id, verdict, created_at} | null}`. The run must be visible in the active space (name its workspace in `x-timepoint-workspace`). Provider and admin members read; a client member is refused.","inputSchema":{"properties":{"id":{"description":"Run id","format":"uuid","type":"string"},"x-timepoint-workspace":{"description":"Workspace uuid to act in (W2). Absent = the caller's personal space. A malformed id, an unknown workspace and one the caller is not a member of all answer 404 workspace_not_found.","format":"uuid","type":["string","null"]}},"required":["id"],"type":"object"},"method":"GET","name":"list_run_independent_reviews","path":"/v1/runs/{id}/independent-reviews","requires_auth":true},{"description":"Record an independent review of a run, by the caller's own login (never named in the body) and its sign-in method (`token`, `magic_link` or `google`; an MCP call is `token`). Stage `plan`: the run reached its confirm_plan decision. Stage `terminal`: the run is complete or failed. The reviewer must not be the run's proposer (its `created_by`). A personal run is visible to its owner only, so every review of one is refused as `own_proposal`: independent review is a workspace feature. Name the run's workspace in `x-timepoint-workspace`; a provider or admin member writes, a client member is refused. Reviews are append-only.","inputSchema":{"properties":{"body":{"additionalProperties":false,"description":"One independent review. The reviewer is never named here: it is the\ncaller's own login.","properties":{"reasons":{"description":"1 to 20 non-empty reasons, each at most 2000 characters (trimmed).","items":{"type":"string"},"type":"array"},"stage":{"description":"`pre_spend` for a kickoff; `plan` or `terminal` for a run.","type":"string"},"subject_sha256":{"description":"Optional: `sha256:<64 hex>`, the hash of what the reviewer read (a plan\nor report hash).","type":["string","null"]},"verdict":{"description":"`approve` or `reject`.","type":"string"}},"required":["stage","verdict","reasons"],"type":"object"},"id":{"description":"Run id","format":"uuid","type":"string"},"x-timepoint-workspace":{"description":"Workspace uuid to act in (W2). Absent = the caller's personal space. A malformed id, an unknown workspace and one the caller is not a member of all answer 404 workspace_not_found.","format":"uuid","type":["string","null"]}},"required":["id","body"],"type":"object"},"method":"POST","name":"record_run_independent_review","path":"/v1/runs/{id}/independent-reviews","requires_auth":true},{"description":"The stored `plan` artifact of a deep_sim run — the roster (each entry with its\nregistry `path`, so the plan page can link `/entity`), the quote and the\nbrief. Served verbatim, and — the point of the endpoint — it OUTLIVES\napproval: the roster used to be reachable only in the pre-approval decision\ncard, so a completed run became unfindable by the question it answered and\nthe entities it named. This is what the `/plan?run=` page reads. — The `plan` artifact of a deep_sim run — roster (with registry paths), quote and brief — served verbatim so the plan survives approval.","inputSchema":{"properties":{"id":{"description":"run id","format":"uuid","type":"string"}},"required":["id"],"type":"object"},"method":"GET","name":"get_run_plan","path":"/v1/runs/{id}/plan","requires_auth":true},{"description":"The stored PortalDoc (`portal/0.1`), taxonomy attached at read time (the\ncockpit's discipline: the artifact is history; the vocabulary is live). — The `portal_doc` of a portal_sim run — the WHOLE ensemble (every candidate, retained or not), the convergence/divergence profile, and the instrumentation, with node labels attached from the live bundle and `models`, the run's frozen record of the model slug per role, attached as disclosure.","inputSchema":{"properties":{"id":{"description":"run id","format":"uuid","type":"string"}},"required":["id"],"type":"object"},"method":"GET","name":"get_run_portal","path":"/v1/runs/{id}/portal","requires_auth":true},{"description":"Re-run ANALYZE over the STORED timeline with the current analysis prompt. — Re-run the deep_sim ANALYZE step over the STORED timeline with the current analysis prompt, the plan's locked invariants and its time frame. Deep_sim runs in the complete state with single-model synthesis only. Writes a new VERSIONED `analysis` artifact (with the deterministic answer check) and replaces the cockpit doc's answer and answer-check finding, in one transaction; moments, gate report, assertions, participation, phase and settlement are untouched. A failed model call writes nothing. Changing the report moves its evidence hash, as reaudit does. Priced `reanalyze = 2` credits, reserve→settle under a fresh audit key. Emits NO run_events — the run's terminal stays its last event; the artifacts, the ledger, and this response are the record.","inputSchema":{"properties":{"id":{"description":"Run id (a delivered deep_sim run)","format":"uuid","type":"string"}},"required":["id"],"type":"object"},"method":"POST","name":"reanalyze_run","path":"/v1/runs/{id}/reanalyze","requires_auth":true},{"description":"Re-run the GATE's two judges over the STORED timeline (B4.2). — Re-run the deep_sim GATE's two judges (allegiance, typicality) plus the pure time-binding check over the STORED timeline (CONTRACTS §11.11 B4.2). Deep_sim runs in the complete (clean or blocked-for-review) state only. Writes a new VERSIONED `gate_report` artifact; a blocked delivery whose reaudit comes back clean flips to delivered-clean (the run row's phase). Priced `reaudit = 2` credits, reserve→settle under a fresh audit key (the run's own reservation is already terminal). Emits NO run_events — the run's terminal stays its last event; the artifact, the ledger, and this response are the record.","inputSchema":{"properties":{"id":{"description":"Run id (a delivered deep_sim run)","format":"uuid","type":"string"}},"required":["id"],"type":"object"},"method":"POST","name":"reaudit_run","path":"/v1/runs/{id}/reaudit","requires_auth":true},{"description":"Replicate a COMPLETE deep_sim run that a kickoff compiled: one new kickoff cloned from the source kickoff (same owner, workspace, question, notes, proposals and scopes; title `Replicate: <source title>`; created by you; status `approved`, its approvals the source's re-signed by you, and a `replicate-…` decision naming the source run, the source kickoff and who approved it there), then ONE deep_sim run admitted from the clone through admission's own path (the clone moves approved → planned in the same transaction). The replicate freezes the SOURCE's frozen cast (the same entity ids and fact ids, in order, never a fresh fact selection), its resolved sim params, its resolved model picks and its office-context date, so a default changed since changes nothing; a model no longer available refuses. Before anything is written the controls the replicate would freeze are compared with the source's on query, original_query, cast (entity and fact ids), edges among the cast, office context (content hash), sim params and models: any difference refuses with `409 replicate_not_identical` and `differences: [{field, reason}]`, unless `allow_differences: true`, which admits it and reports them. Never differs silently. Lineage: the run's `parent_run_id` is the source, and its `run.accepted` event records `lineage: {parent_run_id, relation: \"replicate\", source_kickoff_id}`. Spend: nothing is reserved; the replicate's plan phase composes its own quote, held only when someone answers the NEW run's confirm_plan decision (never auto-confirmed). The research approver only (`approvals::can_approve`), because the replicate is created already approved; a model pick and admin-only sim params need the `admin` capability. `dry_run: true` runs every check and writes nothing, not even the kickoff. Space: with no `x-timepoint-workspace` header this acts in the run's own workspace (you must be a member); a header naming a different workspace is `409 workspace_mismatch`. Send `{}` for the defaults.","inputSchema":{"properties":{"Idempotency-Key":{"description":"M4: a caller-chosen key (≤200 chars). The same caller + key + method/path/body within 24h replays the exact first response (header idempotent-replay: true) instead of replicating again; the same key with a different request answers 422 idempotency_key_reused; a concurrent duplicate answers 409 idempotency_in_progress (retryable). Absent = ordinary behavior: every call that admits mints a new replicate.","type":["string","null"]},"body":{"description":"`POST /v1/runs/{id}/replicate`'s body. Send `{}` for the defaults.","properties":{"allow_differences":{"description":"`true`: admit the replicate even when it would differ from the source\nrun, and report each difference. Default: any difference refuses (409\n`replicate_not_identical`) and nothing is written.","type":["boolean","null"]},"cast_approval_id":{"description":"A usable C1 cast approval of YOUR account, needed when the source's\nfrozen cast cap is above the admin tier.","format":"uuid","type":["string","null"]},"dry_run":{"description":"`true`: run every check (the identity check included) and answer 200\nwith what would be admitted. Nothing is written, not even the\nreplicate's kickoff, and nothing is reserved.","type":["boolean","null"]}},"type":"object"},"id":{"description":"The source run: a complete deep_sim run a kickoff compiled","format":"uuid","type":"string"},"x-timepoint-workspace":{"description":"Workspace uuid to act in (W2). Absent = the run's own workspace (personal, if it has none) — you must still be a member of it. Naming a workspace other than the run's own is refused (409 workspace_mismatch). A malformed id, an unknown workspace and one you are not a member of all answer 404 workspace_not_found.","format":"uuid","type":["string","null"]}},"required":["id","body"],"type":"object"},"method":"POST","name":"replicate_run","path":"/v1/runs/{id}/replicate","requires_auth":true},{"description":"Replication verdicts for a run, read from either side: `{run_id, replicate_of: uuid | null, pairs: [{original: {run_id, state, top_outcome: {label, p, tied_labels?} | null}, replicate: {…}, verdict, basis}]}`. The pairs are (the run this one replicates, this run) when this run is a replicate, then (this run, r) for each replicate r of this run, newest first. `verdict`: `replicated` when both runs are complete and their top outcomes match; `not_replicated` when both have answers whose top outcomes differ; `pending` while either is created or running; `unavailable` when either failed or has no answer outcomes. The top outcome is the highest-probability outcome of the run's current answer (the one its report shows, after any reanalysis); a tie at the top compares the set of tied labels; labels match after case-folding, removing punctuation and collapsing whitespace. Labels are model-written text, so an equivalent outcome worded differently reads `not_replicated`; `basis` states the rule on every pair. Deterministic, no model call. Only runs visible in the space are shown (a client member sees runs delivered to them). Space: as `replicate_run` (absent header = the run's own workspace, if you are a member).","inputSchema":{"properties":{"id":{"description":"A run: an original or a replicate","format":"uuid","type":"string"},"x-timepoint-workspace":{"description":"Workspace uuid to act in (W2). Absent = the run's own workspace (personal, if it has none) — you must still be a member of it. Naming a workspace other than the run's own is refused (409 workspace_mismatch). A malformed id, an unknown workspace and one you are not a member of all answer 404 workspace_not_found.","format":"uuid","type":["string","null"]}},"required":["id"],"type":"object"},"method":"GET","name":"get_run_replication","path":"/v1/runs/{id}/replication","requires_auth":true},{"description":"The long-form HTML rendering of a stored CockpitDoc — answer, then\ntimepoints with moments and dialog, NO coordinates, honesty labels inline.\nThe only server-rendered reading. — It is an EXPORT, not the product's report room. The product used to open\nthis in a bare popup with no top bar, no rail and no way back; that reading\nis `/report?run={id}` now (ui/src/tools/Report.tsx), which flies the same\nCockpitDoc through the same renderers as the cockpit and the storyboard.\nThis response therefore carries its own way home: an export that lands in a\nbrowser (a bookmark, a `curl > file.html`, a pasted link) must still say\nwhere it came from and offer the door back into the product.","inputSchema":{"properties":{"id":{"description":"run id","format":"uuid","type":"string"}},"required":["id"],"type":"object"},"method":"GET","name":"get_run_report","path":"/v1/runs/{id}/report","requires_auth":true},{"description":"Mint a share for a delivered run. Returns the URL once; only the hash is kept.","inputSchema":{"properties":{"body":{"description":"The JSON request body.","properties":{"ttl_days":{"format":"int64","type":["integer","null"]}},"type":"object"},"id":{"description":"run id","format":"uuid","type":"string"}},"required":["id","body"],"type":"object"},"method":"POST","name":"create_share","path":"/v1/runs/{id}/share","requires_auth":true},{"description":"List the run's simulation branches: `{run_id, branches: [{branch_id,\nrevision, state_hash, manifest_hash}]}` (empty when the run has none).\nReadable by whoever may read the run: its creator, a client it was\nDELIVERED to, and a team key's engagement grant. Never a share artifact.\nUnknown and not-yours are one 404; a withdrawn run is 410 for its creator\nand 404 for everyone else.","inputSchema":{"properties":{"id":{"format":"uuid","type":"string"}},"required":["id"],"type":"object"},"method":"GET","name":"list_simulation_branches","path":"/v1/runs/{id}/simulation","requires_auth":true},{"description":"Initialize an explicit resource-disclosure/1 manifest (source_revision,\nknowledge, branch, initial_world). Reusing an identical manifest is idempotent.\nRules are declared assumptions; this does not validate narrative forecasts.","inputSchema":{"properties":{"body":{"description":"The JSON request body."},"id":{"format":"uuid","type":"string"}},"required":["id","body"],"type":"object"},"method":"POST","name":"initialize","path":"/v1/runs/{id}/simulation","requires_auth":true},{"description":"Replay every accepted event and compare the stored branch projection,\nscenario knowledge included. Readable by the run's creator, a client it was\nDELIVERED to, and a team key's engagement grant; never a share artifact.\nThe creator and a team-grant reader get the full view; any other reader\ngets the client view, which withholds the authoring account\n(`knowledge.owner_id`) and says so with `identity_withheld: true`. Unknown\nand not-yours are one 404; a withdrawn run is 410 for its creator only.","inputSchema":{"properties":{"branch":{"format":"uuid","type":"string"},"id":{"format":"uuid","type":"string"}},"required":["id","branch"],"type":"object"},"method":"GET","name":"read","path":"/v1/runs/{id}/simulation/{branch}","requires_auth":true},{"description":"Apply an explicit Proposal: id, branch, expected_revision, actor, action,\nmodel_receipts. Domain rejection returns an accepted=false durable receipt.","inputSchema":{"properties":{"body":{"description":"The JSON request body."},"branch":{"format":"uuid","type":"string"},"id":{"format":"uuid","type":"string"}},"required":["id","branch","body"],"type":"object"},"method":"POST","name":"act","path":"/v1/runs/{id}/simulation/{branch}/actions","requires_auth":true},{"description":"Inspect only the named actor's permitted knowledge at the committed head.\nSame readers and views as the branch read: the creator and a team-grant\nreader get the full view, a client the run was DELIVERED to gets the client\nview (`identity_withheld: true`); never a share artifact.","inputSchema":{"properties":{"actor":{"type":"string"},"branch":{"format":"uuid","type":"string"},"id":{"format":"uuid","type":"string"}},"required":["id","branch","actor"],"type":"object"},"method":"GET","name":"actor","path":"/v1/runs/{id}/simulation/{branch}/actors/{actor}","requires_auth":true},{"description":"Optional actor-scoped speech. Input: id, revision, actor, present, voice\n(plain/brief). Credit-free, provider-accounted, at most four turns per event.\nPending means unresolved work; retrying its ID never resends inference.","inputSchema":{"properties":{"body":{"description":"The JSON request body."},"branch":{"format":"uuid","type":"string"},"id":{"format":"uuid","type":"string"}},"required":["id","branch","body"],"type":"object"},"method":"POST","name":"dialog","path":"/v1/runs/{id}/simulation/{branch}/dialog","requires_auth":true},{"description":"Record a shadow coverage evaluation. Input: id, revision, predictions. Every\ncommitted event/actor is counted under uniform/1 and simple/1. Missing readings\nremain unknown; this never suppresses mandatory work or claims measured savings.","inputSchema":{"properties":{"body":{"description":"The JSON request body."},"branch":{"format":"uuid","type":"string"},"id":{"format":"uuid","type":"string"}},"required":["id","branch","body"],"type":"object"},"method":"POST","name":"fidelity","path":"/v1/runs/{id}/simulation/{branch}/fidelity","requires_auth":true},{"description":"Fork an exact committed prefix: child UUID and parent revision. Initial state\ncomes from replay, not an independently generated predecessor description.","inputSchema":{"properties":{"body":{"description":"The JSON request body."},"branch":{"format":"uuid","type":"string"},"id":{"format":"uuid","type":"string"}},"required":["id","branch","body"],"type":"object"},"method":"POST","name":"fork","path":"/v1/runs/{id}/simulation/{branch}/fork","requires_auth":true},{"description":"Evaluate and retain explicit candidate action paths from one committed state.\nInput: id, revision, target_cash_cents, candidates, max_steps (0..128).\nFeasible ranking excludes invalid and incomplete evaluations. No model call.","inputSchema":{"properties":{"body":{"description":"The JSON request body."},"branch":{"format":"uuid","type":"string"},"id":{"format":"uuid","type":"string"}},"required":["id","branch","body"],"type":"object"},"method":"POST","name":"search","path":"/v1/runs/{id}/simulation/{branch}/search","requires_auth":true},{"description":"One run's spend: `{run_id, kind, state, credits: {reservation_state, reserved, settled}, usd: {openrouter, exa}, openrouter_calls, estimated_usd, children: {runs, openrouter_usd, credits_settled}}`. `usd.openrouter` sums the run's recorded model calls (`estimated_usd` is the part recorded as an estimate); a run makes no Exa calls itself, so `usd.exa` is 0 (research spend is per pass, on `GET /v1/drilldown/jobs/{id}/spend`). `children` sums the runs this run spawned (news_scan stories, find_money and find_grants quick sims). A find_grants run also carries `pass: {exa_usd, openrouter_usd, cap_exa_usd, cap_openrouter_usd, stopped}` — its one sourcing pass's committed spend (settled actuals, else the reservation) against the caps frozen at admission, and whether a cap stopped it; its `usd.exa` is the pass's Exa spend. The run's creator or an administrator; anyone else gets the same 404 as an unknown run.","inputSchema":{"properties":{"id":{"description":"Run id","format":"uuid","type":"string"}},"required":["id"],"type":"object"},"method":"GET","name":"get_run_spend","path":"/v1/runs/{id}/spend","requires_auth":true},{"description":"GET /v1/runs/{id}/steps","inputSchema":{"properties":{"id":{"format":"uuid","type":"string"}},"required":["id"],"type":"object"},"method":"GET","name":"list_orchestration_steps","path":"/v1/runs/{id}/steps","requires_auth":true},{"description":"POST /v1/runs/{id}/steps/{step_id}","inputSchema":{"properties":{"body":{"description":"The JSON request body."},"id":{"format":"uuid","type":"string"},"step_id":{"format":"uuid","type":"string"}},"required":["id","step_id","body"],"type":"object"},"method":"POST","name":"decide_orchestration_step","path":"/v1/runs/{id}/steps/{step_id}","requires_auth":true},{"description":"Which frames hold charts (or stored abstentions) + the served price. — Chart state for a run's storyboard: `{rendered: [t…], abstained: [t…], price_cr}`. Absence of a t means that frame is uncharted until a reader pays for the attempt.","inputSchema":{"properties":{"id":{"format":"uuid","type":"string"}},"required":["id"],"type":"object"},"method":"GET","name":"list_storyboard_charts","path":"/v1/runs/{id}/storyboard/charts","requires_auth":true},{"description":"Chart one frame — charge first, one validation retry, refund on failure or\nabstention; the abstention is stored so repeat clicks answer free. — Ask the chart agent for one frame's quantitative reading. Charges the served `storyboard_chart` price BEFORE the call; refunds typed on failure AND on honest abstention (`{abstained: true, reason}`); a delivered chart stays charged and is stored once forever as `storyboard_chart_t{t}` (spec-validated, reject-never-repair).","inputSchema":{"properties":{"body":{"description":"The JSON request body.","properties":{"regenerate":{"description":"Replace an existing chart or stored abstention (RED-UX A-2, Sean-ruled):\na fresh paid attempt that overwrites the artifact. Absent/false keeps\nthe attempted-once behavior.","type":"boolean"},"t":{"description":"The timepoint to chart (1..=t_count).","format":"int64","type":"integer"}},"required":["t"],"type":"object"},"id":{"format":"uuid","type":"string"}},"required":["id","body"],"type":"object"},"method":"POST","name":"render_storyboard_chart","path":"/v1/runs/{id}/storyboard/charts","requires_auth":true},{"description":"The stored spec — the client renders ONLY this. — The validated chart spec (or stored abstention) for one frame. 404 `not_rendered` until a reader pays for the attempt.","inputSchema":{"properties":{"id":{"format":"uuid","type":"string"},"t":{"format":"int64","type":"integer"}},"required":["id","t"],"type":"object"},"method":"GET","name":"get_storyboard_chart","path":"/v1/runs/{id}/storyboard/charts/{t}","requires_auth":true},{"description":"Which frames are rendered + the served price — the page paints buttons from\nthis, never from a compiled-in price. — Rendered storyboard frames for a run: `{rendered: [t…], price_cr}`. Absence of a t means that frame is text-only until a reader pays to render it.","inputSchema":{"properties":{"id":{"format":"uuid","type":"string"}},"required":["id"],"type":"object"},"method":"GET","name":"list_storyboard_images","path":"/v1/runs/{id}/storyboard/images","requires_auth":true},{"description":"Render one frame — charge first (the credit buys the real attempt), refund\ntyped on failure, store once forever. — Render the image for one storyboard frame `{t}`. Charges the caller the served `storyboard_image` price BEFORE the generation call (refunded typed if generation fails); an already-rendered frame answers `{already: true}` and charges nothing. The png lands as the run artifact `storyboard_image_t{t}`.","inputSchema":{"properties":{"body":{"description":"The JSON request body.","properties":{"regenerate":{"description":"Replace an existing render (RED-UX A-2, Sean-ruled): charges the full\nprice again and overwrites the artifact. Absent/false keeps the\ngenerated-once behavior.","type":"boolean"},"t":{"description":"The timepoint to render (1..=t_count).","format":"int64","type":"integer"}},"required":["t"],"type":"object"},"id":{"format":"uuid","type":"string"}},"required":["id","body"],"type":"object"},"method":"POST","name":"render_storyboard_image","path":"/v1/runs/{id}/storyboard/images","requires_auth":true},{"description":"The stored png, served with the bearer (the page fetches → blob URL). — The rendered png for one storyboard frame. 404 `not_rendered` until a reader pays to render it — absence is a state, not an error page.","inputSchema":{"properties":{"id":{"format":"uuid","type":"string"},"t":{"format":"int64","type":"integer"}},"required":["id","t"],"type":"object"},"method":"GET","name":"get_storyboard_image","path":"/v1/runs/{id}/storyboard/images/{t}","requires_auth":true},{"description":"Long-poll one run. Holds the request until the run is `complete` or `failed`, pauses for a decision (phase `awaiting_decision`), leaves `since_state` or `since_phase`, or `timeout_s` passes (at most 50 s, default 25 s). Answers 200 with the `GET /v1/runs/{id}` document plus `{changed, timed_out, waited_ms}`; call again with the returned state to keep waiting. Readable exactly as `GET /v1/runs/{id}` is.","inputSchema":{"properties":{"id":{"description":"Run id","format":"uuid","type":"string"},"since_phase":{"description":"The phase the caller last saw; the wait also returns when it changes.","type":"string"},"since_state":{"description":"The state the caller last saw (`created`, `running`, …). The wait\nreturns as soon as the run is in another state. Without it the wait\nreturns when the run is terminal or waits on a decision.","type":"string"},"timeout_s":{"description":"Seconds to wait at most, 1 to 50 (default 25).","format":"int64","minimum":0,"type":"integer"}},"required":["id"],"type":"object"},"method":"GET","name":"wait_run","path":"/v1/runs/{id}/wait","requires_auth":true},{"description":"The setting, the questions waiting on the user, and the names being left alone. — The caller's name-look-up setting and what it is doing right now: `{choice: \"allow\"|\"forbid\"|\"consent_gate\", note, asks: [...], withheld: [...]}`. `choice` defaults to `allow` for a caller who has never set it (no row is written on read). Both lists are derived from the setting AS IT STANDS, not from stored history: `asks` are the people a look-up is waiting on the caller for, `withheld` are the watched people the setting is currently keeping Timepoint from looking up, and both are empty under `allow`. Every entry carries a plain-sentence `note`. Read-only — reading never raises a question.","inputSchema":{"properties":{},"required":[],"type":"object"},"method":"GET","name":"get_name_lookups","path":"/v1/settings/name-lookups","requires_auth":true},{"description":"Change the setting. Takes effect on the next look-up, server-side. — Set the caller's name-look-up choice. Body: `{choice: \"allow\"|\"forbid\"|\"consent_gate\"}` — anything else is a typed 422 `invalid_choice` and nothing is written. Answers already given about individual people are kept, so switching away from `consent_gate` and back does not re-ask. Returns `{choice, note}`.","inputSchema":{"properties":{"body":{"description":"The JSON request body.","type":"object"}},"required":["body"],"type":"object"},"method":"PUT","name":"put_name_lookups","path":"/v1/settings/name-lookups","requires_auth":true},{"description":"Answer one of the questions — remembered for that person from then on. — Answer for ONE person: `{allow: true|false}`. The answer is remembered for that person and applies to every later look-up (answering again replaces it). Answering someone who was never asked about is legal and simply records the answer in advance. An entity id that is not one of the caller's own answers 404 — exactly as an id that never existed does, so the refusal says nothing about which records are out there.","inputSchema":{"properties":{"body":{"description":"The JSON request body.","type":"object"},"entity_id":{"description":"The registry entity id.","type":"string"}},"required":["entity_id","body"],"type":"object"},"method":"POST","name":"answer_name_lookup_ask","path":"/v1/settings/name-lookups/asks/{entity_id}","requires_auth":true},{"description":"Revoke (creator only). The page stops resolving immediately.","inputSchema":{"properties":{"token":{"description":"the tps_ share token","type":"string"}},"required":["token"],"type":"object"},"method":"DELETE","name":"revoke_share","path":"/v1/shared/{token}","requires_auth":true},{"description":"The `run.sim_params` defaults (V3, gate G8), for every signed-in caller. Each item: `key`, `kind` (the family its prefix names: deep_sim | portal_sim | quick_sim | story_sim | research | cast_approval), `run_kinds` (the kinds that take it as a per-run `POST /v1/runs {sim_params}` override), `default`, `min`, `max`, `value_type` (int | numeric), `class`, `admin_only` (only an admin may override it), `affects_price` and `description`. Non-admins see every row too: they need the defaults that apply to them, and the numbers carry no secret; `admin_only` marks what they cannot override. `cast_tiers` restates the cast rows (C1): the default entity cap, the admin tier (the row max), facts per entity and the largest approval N.","inputSchema":{"properties":{},"required":[],"type":"object"},"method":"GET","name":"list_sim_params","path":"/v1/sim-params","requires_auth":true},{"description":"Admin. Staged actions, newest first (at most 200), filtered by `status` (default `requested`, the ones awaiting a person). Each carries its `summary`, the `preview` recorded at staging, and its append-only `history`.","inputSchema":{"properties":{"status":{"description":"`requested` (default), `executed`, `rejected`, `cancelled` or `all`.","type":["string","null"]}},"required":[],"type":"object"},"method":"GET","name":"list_staged_actions","path":"/v1/staged-actions","requires_auth":true},{"description":"Admin. Stage a production write for a person to approve in /service: `deliver_review`, `deliver_wrapup`, `deliver_file`, `merge_clients`, `retract_items` or `grant_credits`. This is how an agent delivers: agent and API tokens cannot deliver or merge directly. Staging runs the write's own checks and rolls them back; the record's `preview` is what it would return, or its refusal. A `retract_items` request is checked as a preview only (see `POST /v1/admin/retractions/preview`) and is refused now (409 `staged_action_refused`, refusal `preview_changed` or `not_retractable`) unless it matches its `preview_hash` and can run. A `grant_credits` request (target `{owner_id, credits}`) adds credits to an existing identity: it is refused now (422 `invalid_staged_action`) unless `owner_id` names an identity and `credits` is a whole number from 1 to the current `credit_grant_max_credits`; its `preview.would_return` is `{owner_id, credits, balance_before, balance_after}`. Nothing changes until a person approves. Staging the same kind and target again while one is open returns the open one (200); for `grant_credits` that is one open request per `owner_id`, whatever the amount.","inputSchema":{"properties":{"Idempotency-Key":{"description":"M4: a caller-chosen key (≤200 chars). The same caller + key + method/path/body within 24h replays the first response (header idempotent-replay: true) instead of re-running; the same key with a different request answers 422 idempotency_key_reused; a concurrent duplicate answers 409 idempotency_in_progress (retryable). Absent = ordinary behavior — including this operation's own same-kind-and-target replay.","type":["string","null"]},"body":{"description":"The JSON request body.","properties":{"content_base64":{"description":"The file's bytes, base64 (standard alphabet).","type":"string"},"content_type":{"description":"One of text/markdown, text/csv, application/json, text/plain, application/pdf.","type":"string"},"folder":{"description":"Subfolder path segment, e.g. \"briefs\" (empty for the root folder).","type":"string"},"name":{"description":"File name within the folder, 1-160 characters, no slashes, not hidden.","type":"string"},"title":{"type":["string","null"]}},"required":["name","content_type","content_base64"],"type":"object"}},"required":["body"],"type":"object"},"method":"POST","name":"stage_action","path":"/v1/staged-actions","requires_auth":true},{"description":"Admin. Approval grants, newest first (at most 100): who wrote each, the note, when it expires, its `status` (`active`, `expired`, `revoked` or `used`) and its items (each staged action, its status, and whether an agent used it).","inputSchema":{"properties":{},"required":[],"type":"object"},"method":"GET","name":"list_approval_grants","path":"/v1/staged-actions/grants","requires_auth":true},{"description":"Admin, through your own browser sign-in only (email link or Google; never a token, an MCP token or a session over MCP). Let an agent approve these staged deliveries (`deliver_review`, `deliver_wrapup`, `deliver_file`; never `grant_credits` or `retract_items`). You must be allowed to approve each one yourself. The grant binds each action and what it would deliver now; it lasts 24 hours, each item is used once, and you can revoke it. An agent approves with `grant_id`, your `note` restated exactly as `grant_note`, and its own `agent_note`.","inputSchema":{"properties":{"body":{"description":"The JSON request body.","properties":{"note":{"description":"Your note to the agent, 10 to 500 characters. The agent must restate it\nexactly when it approves.","type":"string"},"staged_action_ids":{"description":"The staged actions an agent may approve: 1 to 20, each open and of a\ngrantable kind (`deliver_review`, `deliver_wrapup`, `deliver_file`).","items":{"format":"uuid","type":"string"},"type":"array"}},"required":["staged_action_ids","note"],"type":"object"}},"required":["body"],"type":"object"},"method":"POST","name":"create_approval_grant","path":"/v1/staged-actions/grants","requires_auth":true},{"description":"Admin, through your own browser sign-in only. Revoke an approval grant: no agent may approve under it from now on. Items already used stay used.","inputSchema":{"properties":{"id":{"description":"Grant id","format":"uuid","type":"string"}},"required":["id"],"type":"object"},"method":"POST","name":"revoke_approval_grant","path":"/v1/staged-actions/grants/{id}/revoke","requires_auth":true},{"description":"Admin. One staged action, plus `check_now`: its write's checks run again now and rolled back (`{ok, would_return}` or `{ok: false, status, refusal}`), so the approver sees what approving would do at this moment. `can_approve` says whether this caller, through this credential, may approve or reject it now; `approve_refusal` says why not (null when it may). `grant` is the approval grant it was approved under, else the newest live one covering it (`{id, granted_by, note, expires_at, revoked_at, used_at, used_by, usable, content_changed}`), or null; `can_grant` says whether you may write a grant for it now (your browser sign-in); `can_approve_with_grant` says whether this agent may approve it under that grant now.","inputSchema":{"properties":{"id":{"description":"Staged action id","format":"uuid","type":"string"}},"required":["id"],"type":"object"},"method":"GET","name":"get_staged_action","path":"/v1/staged-actions/{id}","requires_auth":true},{"description":"Admin. A person approves directly: a browser sign-in, a person's session over MCP, or a person's API or MCP token (recorded as `decided_via`: `magic_link`, `google`, `mcp:<method>`, `mcp_token` or `token`). AN AGENT may approve only a delivery (`deliver_review`, `deliver_wrapup`, `deliver_file`) and only under a person's approval grant (an operator's token, `operator`, approves deliveries only and needs no grant; `merge_clients`, `retract_items` and `grant_credits` need a person's browser sign-in, 403 `person_sign_in_required`): send `grant_id`, `grant_note` (the person's note, restated exactly) and `agent_note` (what you are doing, 10 to 500 characters). The grant must be live (24 hours, not revoked), cover this action, be unused for it, and what the action would deliver must not have changed since the grant; otherwise 403 `grant_refused`. Without a grant an agent gets 403 `person_sign_in_required`; ask a person to write a grant in /service. The requester may approve its own request only under a grant. Any person admin may approve any client's request. Approve a `requested` staged action: every check of its write runs again and the write happens now, recorded as the approver's. You cannot approve an action you staged yourself (a request a token staged may be approved by that identity's own browser sign-in). If a check refuses now, nothing is written, the refusal is recorded, and the action stays `requested`. Approving `grant_credits` writes one ledger grant and one credit-grant record naming who asked and who approved; its `result` is `{owner_id, credits, balance_before, balance_after, entry_id, grant_id}`. Its checks run again first: a cap lowered since staging refuses it (409, refusal `invalid_staged_action`).","inputSchema":{"properties":{"body":{"description":"The JSON request body.","properties":{"agent_note":{"description":"Approve under a grant: what the agent is doing, 10 to 500 characters.","type":["string","null"]},"grant_id":{"description":"Approve only: an agent approving under a person's approval grant names\nit here (see `POST /v1/staged-actions/grants`). Ignored for a person who\nmay approve directly.","format":"uuid","type":["string","null"]},"grant_note":{"description":"Approve under a grant: the grant's note, restated exactly.","type":["string","null"]},"note":{"description":"Optional note recorded with the decision (send `{}` for none).","type":["string","null"]}},"type":"object"},"id":{"description":"Staged action id","format":"uuid","type":"string"}},"required":["id","body"],"type":"object"},"method":"POST","name":"approve_staged_action","path":"/v1/staged-actions/{id}/approve","requires_auth":true},{"description":"Admin. Withdraw a `requested` action you staged (the same identity that staged it; a token may cancel its own). Final; nothing is written.","inputSchema":{"properties":{"body":{"description":"The JSON request body.","properties":{"agent_note":{"description":"Approve under a grant: what the agent is doing, 10 to 500 characters.","type":["string","null"]},"grant_id":{"description":"Approve only: an agent approving under a person's approval grant names\nit here (see `POST /v1/staged-actions/grants`). Ignored for a person who\nmay approve directly.","format":"uuid","type":["string","null"]},"grant_note":{"description":"Approve under a grant: the grant's note, restated exactly.","type":["string","null"]},"note":{"description":"Optional note recorded with the decision (send `{}` for none).","type":["string","null"]}},"type":"object"},"id":{"description":"Staged action id","format":"uuid","type":"string"}},"required":["id","body"],"type":"object"},"method":"POST","name":"cancel_staged_action","path":"/v1/staged-actions/{id}/cancel","requires_auth":true},{"description":"Admin, and a person, never an agent, through any credential, under the same rule as approve (not the requester). An approval grant never lets an agent reject. Reject a `requested` staged action. Final; nothing is written.","inputSchema":{"properties":{"body":{"description":"The JSON request body.","properties":{"agent_note":{"description":"Approve under a grant: what the agent is doing, 10 to 500 characters.","type":["string","null"]},"grant_id":{"description":"Approve only: an agent approving under a person's approval grant names\nit here (see `POST /v1/staged-actions/grants`). Ignored for a person who\nmay approve directly.","format":"uuid","type":["string","null"]},"grant_note":{"description":"Approve under a grant: the grant's note, restated exactly.","type":["string","null"]},"note":{"description":"Optional note recorded with the decision (send `{}` for none).","type":["string","null"]}},"type":"object"},"id":{"description":"Staged action id","format":"uuid","type":"string"}},"required":["id","body"],"type":"object"},"method":"POST","name":"reject_staged_action","path":"/v1/staged-actions/{id}/reject","requires_auth":true},{"description":"The published integrity statistic, split into the caller's own and the\nhouse's — the same law every other run-scoped read already obeys. — TT integrity statistics in TWO clearly-named parts, each with its own honest basis: `{yours, house}`, where each part is `{moments_total, classified, abstained, never_classified, abstain_rate, by_day, basis}`. `yours` counts moments from runs the caller started — exact, their data. `house` counts moments from the house's published news wire — exact, and safe to serve because that wire is public by construction. THERE IS NO THIRD BUCKET: no other account's activity is aggregated here at any granularity, and no figure is noised, rounded or otherwise fabricated. `abstain_rate` = abstained / **classified** — how often the reader DECLINED when it was actually asked. It is `null` when that part has nothing classified, with `basis` saying so (absence labeled, never a fabricated 0). `never_classified` is reported separately and is NOT in the denominator: a moment nobody put to the reader is not a moment the reader declined, and dividing by the total conflated the two. `by_day` holds that part's most recent ≤14 days carrying moments, chronological: `[{day, total, abstained}]`.","inputSchema":{"properties":{},"required":[],"type":"object"},"method":"GET","name":"tt_stats","path":"/v1/stats/tt","requires_auth":true},{"description":"BETA §10's two wall numbers, served (B6.4): Synths under monitoring and sims\nrun — the caller's own, and the house's. Queries only.","inputSchema":{"properties":{},"required":[],"type":"object"},"method":"GET","name":"wall_stats","path":"/v1/stats/wall","requires_auth":true},{"description":"The Trajectory room, in one read. — Your Trajectory room in one read. Always 200. `gate` says whether TP_TRAJECTORY_ENABLED switched it on for this deployment; while it is off every write answers 503 `trajectory_not_enabled`. `calendar.standing` is the stored connection read without a Google round trip (`not_configured`, `not_connected`, `token_unreadable`, `scope_missing` or `connected`, with a reason) and `calendar.last_read` the state of the last trailing read (null before one). `defaults` carries 85 years, 8 hours of sleep, 07:00 to 23:00 and an 8-week window, with why 85; `categories` is the taxonomy in display order. `profile` and `sundial` are null until you start the clock; the sundial is your remaining waking hours drawn as one day, with the dial's clock reading and one sentence. `snapshot` is the latest unexpired calendar aggregate: hours and events a week per category, never an event title. `portfolio` (null until a calendar read or a slider gives it rows) projects each category over the rest of your life: share of the waking week, lifetime hours, share of remaining conscious life, minutes of the life-day, the observed weekly range and an uncalibrated value score, with unscheduled time as its own row; a week over its waking hours is scaled with a warning, never capped silently. `time_machine` is cumulative hours per row at each age up to your life expectancy. `quotes` are credits from the live price list (null when a price is not listed, never 0). `sims` and `whats_next` fill in later. Every projection is deterministic arithmetic over your own numbers, not a prediction. Private: only your own rows are read.","inputSchema":{"properties":{},"required":[],"type":"object"},"method":"GET","name":"get_trajectory","path":"/v1/trajectory","requires_auth":true},{"description":"Read the calendar's last few weeks and rebuild the portfolio from them. — Read your primary calendar's last `weeks` weeks (4 to 12; null reads your profile's window) live, classify the events, and keep the result as one aggregate snapshot: hours and events a week per category, never an event, a title or a person. Declined, free, cancelled and all-day events are set aside (counted in the snapshot's `dropped`), and double-booked minutes count once. Answers 200 `{document, calendar: {state, reason}, series, notes}`: `document` is the room as `get_trajectory` reads it, `calendar` the state of THIS read (`ok` or one of the six calendar refusals, with its reason), `series` the recurring activities found, each with the rule that chose its category (shown to you in this response only and never stored), and `notes` plain sentences on what was set aside. When the read is not `ok` nothing is stored and the document is unchanged. Needs a profile (409 `profile_required`); 422 `invalid_request` for a window outside 4 to 12; 503 `trajectory_not_enabled` while TP_TRAJECTORY_ENABLED is off.","inputSchema":{"properties":{"body":{"description":"`POST /v1/trajectory/portfolio`.","properties":{"weeks":{"description":"The trailing window in weeks, 4 to 12; null or absent reads the\nprofile's `window_weeks`.","format":"int64","type":["integer","null"]}},"type":"object"}},"required":["body"],"type":"object"},"method":"POST","name":"build_trajectory_portfolio","path":"/v1/trajectory/portfolio","requires_auth":true},{"description":"Forget Trajectory: profile, snapshots and sim links. — Forget your Trajectory: the profile, every calendar snapshot and every simulation link are deleted. Runs a simulation already admitted stay in your run history, and your calendar connection is not touched. Idempotent: `{forgotten: true, had_profile, snapshots_deleted, sims_deleted, note}`. Answers whether or not TP_TRAJECTORY_ENABLED is on: a person can always forget what was kept about them.","inputSchema":{"properties":{},"required":[],"type":"object"},"method":"DELETE","name":"forget_trajectory","path":"/v1/trajectory/profile","requires_auth":true},{"description":"Start or change the Trajectory profile. — Start or change your Trajectory profile. The first call needs `birth_date` (YYYY-MM-DD) or `birth_year` (stored as July 2 of that year, `birth_precision: year`); every other field keeps its stored value, or the default, when left out, and `ideal_hours: null` clears a saved ideal week. Refusals are 422 `{error: invalid_request, reason}`: your age must be above 0 and below `life_expectancy_years`, which is 50 to 120; sleep 0 to 16 hours; `wake` before `bed`, and `bed - wake` within two hours of the waking day your sleep leaves; `window_weeks` 4 to 12; hours 0 to 168 a week on known categories (unscheduled is what the others leave and cannot be set); at most 50 overrides of 2 to 80 characters; ratings finite. Answers the whole document, as `get_trajectory`. 503 `trajectory_not_enabled` while TP_TRAJECTORY_ENABLED is off.","inputSchema":{"properties":{"body":{"description":"`PUT /v1/trajectory/profile`. Every field is optional on update; the first\ncreate needs `birth_date` or `birth_year`. A field left out keeps what is\nstored (or the config default on create).","properties":{"bed":{"description":"Bed time, `HH:MM`, after `wake` (default `23:00`): the dial's sunset.","type":["string","null"]},"birth_date":{"description":"`YYYY-MM-DD`.","type":["string","null"]},"birth_year":{"description":"A year alone is stored as July 2 of that year, `birth_precision: year`.","format":"int32","type":["integer","null"]},"ideal_hours":{"description":"A saved ideal week, hours a week per category key; `null` clears it.","type":["object","null"]},"life_expectancy_years":{"description":"50 to 120 (default 85).","format":"double","type":["number","null"]},"manual_hours":{"additionalProperties":{"format":"double","type":"number"},"description":"Slider values, hours a week per category key (0 to 168). Replaces the\nstored map.","propertyNames":{"type":"string"},"type":["object","null"]},"overrides":{"description":"Title rules (at most 50). Replaces the stored list.","items":{"description":"An owner's correction, stored on the profile: an event whose title\ncontains `title_contains` (case-insensitive) belongs in `category`. The\nowner typed or tapped it, which is why it may hold title text at all.","properties":{"category":{"type":"string"},"title_contains":{"type":"string"}},"required":["title_contains","category"],"type":"object"},"type":["array","null"]},"ratings":{"additionalProperties":{"description":"The value lens's inputs for one category (Life Choices'\n`calculateEventValue`). A seed in the config, or the owner's re-rating.","properties":{"cost_max":{"format":"double","type":"number"},"cost_min":{"format":"double","type":"number"},"events_per_year":{"format":"double","type":"number"},"hours_max":{"format":"double","type":"number"},"hours_min":{"format":"double","type":"number"},"love_mult":{"format":"double","type":"number"},"people_mult":{"format":"double","type":"number"},"value_max":{"format":"double","type":"number"},"value_min":{"format":"double","type":"number"},"vortex_mult":{"format":"double","type":"number"}},"required":["value_min","value_max","cost_min","cost_max","hours_min","hours_max","people_mult","love_mult","vortex_mult","events_per_year"],"type":"object"},"description":"Re-ratings per category key; a key left out uses the config seed.\nReplaces the stored map.","propertyNames":{"type":"string"},"type":["object","null"]},"sleep_hours_per_day":{"description":"0 to 16 (default 8).","format":"double","type":["number","null"]},"wake":{"description":"Wake time, `HH:MM` (default `07:00`): the dial's sunrise.","type":["string","null"]},"window_weeks":{"description":"Trailing calendar window in weeks, 4 to 12 (default 8).","format":"int64","type":["integer","null"]}},"type":"object"}},"required":["body"],"type":"object"},"method":"PUT","name":"put_trajectory_profile","path":"/v1/trajectory/profile","requires_auth":true},{"description":"The time machine at one age, and an ideal week against the current one. — Arithmetic over your own stored numbers (the latest calendar snapshot and your sliders, the same week the room shows), nothing read live and nothing stored. `at_age` (0 to your life expectancy) answers `cumulative`: hours per row by that age at your current pace. `ideal_hours` (hours a week per category, 0 to 168; a category left out keeps its current hours; unscheduled cannot be set) answers `compare`: per category the current and ideal hours a week and the ideal's change to lifetime hours, minutes of your life-day and share of remaining conscious life, the same for unscheduled time, an over-budget warning when the ideal week holds more than your waking hours (scaled to fit, never capped silently), and one sentence. Either part is null when not asked for. A deterministic projection, not a prediction. Needs a profile (409 `profile_required`); 422 `invalid_request` with a reason; 503 `trajectory_not_enabled` while TP_TRAJECTORY_ENABLED is off.","inputSchema":{"properties":{"body":{"description":"`POST /v1/trajectory/projection`. Both fields are optional; each one asks\nfor its own part of the answer.","properties":{"at_age":{"description":"An age for the time machine (0 to your life expectancy): cumulative\nhours per row at that age.","format":"double","type":["number","null"]},"ideal_hours":{"description":"An ideal week to compare with the current one, hours a week per\ncategory key (0 to 168; unscheduled is what the others leave and cannot\nbe set). A key left out keeps its current hours.","type":["object","null"]}},"type":"object"}},"required":["body"],"type":"object"},"method":"POST","name":"project_trajectory","path":"/v1/trajectory/projection","requires_auth":true},{"description":"Start a trajectory simulation. — Simulate where your current pattern leads, through the runs the product already has. `mode: quick` admits a `quick_sim` (the `quotes.quick` credits, held by the ledger) and answers `{run_id, read_url}`; `mode: deep` admits a `deep_sim` whose plan brings the exact quote as a decision on the run, and nothing is reserved until you confirm it; a `goal` is accepted in any mode only together with its `by` date (422 otherwise, so a deep query never drops it); `mode: walk_back` pins `goal` by `by` as an ordinary pin (`pin_outcome`; `quotes.walk_back` credits) and answers `{pin_id}`, which `record_outcome` scores later. The query is composed from your profile's numbers and category labels only, never an event title, a name or an address; it is returned as `query`. Every result is generated and uncalibrated, and says so in `label`. 409 `profile_required` before a profile; 402 when credits are short (nothing charged); 503 `trajectory_not_enabled` while off.","inputSchema":{"properties":{"body":{"description":"The simulation to start.","properties":{"by":{"description":"`walk_back`: the date to reach it by, `YYYY-MM-DD`, after today.","type":["string","null"]},"goal":{"description":"`walk_back`: the outcome to reach (at most 300 characters). A deep\nquery carries it as the pinned goal when given.","type":["string","null"]},"horizon_years":{"description":"The horizon the question names (1 to 45 years; default 5).","format":"int32","minimum":0,"type":["integer","null"]},"ideal_hours":{"additionalProperties":{"format":"double","type":"number"},"description":"An ideal week, `{category: hours a week}`; its differences from the\ncurrent week ride into a deep query. Optional.","propertyNames":{"type":"string"},"type":["object","null"]},"mode":{"description":"`quick` (a `quick_sim`, 2 credits), `deep` (a `deep_sim`; the plan\nbrings the exact quote and nothing is reserved until you confirm) or\n`walk_back` (a pin walked back from `goal` by `by`).","type":"string"}},"required":["mode"],"type":"object"}},"required":["body"],"type":"object"},"method":"POST","name":"simulate_trajectory","path":"/v1/trajectory/simulate","requires_auth":true},{"description":"Serve the vendored bundle file VERBATIM (raw bytes, never re-serialized) with\n`ETag: \"<schema> v<version>\"`; `If-None-Match` hit → 304. — The loaded TT taxonomy bundle, served VERBATIM (raw file bytes, never re-serialized) with `ETag: \"<schema> v<version>\"`. Send `If-None-Match` to get a 304 on an unchanged bundle.","inputSchema":{"properties":{},"required":[],"type":"object"},"method":"GET","name":"serve_bundle","path":"/v1/tt/bundle","requires_auth":true},{"description":"Accept one file, sanitize it, store the text. — Accept ONE file as `multipart/form-data` and store the sanitized TEXT it contains (never the raw bytes, never raw HTML). Guardrails run in order and every one refuses typed, storing nothing: (1) a 2 MB size cap enforced WHILE STREAMING → 413 `file_too_large` naming the cap; (2) a content sniff — the BYTES decide the type, not the extension — accepting plain text, markdown, csv and html, refusing everything else 422 `unsupported_file_type` (a PDF gets its own honest reason: no PDF reader exists yet); (3) valid UTF-8 or 422 `not_utf8` (never a lossy decode); (4) sanitization — HTML is tag-stripped (scripts/styles dropped, entities decoded) and all text is scrubbed of control and invisible characters. Text longer than the stored ceiling is truncated and SAID SO. The stored text is DATA: wherever it reaches a model it rides inside a delimited untrusted-content block declared in a versioned prompt row, and the delimiters are neutralized in the text itself so they cannot be forged. Returns 201 `{upload_id, filename, kind, bytes, chars, truncated, preview, created_at}`. Reads are owner-scoped.","inputSchema":{"properties":{},"required":[],"type":"object"},"method":"POST","name":"create_upload","path":"/v1/uploads","requires_auth":true},{"description":"The proposals — nothing is written. — The people, organizations and markets a stored reading found in the document, as PROPOSALS: `{upload_id, filename, written: false, proposals: [{index, name, kind, role, slug, entity_id, path, matches_existing, would_mint}]}`. This call is read-only — it resolves each name against the registry to say whether approving it would match an existing entity or mint a new one, and writes NOTHING. Approving is a separate POST to the same path carrying the indexes the human chose. Owner-scoped; 409 `no_extract` until the document has been read.","inputSchema":{"properties":{"id":{"description":"Upload id","format":"uuid","type":"string"}},"required":["id"],"type":"object"},"method":"GET","name":"list_entity_proposals","path":"/v1/uploads/{id}/entities","requires_auth":true},{"description":"Write the approved subset — resolve-or-mint, then one assertion each. — Write ONLY the proposals a human approved. The body names the proposal indexes (`{approve: [0, 2]}`) — an absent or empty list is a typed refusal, never an implicit approve-all, and an index outside the proposal list is refused before anything is written. Each approved name goes through `entities::resolve_or_mint` (the registry's one minter) and then through `entities::commit_assertions` (the ONE writer of assertions — the gate is code and this path goes through it, never around it). Each written row states its own provenance in the column named for it: basis GROUNDED (a person approved a real, named document), source `document_upload <upload id> :: <filename>, approved by <account>`, and no run id, because an upload is not a run. The value carries the claim alone — the role the document puts the actor in. Returns `{upload_id, approved, entities: [...], assertions: {committed, skipped}, provenance}`.","inputSchema":{"properties":{"body":{"description":"The JSON request body.","properties":{"approve":{"description":"The proposal indexes the human approved. Only these are written; an empty\nor absent list is a typed refusal, never \"approve everything\".","example":[0,2],"items":{"minimum":0,"type":"integer"},"type":["array","null"]}},"type":"object"},"id":{"description":"Upload id","format":"uuid","type":"string"}},"required":["id","body"],"type":"object"},"method":"POST","name":"approve_entities","path":"/v1/uploads/{id}/entities","requires_auth":true},{"description":"Read one stored document into a simulatable situation. — Read a stored document into a simulatable situation — the SAME structured extraction the link drop runs (`storysim::run_extract`), over a versioned prompt row that wraps the document in a delimited UNTRUSTED CONTENT block and states that the text is data, never instructions. Returns `{upload_id, filename, title, summary, scenario, cast: [{name, kind, role}], themes, model, tier, already}`. Charge-once: a document already read serves the stored reading with `already: true` and spends nothing. When the model cannot produce a valid reading the refusal is typed (422 `extract_invalid`) and NOTHING is written. Owner-scoped: an unknown upload and somebody else's upload are the same 404.","inputSchema":{"properties":{"id":{"description":"Upload id","format":"uuid","type":"string"}},"required":["id"],"type":"object"},"method":"POST","name":"extract_upload","path":"/v1/uploads/{id}/extract","requires_auth":true},{"description":"Assert a VERDICT about one of the caller's own moments — what the evidence currently supports, as an append-only assessment EVENT (never a column update). `verdict` is one of `supported | unsupported | contradicted`; `note` (required) carries the evidence in words; `supersedes` optionally names the prior verdict this one replaces in the view — the prior row persists. The response records the taxonomy release the assessment was made under, and `filter_version: null` labeled as 'assessed before filters existed' — the field is not accepted as input because no filter is deployed to cite.","inputSchema":{"properties":{"body":{"additionalProperties":false,"description":"What a caller may assert. `deny_unknown_fields` is the 8.2 gate: a\n`filter_version` (or anything else) offered here is refused, not dropped.","properties":{"moment_id":{"description":"The moment being assessed — must be the caller's own.","type":"string"},"note":{"description":"Why, in words a person can act on. Required, non-empty.","type":"string"},"supersedes":{"description":"The verdict this one supersedes IN THE VIEW. The predecessor stays.","format":"uuid","type":["string","null"]},"verdict":{"description":"supported | unsupported | contradicted (three-valued on purpose).","type":"string"}},"required":["moment_id","verdict","note"],"type":"object"}},"required":["body"],"type":"object"},"method":"POST","name":"post_verdict","path":"/v1/verdicts","requires_auth":true},{"description":"What does the graph currently support for this moment? The CURRENT verdict (newest assessment) plus the full history of what was believed when, newest first — each row `{id, verdict, filter_version, bundle_version, note, asserted_by, asserted_at, supersedes}`. `current` is `null` for a moment that exists but has never been assessed — an absence, labeled in `basis`, never an implied standing. Readable for the caller's own moments and the house's.","inputSchema":{"properties":{"moment_id":{"description":"The moment's id","type":"string"}},"required":["moment_id"],"type":"object"},"method":"GET","name":"get_verdicts","path":"/v1/verdicts/{moment_id}","requires_auth":true},{"description":"Admin only. Every workspace, newest first: id, display name, creator and member count.","inputSchema":{"properties":{},"required":[],"type":"object"},"method":"GET","name":"list_workspaces","path":"/v1/workspaces","requires_auth":true},{"description":"Admin only (W3, gate G7). Create a workspace; the creator is inserted as its `admin` member so it is never created with no one able to administer it.","inputSchema":{"properties":{"body":{"description":"The JSON request body.","properties":{"display_name":{"type":"string"}},"required":["display_name"],"type":"object"}},"required":["body"],"type":"object"},"method":"POST","name":"create_workspace","path":"/v1/workspaces","requires_auth":true},{"description":"Admin only. A workspace's members: name, role, who added them and when.","inputSchema":{"properties":{"id":{"description":"Workspace id","format":"uuid","type":"string"}},"required":["id"],"type":"object"},"method":"GET","name":"list_workspace_members","path":"/v1/workspaces/{id}/members","requires_auth":true},{"description":"Admin only. Add an identity to a workspace with a role (`provider`, `client` or `admin`). ZERO CONTACT (PLAN.md policy 1): this never sends mail — adding a member is silent by construction. A `client` role is exclusive: that identity may hold no other workspace membership (the 0055 trigger enforces it), refused as `workspace_client_exclusive`. The identity must already have a token minted (`provision_client` for a client, or an existing account for a provider/admin).","inputSchema":{"properties":{"body":{"description":"The JSON request body.","properties":{"name":{"type":"string"},"role":{"type":"string"}},"required":["name","role"],"type":"object"},"id":{"description":"Workspace id","format":"uuid","type":"string"}},"required":["id","body"],"type":"object"},"method":"POST","name":"add_workspace_member","path":"/v1/workspaces/{id}/members","requires_auth":true},{"description":"Admin only. Remove a member from a workspace. ZERO CONTACT (PLAN.md policy 1): silent, no mail.","inputSchema":{"properties":{"body":{"description":"The JSON request body.","properties":{"name":{"type":"string"}},"required":["name"],"type":"object"},"id":{"description":"Workspace id","format":"uuid","type":"string"}},"required":["id","body"],"type":"object"},"method":"POST","name":"remove_workspace_member","path":"/v1/workspaces/{id}/members/remove","requires_auth":true},{"description":"GET /v1/world-model","inputSchema":{"properties":{"cursor":{"format":"uuid","type":"string"},"engagement_scope":{"description":"in or all; client projections always use in. Only explicit scope decisions omit research.","type":"string"},"include_generated":{"type":"boolean"},"kind":{"type":"string"},"limit":{"minimum":0,"type":"integer"},"part":{"description":"entities (default), facts, or relationships; iterate each to inventory the entire model.","type":"string"},"q":{"type":"string"},"updated_since":{"format":"date-time","type":"string"},"x-timepoint-workspace":{"description":"Shared workspace. MCP client and team keys inject their bound workspace.","format":"uuid","type":["string","null"]}},"required":[],"type":"object"},"method":"GET","name":"get_world_model","path":"/v1/world-model","requires_auth":true},{"description":"`GET /v1/wrapups` — the wrap-ups delivered to the caller. — The engagement wrap-up reports delivered to the caller, newest first: wrap-up id, engagement id, the engagement's question, the delivered version, its content hash, delivery time and how many simulations it rolls up. A wrap-up is listed once it has passed QA and been delivered, to the engagement's client or a member of the workspace the engagement is linked to. Open one with `get_delivered_wrapup` (structured) or `get_wrapup_report` (readable).","inputSchema":{"properties":{},"required":[],"type":"object"},"method":"GET","name":"list_delivered_wrapups","path":"/v1/wrapups","requires_auth":true},{"description":"`GET /v1/wrapups/{id}` — one delivered wrap-up, structured. — One delivered engagement wrap-up as structured data: the delivered version's snapshot (`wrapup/0.1`) — the engagement, a summary, each delivered simulation's answer (outcomes with uncalibrated probabilities, the highest-leverage action, uncertainty, key moments; the full run opens with `get_run_report`), the research behind it (research jobs, approved record counts, current decision-makers with a re-verify flag) and provenance. Unknown and not-yours are the same 404.","inputSchema":{"properties":{"id":{"description":"wrap-up id (from `list_delivered_wrapups`)","format":"uuid","type":"string"}},"required":["id"],"type":"object"},"method":"GET","name":"get_delivered_wrapup","path":"/v1/wrapups/{id}","requires_auth":true},{"description":"`GET /v1/wrapups/{id}/report` — one delivered wrap-up, readable. — One delivered engagement wrap-up as a readable document: `format=html` (default; a self-contained page whose print stylesheet makes print-to-PDF the PDF export) or `format=md` (markdown, best for an agent). Same content and the same read rule as `get_delivered_wrapup`.","inputSchema":{"properties":{"format":{"description":"`html` (default; print it to save a PDF) or `md`.","type":"string"},"id":{"description":"wrap-up id","format":"uuid","type":"string"}},"required":["id"],"type":"object"},"method":"GET","name":"get_wrapup_report","path":"/v1/wrapups/{id}/report","requires_auth":true}],"v":"mcp-manifest/0.1"}