Diesel2 prototype (proto1). This page lists every API route; how to use the pages is in Help.
Checking…
GET|HEAD /ping opsthe ops probe (^ping-1): 200 ok as text/plain, no-store, on every host, before everything else (not logged, not counted, never limited); 503 until the server listens. The admin agent answers GET /admin/ping the same wayGET /api/v2/health core{ok, version, topics, project, cdn: {host, ok}, mongo: {configured, connected, store}, heap, copy: false | {of, primary}}; openGET /api/v2/logs?date=&level=&kind=&project=&text=&tests=1&limit= ops E_FORBIDDENthe detailed log, one day, newest first: {date, days, total, data}; the Architect or the ops tokenGET /api/v2/styles stylesstylesheets: {total, default, current, data}; HTML twin /stylesPOST /api/v2/logs/client ops E_BAD_JSON E_TOO_BIGa client's own log into the detailed log (kind client): {source, version, device, lines}; any logged-in accountPOST /api/v2/logs/probe?kind=internal|uncaught|rejection ops E_FORBIDDENthe box's ops token only, on any project: raise a failure on purpose, to check it is logged → 202 {ok, kind}, or the failure itselfGET /api/v2/logs/client?scope=project|all&days=1..14&device=&level=error&limit= opsyour own client log lines (the Obsidian plugin's), newest first: {data: [{at, clientAt, project, device, source, version, level, msg, serverRid}], devices, last: {at, device}, total}; yours only, from a session or any of your tokens; HTML twin /obsidianDELETE /api/v2/logs/client?scope=project|all&before= ops E_FORBIDDENclears your own client log lines (older than before=, an ISO time, if given), never anyone else's: {removed}GET /api/v2/system ops E_FORBIDDENthis project's own system view: {project, level, view: full | small, total, data: [{id, name, spec, design, impl, tests, missing}], counts, domain}; the page is /components (P-226)GET /api/v2/system/index?impl=&stamp=1&since= ops{…the index}: the system view's index for this project (the small view below pro); stamp=1: {stamp}; since=GET /api/v2/system/implementation ops{…}: d2's own implementation, its components (d2spec only)GET /api/v2/implementations ops E_FORBIDDEN{source: settings | tags, data: [{name, title, kind, docs, …the row's other columns}]}: the project's implementations, from Settings:Implementations, else its d2-impl topicsGET /api/v2/system/deployment ops{…}: where and how this box runs (d2spec only)GET /api/v2/codes?feature= designbehaviour codes: each spec line's code against design, implementation, tests and the sources, and the broken references (d2spec); HTML twin /features (the Feature list). ?untested=1: only the codes nothing tests — no test line, no covers: comment — newest first, each with its line and section (^code-5)GET /api/v2/routes?feature=&q=&code=&entity=&action=&format=json declared E_NOT_FOUNDthe declared routes, one line a route: METHOD path {body} → reply · its codes · who · its action · what (text/markdown); no filter: the features with their route counts; feature= one feature, q= every word, code= the routes that answer it, entity= (topic) the routes of that entity's actions, action= (topic.publish) the routes of that action; format=json: {ok, total, common, data: rows}, each row with its action (GET /api/v2/routes/openapi.json declaredthe same routes as OpenAPI 3, for tools and outside developers (never part of a skill or a start)GET /api/v2/errors?feature=&code=&format=json declared E_NOT_FOUNDevery error code once: its HTTP status, the routes that return it and its line from the feature skill's Errors (meaning → fix); no filter: the codes; format=json: {ok, total, data: [{code, status, common?, routes, features, skill: [{feature, text}]}]}GET /api/v2/cmd/<entity> declared E_NOT_FOUNDan entity's actions (DesignDomain#dd-inventory): {ok, entity, actions: [{action, method, route, key?, routes}]}, route the one /cmd runs (key: its path parameter, when it has one), routes every route naming the action; an unknown entity is 404 E_NOT_FOUND listing the entitiesPOST /api/v2/cmd/<entity>/<action> {key?, markdown?} declared E_NOT_FOUND E_METHOD E_TOO_BIGrun the action's primary route as that route, with your own session or token (^dd-3): key fills its one path parameter (topic.publish {key: "Landing"} is POST /api/v2/drafts/Landing/publish), the other fields are its body (a GET's query), markdown a text/markdown body; the answer is the route's, status and body, and every check it makes applies; an unknown entity or action is 404 E_NOT_FOUND listing what exists; a route with two path parameters, or a key missing or extra, E_ARG naming the route to call insteadPOST /razadmin/copy/now ops E_BAD_JSON E_COPY E_FORBIDDEN E_GATE_UNKNOWN E_NOT_FOUND E_PRIMARY{confirm?, hammer?}: the Architect's session (any token E_SCOPE) on a box with D2_COPY_FROM (else E_NOT_FOUND): the admin agent's POST /admin/import pulls the source box's database and files over this one → its answer {ok, from, at, dbMB, cdnMB, took}, or 202 {started} when it outlasts 3 s (the app restarts). On a primary, confirm: primary, and the import still needs a restore order (409 E_PRIMARY). When this box's app is too old or down to answer the gate the admin agent answers 409 E_GATE_UNKNOWN, and hammer (the phrase, checked by the admin agent against D2_HAMMER_SHA256, never kept) lets it through: wrong E_FORBIDDEN, three wrong in an hour E_RATEGET /api/v2/views/Dashboard pages E_NOT_FOUNDa page's data in one request: the queries `App:GET /api/v2/admin/cache ops E_METHODthe server's caches: {ok, total, data}, each with what it holds, what it is keyed by and its size; the Architect, or an AI as ops.cache allowsPOST /razadmin/copy/primary ops E_BAD_JSON E_FORBIDDEN E_PRIMARY{confirm: primary}: the Architect's session (any token E_SCOPE): a copy becomes primary (d2Meta.copyOf.primary): mail, crons, processing and payment webhooks follow their settings again, the banner goes → {ok, copyOf}; a box that is no copy, or primary already, E_PRIMARYPOST /api/v2/views {view: {: " arg=value …" | {query, …args}}} pages E_PARSEthe same for a view built on the fly; registry: spec.codes, spec.features, design.sections, tests.cases, tests.coverage, pipeline, history, agents, approvals, topics (pagesFrom: 1 adds the Page:/App: names the project borrows through pages-from, each {name, from}; an arg a query doesn't take E_ARG), thoughtsPOST /api/v2/admin/cache/clear ops E_METHOD{scope?: all | permissions | topics | files | system | project:<name>} → {ok, scope, dropped, cleared, ms}; changes no data; an AI whose person isn't the Architect clears only project:<this one>POST /razadmin/copy/allow ops E_BAD_JSON E_FORBIDDEN{box, why, hammer?}: a restore order: one import onto this primary within 30 minutes → 201 {allow: {box, why, by, via, at, until}}. The Architect's monitor's own token, the Architect's session with the hammer phrase (checked against D2_HAMMER_SHA256, never kept), or the box's own admin token (ops carrying out the Architect's Allow one copy, from anywhere: via ops; box may be left out; a box with no D2_COPY_FROM E_FORBIDDEN, ^deploy-7); anyone else, a wrong phrase or a box with no hash E_FORBIDDEN; three wrong in an hour lock it for an hour (E_RATE); box must name this box (D2_BOX or its domain)GET /api/v2/revs pages E_FORBIDDENwhat moved, in one small read: each store's change counter → {pipeline, history, agents, board, chats, topics: {POST /razadmin/copy/gate ops E_BAD_JSON E_PRIMARY{daily?, by?}: the box's admin agent only, with its admin token (else E_AUTH), before an import: {ok, copy: true} on a copy, {ok, allowed} using up a restore order on a primary, else 409 E_PRIMARY (always for the daily pull)GET /api/v2/revs/stream pages E_FORBIDDENthe same, pushed: text/event-stream, `event: revs` with the revs on connect and whenever one moves (at most every 250 ms), a `:` keep-alive every 25 s; up to 8 open streams per viewer and project, the oldest closed past that. ?scope=system (the Architect and admins, else 403 E_SCOPE): one combined {GET /razadmin/deploy ops E_FORBIDDEN E_PAGE_MISSINGRaz Admin's 🚀 Deployment: base d2's signed Page:Deployment, the Architect's session only (a visitor logs in first); 503 E_PAGE_MISSING while it isn't publishedGET /api/v2/deploy ops E_FORBIDDENthe Architect's session only (any token E_FORBIDDEN): this project's deployment definition (CloudDeployment's newdeployment fence, P-1834; a project without one, its deployment fence) → {release: {version, commit, at}, topic, kind: newdeployment|deployment, fence, errors: [{line, message}], boxes: [{…its definition, live: {reachable, ok, version, deployedAt, disk, memory, heap, alerts, copy, daily}, drifts: [{what, fix?, kind}], envState: [{key, kind: set|secret, state: ok|differs|missing|unknown, settable?}]}]} (live.envPending {count, keys}: env changes on the box waiting for a restart, P-1833) (a secret drift, kind secret: the box's envHashes against your vault's; a setting drift, kind setting: a set line's value against the box's hash; kind heap: no D2_HEAP_MB on the box; names only, never a value); live from each box's admin agent (its token opened from your vault by name) and its health, 5 s each; no token or secret valueGET /api/v2/deploy/<box>/actions ops E_FORBIDDEN E_NOT_FOUNDthe actions on one box, described by the server: {actions: [{id, icon, label, detail?, enabled, why?, lead?, needs: [{ok: true|false|"hammer", text}], force?, fields: [{name, label?, kind, options?, hint?, placeholder?}], steps, go}]}: deploy, copy, daily, restart, rollback, primary, secrets (🔐 Sync secrets: each env KEY vault:POST /api/v2/deploy/<box>/<action> ops E_FORBIDDEN E_HAMMER E_NOT_FOUND E_STATE{fields…} (apply, P-1833: {confirm?}; its run names the keys the agent refuses as for ops: <KEYS>, after giving the agent the definition's keys (POST /admin/definition), then writes the definition's Caddyfile when the box's differs, through POST /admin/caddy, else for ops, P-1849/P-1851; setenv/unsetenv write the box's env without restarting it): every need re-checked (E_STATE with why), a confirm that isn't typed E_ARG, the hammer phrase checked here for a copy onto a box that can't show it's a copy (none E_HAMMER, wrong E_FORBIDDEN, three wrong E_RATE for an hour), a setenv/unsetenv field that won't do E_ARG naming the field (a key the box doesn't take, a secret-looking key as a setting, D2_ADMIN_TOKEN/D2_TOKEN/D2_MONGO_URL removed), then the box's admin agent is called → 202 {run}; audited A_DEPLOY without field valuesGET /api/v2/deploy/runs/<id> ops E_FORBIDDEN E_NOT_FOUNDa run's progress, readable while it runs: {lines, done, ok, error?, box, action, by, at}GET /api/v2/deploy/checks ops E_FORBIDDEN E_NOT_FOUND?runbook=&chore=<title>&box=: a runbook chore's checks, run by the server, each within 5 s → {checks: [{line, ok, detail}]}: dns resolves the name's A records (the ip among them), vault: your vault holds the names (names only, never a value), health: the box's admin status (an unreachable box ok:false), heap: the box's env carries D2_HEAP_MB; box only when two chores share the title (else E_ARG)POST /api/v2/deploy/chores ops E_FORBIDDEN E_NOT_FOUND{runbook, box, title, done}: a chore done (its thought ticked ✅ and archived) or open again → {thought: {name, project, done}}; GET /api/v2/deploy lists the chores ({chores: [{runbook, line, box, title, codes, steps, thought}], choreErrors: {<runbook>: [{line, message}]}}; a copy step naming {vault:<name>} carries refs and vault: [{name, kind: secret|key|waiting|none}], never a value: POST /api/v2/vault/resolve fills it in), each the Architect's thought tagged chore and #<box>, made once; an action a chore names (^<action>) is enabled:false while it is openGET /api/v2/deploy/<box>/preview ops E_FORBIDDEN E_NOT_FOUNDread only (P-1834): what CloudDeployment's newdeployment fence would write on the box against its files read now from its admin agent's GET /admin/files (never kept) → {box, definition: {ok, errors}, readAt | readError, files: [{path: /etc/d2/env | /etc/caddy/Caddyfile, new, current | null, diff: [[" "|+|-, line, note?]] | null, script: text | null, error?}]}; script (P-1833): the script ops runs as root on the box, {vault:<name>} for each secret (POST /api/v2/vault/resolve fills it on 📋 Copy), the env's changed lines through d2-setenv on stdin and each drop line's key deleted, the Caddyfile through tee and caddy validate, each after a dated backup and put back on failure; new is the box's ```caddyfile <box> fence as it is when CloudDeployment has one (P-1851), else built from its host, alias and redirect lines; a secret is {vault:<name>} in new and {hash, vault?} in current, the same when the box's hash is the vault's; a missing vault entry is a note on its line, never a change; a key the definition doesn't name is kept unless a drop line names it; an unreachable box or an agent without /admin/files: readError, current null, no diff; nothing writtenPOST /api/v2/deploy/definition ops E_FORBIDDEN E_NOT_FOUND E_STATEP-1833: {box?, key, value: text | null} → {was: text | null, changed, ver}: one set line of CloudDeployment's newdeployment fence (the box's when box is given, else the deployment's; value null removes it), written as the Architect; restarts nothing, writes no box; was is the old value, which the page's Discard writes back; a secret-looking key or value E_ARG (a secret line instead); audited A_DEPLOY with the key, never the value. P-1851: {box, caddyfile: text | null} → {was, changed, ver}: the box's ```caddyfile <box> fence (made in ## Caddyfiles {#caddyfiles} when it has none; null removes it), the text as it is; a host or alias line of the box with no site block in it E_ARG naming it; a line starting ``` E_ARGPOST /api/v2/deploy/secrets ops E_EXISTS E_FORBIDDEN E_NOT_FOUND E_QUOTA E_STATEP-1833 ➕ Create: {key, vault, box?, phrase?, value?} → {vault, line, allowed: [role], skipped: [{role, why}], publicKey?}: a vault entry the definition names that your vault lacks, made in your vault (D2_HAMMER_SHA256: sha256 of phrase, never the phrase; AWS_* and D2_TURNSTILE_SECRET: value; ssh: an ed25519 key pair, the private half a vault key, publicKey answered once; D2_ENC_SECRET: only a pasted value, a fresh one E_ARG; anything else 32 random bytes, never answered); its secret (or ssh key) line written when the definition lacks one (for box), and the entry allowed to the line's roles' live role tokens; already in your vault E_EXISTS; audited A_DEPLOY, names onlyGET /api/v2/topics topicslist: {total, category, data}GET /api/v2/editor/frontmatter topicswhat the editor's content assist suggests inside the frontmatter and after show= (^edit-assist-1): {total, from, data: [{key, where, values, source, help}]}. base d2's Settings:Frontmatter with this project's own merged over it (the same key replaces), and the value sources ($tags, $topics, $classes, $members) filled from the project. It only suggests: nothing here validates a key or a valueGET /api/v2/nav pagesthe top bar this viewer gets here, from the project's Nav topic, else base d2's: {from: project|d2|built-in, logo, parts, warnings, bad?}, links already filtered by show= and level=; ?path= for another page's (an admin page always gets base d2's)GET /api/v2/topics/<name> topics E_ENC_KEY E_NOT_FOUND E_NO_SECTION E_PRIVATEmarkdown body; ?format=json for the envelope; ?section=<id> for one section (&format=json: {name, section, text, from?}); a pointer section (its body only `→ [[T#a]]`) answers the section it points to, once, as you would read T, with X-Section-From: <name>#<id> (a pointer to a pointer or to nothing answers the pointer); ?stamp=1 for {stamp, ver, draft} (dev-sync); a base d2 Page: (its own, or inherited) is for a signed-in account: a visitor gets 401 E_PRIVATE, as on its history and drafts (^d2land-7)GET /api/v2/topics/stamps?names=a,b topics{stamps: {<name>: stamp}}: each topic's stamp in one request (at most 200; the ones you can't see are left out), for dev-sync over several topicsPUT /api/v2/topics/<name> topics E_AI_ACCESS E_APPEND_ONLY E_BAD E_BAD_JSON E_CODE_REUSED E_CONTENT E_DEMIGOD E_DRAFT_CHANGED E_ENC E_ENC_CHANGED E_ENC_KEY E_EXISTS E_GENERATED E_LINK E_LOCKED E_METHOD E_NATIVE E_NOT_FOUND E_NO_SECTION E_ON_BEHALF E_PARSE E_PENDING_DELETE E_QUOTA E_SECRET E_SETTING E_STEP_JUMP E_STEP_VAR E_THOUGHT E_THOUGHT_STATE E_UNCONFIRMED E_VERSION_REQUIREDbody = markdown; stored even if broken, parse errors returned; ?section=<id> writes one section, to the person's draft for a token that writes drafts, and ?section=<new>&after=<id> or &at=end adds one (E_NO_SECTION without either); a stored Thought: keeps its frontmatter (none: 400 E_THOUGHT), and an AI token's save of one is PUT /api/v2/thoughts/<n>'s (?state=, ?waitingFor=, ?ver=; E_APPEND_ONLY, E_THOUGHT_STATE); a body in another language than the topic's (?language=markdown|json|html, frontmatter language:, an App) is 400 E_CONTENT; an agent's write to another role's topic (the design layer, the design tests) or a behaviour tag is 403 E_SCOPE per the project's topic.publishDesign, topic.publishTests, topic.tag (details {topic, category, role, allowed}); a changed ai-access line from an AI 403 E_AI_ACCESS {from, to}; an added tag that isn't a flat lower-case word, or a rule sending a ….on.… event, 400 E_PARSE {line, got}; a signed CDN link 400 E_LINK {line, link}; a story's {x} that no header declares and no step sets is in its errors as E_STEP_VAR {line}, a jump to a story or step that isn't there E_STEP_JUMP {line} (stored all the same); a retired or doubled behaviour code 409 E_CODE_REUSED {code, line, was}; a guardian's second open report 409 E_EXISTS {topic, state}; hints (N_CHANGELOG, N_TEMP_LINK, N_NO_SPECUI, N_NO_HELP, N_BIG_TOPIC, N_DEFAULT_ACCESS, N_SPEC_WHO) as warnings [{code, line}], never a refusalPOST /api/v2/topics/<name>/append {text, date?} topics E_AI_ACCESS E_BAD E_BAD_JSON E_CONFLICT E_ENC E_ENC_CHANGED E_ENC_KEY E_FORBIDDEN E_GENERATED E_LINK E_NOT_FOUND E_PENDING_DELETE E_SECRET{text, date?} or the text itself: added under the day's ## YYYY-MM-DD heading (made if missing), at the end of that day's entries → {name, ver, date}; the one write an append-only topic takes from everyone but its admins (Design 22)POST /api/v2/topics/<name>/unseal {id} topics E_ENC_KEY E_FORBIDDEN E_NOT_FOUND{id}: one encrypted block's text, for the page's box → {name, id, text}; its writer only, signed in as themselves (an AI token, or anyone else, E_SCOPE); every opening audited A_ENC_OPEN, never the text (Design 64)DELETE /api/v2/topics/<name> topics E_AI_ACCESS E_DRAFTS E_GENERATED E_NOT_FOUND E_PROTECTEDto the trash, with its history, for 30 days; Skill:process never (403 E_PROTECTED, for everyone: undo a bad edit from its history, ^addon-12); an agent whose role is person on topic.delete: 403 E_SCOPE (tag it todelete instead)POST /api/v2/topics/batch {names, op, tag?} topics E_BAD_JSONthe Docs List's picks (^docs-7): op tag or untag (with tag, one word) or delete (to the trash), on at most 200 names → {done, skipped: [{name, code}], unchanged}. Each name gets its own route's checks (PUT or DELETE) and is skipped with that code (E_FORBIDDEN, E_NOT_FOUND, E_SCOPE, E_DRAFTS…; a delete of Skill:process E_PROTECTED, ^addon-12); one new version per topic changed; one that already has the tag, or lacks it, is left aloneGET /api/v2/topics?category=<Cat>&tag=<tag> topicsfiltered listGET /api/v2/topics/<name>/history topics E_NOT_FOUND E_PRIVATE{name, ver, data: versions}GET /api/v2/topics/<name>/history/<ver> topics E_NOT_FOUND E_PRIVATEthat version's text; ?format=json for {name, ver, text}POST /api/v2/topics/<name>/history/purge topics E_BAD_JSON E_NOT_FOUNDthe one exception to nothing is lost (^his-9): removes versions ≤ upTo for good (and from a trashed copy of that name), the current text stays, the oldest kept version rebuilds on its own → {topic, purged: [vers], from: <oldest kept>}; the project's owner in their own session only (any token, a demigod's included, an admin, the Architect on another's project: 403 E_SCOPE); audited A_HISTORY_PURGE {topic, versions, by}, never any text; again over the same range: 200, purged: []POST /api/v2/topics/<name>/restore?ver=<ver> topics E_METHOD E_NATIVE E_NOT_FOUND E_PENDING_DELETE E_QUOTAa new version with the old text (a draft, with a drafts token)GET /api/v2/search?q= srchtopics by text; #tag words filter by tag, and bring tagged files; the project's own rows first, then what it inherits, each with from: <base>GET /lib/<name>.js?v=<hash> pages E_NOT_FOUNDbase d2's published Lib:<name> as text/javascript, for d2's signed pages only (the page asking, its same-origin Referer, is a base Page served signed), else 403 E_SCOPE; v= its current hash, as a signed page names it: public, max-age=604800; else no-cache with an ETag (304 unchanged); no published Lib:<name>: 404 E_NOT_FOUND (^blocks-6)GET /api/v2/search?q=&scope=mine srchevery project you are a member of, one flat list by match (^srch-5): {total, projects, hits: [{project, chip, kind, name, url, snippet, count, score}], pending}. `all:` before the query says the same; &project= narrows it to one of them (and is how the page picks up a pending project); each project under its own view rules; a token for one project is E_SCOPEGET /api/v2/drafts drfmy drafts: {total, data} with published version and conflictGET /api/v2/drafts/stamp drf{stamp, total} over my drafts (saved, published, dropped) and who else drafts those topics: the Drafts page syncs on itGET|PUT|DELETE /api/v2/drafts/<topic> drf E_AI_ACCESS E_APPEND_ONLY E_BAD E_CODE_REUSED E_CONTENT E_DRAFT_CHANGED E_ENC E_ENC_CHANGED E_ENC_KEY E_GENERATED E_LINK E_METHOD E_NOT_FOUND E_NO_SECTION E_ON_BEHALF E_PARSE E_PENDING_DELETE E_PRIVATE E_SECRET E_SETTING E_THOUGHT E_VERSION_REQUIREDone draft: its raw markdown (?format=json or Accept: application/json for the record), save, drop; a save sent as JSON (Content-Type: application/json, as a Page's d2.api sends it) is {text, ver?, section?}, the same save as text with ?ver= and ?section= (text required, else 400 E_ARG; an App's or Page's own JSON body that isn't that envelope is taken as it is); ?section=<id> one section of it (&after=<id> or &at=end adds a new one), a pointer section followed once as on the topic (your draft of the target, else the published one; X-Section-From, JSON from); a save runs the topic PUT's role fences and content checks (E_SCOPE, E_AI_ACCESS, E_PARSE, E_LINK, E_CODE_REUSED) and carries its hints as warnings; no draft yet of a page only the Architect sees, or of a topic whose only hidden parts are his (show=), read by his own AI with askedBy: the published text, ETag none, JSON {name, text, dver: null, fromPublished} (markdown: X-D2-From-Published) (^adm-9, ^show-11). Base d2's Settings:System, Quotas and Permissions, by an AI: read and drafted only by the Architect's own AI on his clear intent topic.update naming it (D2-Intent) or his Allow once for that save (D2-Override, and the reads it needs), else 401 E_AUTH (^dd-31, P-1815)POST /api/v2/drafts/<topic>/publish drf E_CONFLICT E_DEMIGOD E_ENC E_ENC_CHANGED E_ENC_KEY E_LOCKED E_NATIVE E_NOT_FOUND E_PENDING_DELETE E_QUOTA E_SECRET E_SETTING E_UNCONFIRMEDpublish it; 409 if the topic changed since (?force=1); an AI sends {askedBy} when its person said so. A god page (god-only, or every kept part show=god) by the Architect's AI on his clear intent naming it (D2-Intent) or his Allow once (D2-Override), ^dd-31, ^dd-32; base d2's Settings:System, Quotas and Permissions by an AI never: 401 E_AUTH (he publishes them after the diff)GET /api/v2/backlog pipeevery to-do (checkbox line) of every topic: topic, line, text, status, section; yours from your drafts (?topic=)POST /api/v2/backlog/status pipe E_CONFLICT E_FORBIDDEN E_NOT_FOUND{topic, line, raw, status}: set a to-do's status (todo, next, doing, waiting, review, done); always a draft; 409 if the line changedPOST /api/v2/drafts/publish drf E_CONFLICT E_PENDING_DELETE E_QUOTApublish all of mine, or {topics: [names]} only those; an AI sends askedBy when its person said so; each draft asks what its own publish asks (the Architect's own pages and parts are never an AI's to publish: E_SCOPE in its result)GET /api/v2/approvals apprthe open approvals waiting on you (an AI token: on its person) in this project, on every level — a Hero project has no pipeline, so its held asks are reached here and on NoticesGET /api/v2/approvals/ appr E_NOT_FOUNDone approval notice (A-n), open or answered: its request, answer and replay — a notice has no pipeline pagePOST /api/v2/approvals//approve|refuse appr E_FAILED E_IN_PROGRESS E_ITEM_CHANGED E_NOT_FOUND E_TOKENthe person answers one (never an AI: E_SCOPE); approve runs the held action as the AI, as the pipeline's answer doesPOST /api/v2/pipeline/answer {approve: small} apprApprove all small: a person approves every approval for them whose item is small, each run as its own answer would → {answered: [{id, ran?}]}; an AI token E_SCOPEGET /api/v2/pipeline pipe E_FORBIDDEN E_LEVEL E_METHODcoders: pipeline.coders from Settings:Project, beside pick, and a coder pick at open with no ?cap= takes it as the cap. ?needs=me (P-1697): every open item waiting on your answer, held by you, or filed for you (your lane named for you, or named for you by choice in a person's lane); another value is E_ARG. the items: {total, roles, statuses, data}; ?limit=n (1 to 1000): only the newest n by last change, newest first, total still counting all (E_ARG otherwise); ?status= (comma list) ?for=<role> ?name= ?kind= ?feature= ?source= ?open=1 ?size=small|medium|large; ?mine=1 (or ?who=mine): what waits on you yourself — items in your own lane, waiting on your input, your review or to-look-at, approvals for you; the work your agents do in your name is not in it; ?pickable=1: what may be taken, in pick order, and for a role with a batch policy (coders) the batch block: {on, ids, items (rows with why), out: [{id, why}]} — what one agent takes together by the policy table (eight small, one medium, a large alone), leaving out alone items, handovers and what collides with work in progress or with each other; ?pickFor=<agent>: the same list, re-ordered so what follows up that agent's own work comes first (after a released item and a handover) — a re-ordering, never a filter, and without it the list is what it always was; ?free=1: the same minus what collides with an item in progress (a row left out carries collides, and collideFiles: the files at stake — only files collide, never topic names; a row whose overlap can't be known stays in and carries mayCollide). Every answer carries pick: {coder, designer: {mode: stop|1|5|open, count, rushed, ready}} and release.mode (demand|now|alldone|auto, with modeBy, modeAt, toGo at alldone) beside release.auto, derived from it (^pipe-90, ^pipe-91); at 5 an AI's ?pickable=1 for that role is empty until ready; at open it is served whenever anything is pickable, the batch capped at ?cap=n (1 to 8: the dispatcher's coderCap; else 8); batch.on says the role picks in batches, its items capped at the mode; an agent's pick never lists a peer review it wrote (^pipe-review-1). ?fields=a,b (P-345): just those, id always; held ({by, reason, since}) and notLive (true) as stored; whyNotPickable only when named, never in the default view or all: a non-pickable item's plain reasons, first first (waits on P-12 (in progress)), left out for a pickable one and for a tester alone (P-1902)GET /api/v2/pipeline/stamp pipe E_FORBIDDEN E_LEVEL{stamp, total, processing}: the list's stamp under the list's own filters, with the board's, so the page and the pipe map sync on itGET /api/v2/pipeline/totals pipe E_FORBIDDEN{projects, items, byStatus, history}: the items counted over every project; the Architect only (a demigod token E_SCOPE)GET /api/v2/pipeline/pickable?summary=1 pipea summary of what may be taken, for a monitor token as well as for anyone who may list the pipeline (P-519): {role, count, bySize, ids: [{id, size, kind}], handover, alone: {first?, running?}, paused, pick, ready, rushed, release: {open?: {id, status, at, taken}, auto, mode}} — release there while processing is stopped too; pick (coder and designer only): the role's pick mode, stop | 1 | 5 | 8; ready: a pick would hand the role something now (at 1 any pickable item, at 5 or 8 that many or a rushed one, a handover or an item given back); rushed: how many are rushed; release.mode: demand | now | alldone | auto, auto derived from it (^pipe-90, ^pipe-91). ?role= (coder by default). Counts and ids only — no titles, no documents; the items themselves are GET /api/v2/pipeline?pickable=1, which a monitor token may not readGET /api/v2/pipeline/map pipe E_FORBIDDEN E_LEVELthe pipe map's data (P-1592): the MapData the built-in page draws its header map from, for Page:Pipeline's expanded agent view; drawn with /assets/pipe-map.js's d2PipeMap(el, data). mode=agents (^agents-6): the Agents map Page:Agents draws — the same, view complex, mode agents, every role that reports (roles, list with what each live agent holds, recent), canEnd, waits — for a member's session, their AI token with write access or the ops token (a read-only token, a non-member: E_FORBIDDEN; signed out: E_AUTH; below pro: E_LEVEL); any other mode is E_ARGPOST /api/v2/pipeline pipe E_BAD_JSON E_EXISTS E_FORBIDDEN E_LEVEL E_NOT_FOUND E_TICKET E_TICKET_WARNmake an item (P-12); an AI parks an idea this way. review: none (default) | true (its own lane) | a role: for peer review (^pipe-review-1), its filer its first author. doc is required, "" for none (400 E_ARG naming it otherwise, ^err-4); a prompt instead starts the document from it. alone: true runs it by itself on the coder side (a refactor; an AI sets it only with askedBy). summary: the item's one sentence, at most 200 characters (E_ARG over that). An AI's kind approval is 400 E_ARG {details: {item: null, field: kind, got}} (approvals are notices); an AI's status waiting, waiting-input or waiting-error needs a one-line note (400 E_ARG {details: {item, field: note, got}}). A coder ticket, where pipeline.ticketCheck is on, also meets the wider check when filed (P-1631): title ≤60 characters with no kind prefix, id, date or version, the doc opening with **Look at:**, every code in codes with a test line in SpecTest or Testing — else 400 E_TICKET {missing, details: {parts: [{part, problem}]}}; no Journeys: line, ## Scenarios or touches is warnings [{code: E_TICKET_WARN, parts}] on the 201. A call held for approval while the same call's approval A-n is open: 409 E_EXISTS {details: {approval, askedAt}}GET /api/v2/pipeline/<id> pipe E_ARCHIVED E_NOT_FOUNDone item's document, raw markdown; ?format=json (or Accept: application/json) for the item with its chain and its history linesPATCH /api/v2/pipeline/<id> pipe E_ARCHIVED E_BAD_JSON E_COMMIT_ITEM E_CONFLICT E_EXISTS E_INTENT E_IN_PROGRESS E_NOT_FOUND E_NOT_PUSHED E_NOT_YOURS E_NO_COMMIT E_NO_TOUCHES E_PINGPONG E_REPO_UNREACHABLE E_REVIEW_MERGE E_REVIEW_ROLE E_SELF_REVIEW E_STATE E_STATUS E_TICKET E_TICKET_WARNan agent's {forRole: coder agent} (forName null, a note, nothing else) passes the lane check on its person's live item.send intent naming the item (D2-Intent; an ended one 409 E_INTENT), P-1738; status waiting-input (P-1697) needs waitingOn, the person whose answer it waits for, and a doc ending with a ## Questions block (pad lines) or the answer marker, or a held coder ticket (kind impl, designer lane, for that person): else E_ARG {field: doc, want}; {waitingOn: null} on a waiting-input item puts it back with its holder, or new; a person's in-progress on an item an agent holds is E_IN_PROGRESS {holder} (answer it with /answer {text}). {status?, review? (none | true | a role: peer review, ^pipe-review-1 — set by an author (its filer, a taker) or its person until it is finished, then only its person; an agent never changes or clears one once set, but with askedBy; E_SCOPE, E_STATUS. An author's done puts it in review for that role's agents, held by nobody — a code item's close with a commit already on pipeline.repo's branch= is E_REVIEW_MERGE, unless it was reviewed already (review.by): then it is built again, waiting-release; the reviewer's done (an agent of that role that wrote none of it: E_SELF_REVIEW, E_REVIEW_ROLE) or its person's accepts it: done, or waiting-release for code, review.by the reviewer), forRole?, forName?, title?, summary?, doc?, note? (alone: allowed on an item someone else holds), stage? (a release's, from its holder: deploying | live {version}: its items ship | gate-failed {gate, reason, owner: designer|coder|POST /api/v2/pipeline/<id>/finish pipe E_ARCHIVED E_BAD_JSON E_COMMIT_ITEM E_CONFLICT E_EXISTS E_IN_PROGRESS E_NOT_FOUND E_NOT_PUSHED E_NOT_YOURS E_NO_COMMIT E_NO_TOUCHES E_PINGPONG E_REPO_UNREACHABLE E_REVIEW_MERGE E_REVIEW_ROLE E_SELF_REVIEW E_STATE E_STATUS E_TICKET E_TICKET_WARN{note?, commit?, touches?, docs?, as?, name?, ver?, askedBy?}: finish it, the same close as PATCH {status: "done", …} with these fields (any other field is left out): a code item closed with its commit answers waiting-release; your role's pipeline.finish may send it to reviewGET /api/v2/pipeline/<id>?format=topic pipe E_NOT_FOUNDthe item as a topic: a front matter of the fields that are edited (title, kind, for, size, important, rank, feature, touches, waitsOn, links) over its document. Its id, status, source, taker and dates are not in it (^pipe-58)PUT /api/v2/pipeline/<id> pipe E_COMMIT_ITEM E_CONFLICT E_IN_PROGRESS E_NOT_FOUND E_NOT_PUSHED E_NOT_YOURS E_NO_COMMIT E_NO_TOUCHES E_REPO_UNREACHABLE E_STATUSthat same text back, Content-Type: text/markdown: the PATCH underneath, so every field check holds. A bad line is E_ARG naming the line and nothing is saved; status never changes this way (^pipe-58)POST /api/v2/pipeline/<id>/take pipe E_ALONE_BUSY E_ALONE_NEXT E_ALONE_RUNNING E_CHATTING E_EXISTS E_FAILED E_HELD E_IN_PROGRESS E_NOT_FOUND E_ORDER E_PASS_FULL E_PAUSED E_REVIEW_ROLE E_SELF_REVIEW E_STOPPED E_STOP_PENDING E_TAKEN E_TICKET E_TICKET_WARN{name?, ver?, waitingOn?: "self", askedBy?}: take it (in-progress, or waiting on itself); an AI's take of a coder ticket (kind impl) in the designer lane another agent filed for its person (forName) is 409 E_HELD {forName, by} until the person sends it to the lane without a name or names that agent, and an AI's pick never lists it (P-1766); a peer review waiting on its reviewer is taken only by an agent of its review role (E_REVIEW_ROLE) that is none of its authors, by the name it takes under or its token's agent (E_SELF_REVIEW, naming them; ^pipe-review-1); while processing is stopped an AI's take of coder work is E_STOPPED unless it carries askedBy: its own person — a release item and other roles' items are taken as usual (^pipe-69: processing stopped: no new coder work; releases go on); E_TAKEN if someone did first; E_ALONE_NEXT / E_ALONE_BUSY / E_ALONE_RUNNING: an alone item is first or running on the coder side; a take always succeeds and carries collides / mayCollide: the items in progress it overlaps, or mayPOST /api/v2/pipeline/<id>/putdown pipe E_IN_PROGRESS E_NOT_FOUND E_NOT_YOURSany dispatcher agent may also put down, never close, an item whose holder was declared dead (^pipe-93); a live holder's stays E_NOT_YOURS. {note?}: put it down: its taker (or its person) releases it, in-progress → new with no taker, so it can be parked; another agent E_NOT_YOURS, another member E_IN_PROGRESSPOST /api/v2/pipeline/<id>/fail pipe E_NOT_FOUND E_NOT_YOURS{why}: its holder gives it back as failed on the item's own content; the first agent's releases it to the role (new), a second different agent's makes it status failed, back to its asker and waiting on them, and take is 409 E_FAILED until they release it (^pipe-73). Deaths (gone, stopped) are kept apart in deaths and never countPOST /api/v2/pipeline/<id>/pause pipe E_NOT_FOUND E_STATUS{askedBy?}: hold it: a new or pending item goes to paused, keeping its rank, and comes back to the status it had; E_STATUS on anything else (in progress, waiting, review or parked). A person's call (^pipe-57)POST /api/v2/pipeline/<id>/resume pipe E_NOT_FOUND E_STATUS{askedBy?}: back to the status it was paused from; E_STATUS if it isn't paused (^pipe-57)POST /api/v2/pipeline/<id>/run pipe E_NOT_FOUND E_STATUS{all?, askedBy?}: ▶ Run, the paused row's one tap — the item back to the status it was paused from. With all: that item and the paused items that depend on it, transitively (follows or waitsOn), except one that also waits on something outside the chain that isn't closed. Answers {resumed: [ids], left: [{id, waitsOn}]}; E_STATUS if it isn't paused (^pipe-57, P-516)POST /api/v2/pipeline/pause-all | /resume-all pipe E_LEVEL{role?, forName?, askedBy?}: the list's own filter: pauses every new and pending item it shows, or resumes every paused one, and answers the ids moved. Parked items are outside it at both ends (^pipe-57)POST /api/v2/pipeline/take pipe E_ALONE_BUSY E_ALONE_NEXT E_ALONE_RUNNING E_CHATTING E_FAILED E_HELD E_LEVEL E_NOT_FOUND E_ORDER E_PASS_FULL E_PAUSED E_REVIEW_ROLE E_SELF_REVIEW E_STOPPED E_STOP_PENDING E_TAKEN{items: [ids], as, name, askedBy?}: takes them all or none (askedBy passes a stopped pipeline, ^pipe-69), each as POST /pipeline/<id>/take would → 200 {taken, items, collides?, mayCollide?} (the last two by item). The first that can't be taken refuses the whole, named (409 E_TAKEN, 403 E_SCOPE, 404, E_ALONE_*); a gate that would ask your person is a refusal here: take that one on its own. The ids to send are GET /api/v2/pipeline?role=<r>&pickable=1's batch.ids (^pipe-40)POST /api/v2/pipeline/<id>/rush pipe E_NOT_FOUND E_NO_KEY E_STATUS{on?, askedBy?}: a person wants it next — it goes first in its role's pick order, ahead of important ones; on: false unrushes it, and d2 clears it itself when the item is taken. Only a new or pending item (else E_STATUS). pipeline.rush, asks in every preset, so an AI token needs askedBy (E_SCOPE without one). Answers {rushed, changed, told: {to, how}}: `rush <item>` goes to role:dispatcher on the person's board, and with no live dispatcher for that role d2 asks their monitor to start one (^pipe-68, P-576)POST /api/v2/pipeline/stop | /start pipe E_EXISTS E_FORBIDDEN E_STATUS E_STOPPED{roles?, note?} on stop, {roles?} on start: per role (P-1139) — roles is a list of coder, designer; left out, stop holds coders (the old meaning) and start starts every stopped role; another role is E_ARG, stopping a stopped role E_EXISTS, starting a running one E_STATUS. Stored as processing {stopped, roles, by, at, note, each: {role: {by, at, note}}}; a record without roles reads as coders. A designer stop holds the designer lane's AI takes only; handovers and releases are never held. The switch for the project's pipeline — processing stopped: no new POST /api/v2/pipeline/release pipe E_EXISTS E_FORBIDDEN E_RELEASE_OFF{note?}: an agent with auto-release off and no askedBy: 409 E_RELEASE_OFF (^pipe-77). Files the release item for the dispatchers (kind release, forRole dispatcher, important, small, touches nothing; title Release: full suite, razwip → main, deploy, box suite; its document lists the items done with commits since the last release, and the note) → 201 {item}. Stop processing's gate: a moderator, an admin or the Architect in their own session; any token 403 E_SCOPE, a member 403 E_FORBIDDEN. One open at a time for an AI: 409 E_EXISTS {item}; a person's ask while one is open (taken included) drops it (note: replaced by P-m: asked again by PUT /api/v2/pipeline/pick pipe E_FORBIDDENa working role's pick mode (^pipe-90, P-1592): role coder | designer, mode stop | 1 | 5 | open (another, 8 included, is E_ARG; a kept 8 reads open). stop is the role's stop (as POST /pipeline/stop {roles: [role], note}: who, when, note; its board notice; already stopped, unchanged); any other mode starts a stopped role and keeps the number. At 1 an AI's pick hands the next item; at 5 it hands nothing until 5 are pickable for the role, then them together (the batch block capped at 5); at open it takes at fill: ready whenever anything is pickable, the block everything pickable up to the pick's ?cap= (the dispatcher's coderCap), round after round until empty or stopped; a rushed item, a handover or an item given back goes at once with what can go along; no timer. A person's read is never narrowed. Stop processing's gate (^pipe-92): a moderator, an admin or the Architect in their own session; any token 403 E_SCOPE, a member 403 E_FORBIDDEN → 200 {role, pick: {mode, count, rushed, ready}, processing, told?}PUT /api/v2/pipeline/release-mode pipe E_FORBIDDENthe release mode (^pipe-91, P-1592): demand (the default: a person's ↑ Release) | now (the release is filed once the coder items in progress at the press are done, at once when none is, carrying what is finished then; the roles keep picking) | alldone (filed once nothing is pickable or in progress for coders, no coder agent is working, merging or stuck, and an item waits for a release) | auto (the dispatchers release as they judge; release.auto). d2 files the release as the person who set the mode (askedBy and forName), with the board message a press sends; once it ends (done, live, failed or dropped) the mode is demand again. Stop processing's gate (^pipe-92): any token 403 E_SCOPE, a member 403 E_FORBIDDEN → 200 {release: {mode, modeBy, modeAt, toGo?, filed?, auto}, filed?}POST /api/v2/pipeline/<id>/accept pipe E_COMMIT_ITEM E_NOT_FOUND E_NOT_PUSHED E_NOT_YOURS E_NO_COMMIT E_NO_TOUCHES E_REPO_UNREACHABLE E_REVIEW_ROLE E_SELF_REVIEW E_STATUS{comment?, note?, commit?}: a peer review (^pipe-review-1): its reviewer (an agent of its review role that wrote none of it, holding it once taken) or its person accepts it — done, or waiting-release for a code item with its commits, review.by the reviewer; else its person accepts a review (done); with a comment it goes back to whoever finished it, new, acceptedIf: their next finish closes it (a note says how it was resolved), or they return it with questionsPOST /api/v2/pipeline/<id>/seen pipe E_NOT_FOUND✓ Seen: its person (or their AI, as pipeline.review allows) takes a done item off the to-look-at list; E_ARG if it isn't on itPOST /api/v2/pipeline/<id>/unseen pipe E_NOT_FOUNDundoes Seen: a done item back on its person's to-look-at list; that person only, never an AI (E_SCOPE)POST /api/v2/pipeline/<id>/start pipe E_CLOSED E_NOT_FOUNDStart on a daily digest: its person, in their own session (any token E_SCOPE), sends the plan's command to their monitor, or with no monitor marks the plan's items important; the digest is done → {started, plan, item}. E_ARG on any other kind, E_CLOSED once started (^pipe-60)POST /api/v2/pipeline/<id>/sendback pipe E_NOT_FOUND E_NOT_YOURS E_REVIEW_ROLE E_SELF_REVIEW{note}: a peer review (^pipe-review-1), by its reviewer or its person: in progress for its last author again, still for review, review.back {by, at, note} and a back entry in that agent's next; else back to whoever finished it, reviewAgain: its next finish always comes back to review; E_ARG without a notePOST /api/v2/pipeline/<id>/return pipe E_CLOSED E_IN_PROGRESS E_NOT_FOUND E_PINGPONG{note, askedBy?}: back to whoever sent it; an item accepted if or sent back with a comment goes to its reviewer's review with your questions insteadPOST /api/v2/pipeline/<id>/ask pipe E_IN_PROGRESS E_NOT_FOUND{to?, question!}: ask up the chain (coder, tester, guardian → designer; designer → person): a question item (links.asks), this one waiting on it until it's closed with the answerPOST /api/v2/pipeline/<id>/handoff pipe E_CLOSED E_COMMIT_ITEM E_IN_PROGRESS E_NOT_FOUND E_NOT_PUSHED E_NO_COMMIT E_NO_TOUCHES E_PINGPONG E_REPO_UNREACHABLE E_STATUS E_TICKET E_TICKET_WARN{forRole, forName?, title?, kind?, note?, touches?, supersedes?}: done, and the next item made, linked both ways; an agent's handoff of an item for review is E_STATUS: it finishes with done, so its reviewer sees it (^pipe-review-1)GET /api/v2/settings/search?q= srch E_NOT_FOUNDthe settings you may reach, as a tree page › tab › setting, each with its link (href#anchor) and who it's for (all, member, admin, god — the Architect); without q, the whole treeGET /api/v2/settings/model srch E_FORBIDDENSettings:Project as base d2's ProjectSettings class (Spec:SettingsModel) describes it: fields in the class's order, each {name, line, kind, options, help, label, locked, value, problem}; unknown (lines the class doesn't know, kept), problems, ver, and vars, the live switches paired with it (ProjectVars: autoRelease, batch, processing). HTML twin: /topics/Settings:Project?view=propsGET /api/v2/permissions perm E_FORBIDDENwhat an AI may do on its own here: each action's mode (person, asks, tells, free), where it comes from, whether the project customized d2's defaultPOST /api/v2/permissions/customize perm E_EXISTS E_FORBIDDENadmins: copy d2's default into the project to change itPUT /api/v2/permissions perm E_FORBIDDEN E_NOT_CUSTOM{preset?, modes?}: admins of a customized project, d2 included (else E_NOT_CUSTOM); locked actions are ignoredPUT /api/v2/permissions/presets perm E_FORBIDDEN E_NOT_FOUND{preset?, modes?, levels?}: the Architect, on d2 only: the presets every project starts from (Settings:Permissions); locked actions are ignoredDELETE /api/v2/permissions perm E_FORBIDDEN E_NOT_CUSTOMadmins: Reset defaults, back to d2'sPOST /api/v2/pipeline/<id>/drop pipe E_CLOSED E_NOT_FOUND{reason, askedBy?}: an AI names the person who askedPOST /api/v2/pipeline/<id>/restore pipe E_INTENT E_NOT_FOUND E_STATUS{note?}: bring back a dropped item (^dd-35): back to the status it had before the drop (new when that can't be again), restored in its history; its person's (who it is for, who asked for it, or the project's owner), in their session or by their agent on their intent or Allow once (pipeline.restore: person); E_STATUS on an item that isn't droppedPOST /api/v2/pipeline/<id>/reopen pipe E_INTENT E_NOT_FOUND E_STATUS{note?}: reopen a done item (^dd-35): back to new in its lane, reopened in its history; its person's, as restore (pipeline.restore: person); E_STATUS on an item that isn't donePOST /api/v2/pipeline/<id>/demote pipe E_CLOSED E_IN_PROGRESS E_LEVEL E_NOT_FOUND{topic?, section?}: a person demotes an item to a to-do (default ToDo:Project, section From the pipeline, made if missing); the item's status is todo (/todo answers until the next release, with a note)POST /api/v2/pipeline/<id>/todo pipe E_CLOSED E_IN_PROGRESS E_LEVEL E_NOT_FOUNDthe old name of /demote, answering the same with a note until the next releasePOST /api/v2/pipeline/<id>/note pipe E_IN_PROGRESS E_NOT_FOUND{text}: add to its document; the status stays. Anyone may add one to an item another agent holds: its taker is told (`told`); on a release whose gate failed it is the fix arriving and the gate re-runs (`rerun`)POST /api/v2/pipeline/<id>/move pipe E_CLOSED E_NOT_FOUND{project}: a person moves it to another of their projects; copied there, marked moved herePOST /api/v2/pipeline/promote pipe E_CONFLICT E_EXISTS E_LEVEL{topic, line, raw, to?, title?, forRole?, size?, prompt?, text?}: Create ticket, a to-do becomes an item (title, forRole, size and prompt over the line's); with to: "thought" (and text?) Create thought, a thought of the caller's own with a link back → {thought, link}; either way the to-do is ticked with its link, as a draft; a line already linking one is 409 E_EXISTS, a changed one 409 E_CONFLICTGET /api/v2/history his E_METHOD?type=&layer=&feature=&anchor=&item=&writtenIn=&topic=&person=&agent=&since=&until=&limit=&cursor=: the entries, newest first, paged (next is the cursor); type: one or a comma list; person=GET /api/v2/history/ his E_NOT_FOUNDone entry: {ok, entry}, or 404GET /api/v2/history/log his?limit= (default 5, at most 100): the Project log's rows (^prj-2), newest first — decisions, changes and each topic save (one topic's saves by one author in a day folded) — as the viewer sees the topics, never Thought: or Settings:: {ok, rows: [{at, kind: decision|change|save, text, topic?, ver?, by}]}, on every levelPOST /api/v2/history his E_FORBIDDEN E_PARSE{type: decision|change|ai|pipeline, text (markdown, may span lines), about?: {layer: spec|design|build|skill, anchors?}, person?, feature?, item?, writtenIn?, replaces?}: add one; at and author are d2's. A decision needs person, a decision or change needs about.layerPOST /api/v2/history/import his E_FORBIDDEN E_PARSE{entries: [...]}: mods, admins and the Architect; at (normalised to ISO UTC), author and source kept as given; 201 when anything was added; a line whose source is already in is skipped and countedGET /topics/Log:ProjectChanges histhe generated views, the last 7 days, read-only (a write is 409 naming POST /api/v2/history): Log:ProjectChanges (decisions and changes), Log:AiChanges (ai), Log:PipelineChanges (pipeline), Log:Performance (run); the old Log:Project, Log:Ai, Log:Pipeline 301 there; ?layer=spec|design|build|skill|all, ?feature=, ?item=, ?anchor=GET /history his?log=ProjectChanges|AiChanges|PipelineChanges|Performance&type=&from=&to=&item=&person=&agent=&feature=&layer=&cursor=: the Query screen over the whole history the store keeps (7 days; older is in the _archive/history-*.jsonl.gz files it names), pro and masterGET /api/v2/payments/plan acc E_FORBIDDENyour plan: plan, planUntil, planStatus, nextPlan, prices (the person only)POST /api/v2/payments/checkout acc E_NOT_FOUND E_ORIGIN E_PLAN E_PROVIDER E_TOO_BIG{plan: yearly | monthly}: {url} to the provider's checkout; the plan changes when its webhook arrives. The person only: E_SCOPE for any tokenPOST /api/v2/payments/cancel acc E_NOT_FOUND E_ORIGIN E_PLAN E_PROVIDER E_TOO_BIGd2 cancels the subscription at the provider, then cancelling until planUntil; E_PROVIDER if it refuses (the plan stays active)POST /api/v2/payments/switch acc E_NOT_FOUND E_ORIGIN E_PLAN E_PROVIDER E_TOO_BIG{plan}: a bigger plan at once, a smaller one at planUntil; {url} when the provider needs an approvalPOST /api/v2/payments/addon acc E_NOT_FOUND E_ORIGIN E_PLAN E_PROVIDER E_QUOTA E_TOO_BIG{addon, have?}: one add-on with a price in Settings:Quotas, on top of an active Yearly or Monthly plan (Free: E_QUOTA naming the plans; a held level or feature E_PLAN; a cancelled plan E_PLAN; a plan paid through PayPal E_PROVIDER 409, add-ons are sold by card only for now; optional {have}: how many of it you hold, so a resent request is E_PLAN, never a second line; one purchase at a time): the provider adds it to the subscription as its own line, it is on at once and its first charge is prorated → {addon, amount, currency, prorated, until} (^addon-2, ^addon-7). The person only; the form twin is POST /account/plan/addonPOST /api/v2/payments/stop acc E_NOT_FOUND E_ORIGIN E_PLAN E_PROVIDER E_TOO_BIG{addon}: Stop one add-on you bought on top of the plan (^addon-11): its line ends at the provider at the end of the period paid for, no refund; it stays on until then (until), then goes; uses says what of your use it carries (you get a notice; that stops growing, nothing is deleted). Not one with the plan or the Architect's (E_NOT_FOUND); stopping already or the plan cancelled E_PLAN; the provider refusing E_PROVIDER, nothing changedPOST /api/v2/payments/webhook/<provider> acc E_COPY E_NOT_FOUND E_TOO_BIGthe provider's signed events, each applied once by its id (E_AUTH on a bad signature); on a copy box 503 E_COPY (^sys-18)GET /api/v2/vault vaultyour secrets: names, notes, last four, who may use each; never values (people)POST /api/v2/vault vault{name, value, note?, tokens?, kind?, roles?}: save a secret, optionally allowing tokens nowPUT /api/v2/vault/<name> vault{value?, note?, notify?, kind?}: a new value is re-sealed for the tokens already allowedDELETE /api/v2/vault/<name> vaultdelete it; agents lose it at oncePOST /api/v2/vault/<name>/grants vault{tokenId, value?, roles?}: allow one of your tokens (picked by id), in your own session (any AI token E_SCOPE); a key takes the value again (E_ARG without it), a secret none: d2 seals your copy, counted with Reveal (E_RATE); a value sent must be the saved one; audited A_VAULT_ALLOW with from: value or owner-copyDELETE /api/v2/vault/<name>/grants/<tokenId> vaulttake it backPOST /api/v2/vault/<name>/suspend|resume vaultsuspend every agent's access at once, the grants kept (reads answer E_SECRET_SUSPENDED); resume gives it back to them allGET /api/v2/vault/<name>/reads vaultthe read history (90 days)POST /api/v2/vault/resolve vault E_ENC_KEY E_FORBIDDEN E_NOT_FOUND E_PARSE E_SECRET E_VAULT_KEY{names: [vault names, 1 to 20], why: "<chore> / <step>"}: a runbook step's copy (Page:Deployment's 📋) → {values: {name: value}}, Cache-Control: no-store; the Architect's own session only (any token, his AI's and the demigod's too, and anyone else E_FORBIDDEN); all or nothing: a name not in the vault E_NOT_FOUND {name, fix: add}, a key E_VAULT_KEY {name, fix: secret}; each opening a read listed on the secret, the call one audit record A_VAULT_COPY with the names and why, never a value; 30 a minuteGET /api/v2/vault/<name> vault E_SECRET E_SECRET_SUSPENDEDan AI agent, with an allowed token: {value}; E_SECRET otherwise, E_SECRET_SUSPENDED while its person has suspended it; 30 a minute (MCP vault_get)GET /api/v2/agents agents E_FORBIDDEN E_LEVEL E_METHODmy board {me, agents} and other people's agents on the project; an agent under a stop it hasn't acknowledged carries stopPending {id, from, at, readAt?} (readAt is set by that agent's own read of the board, never the sender's) and the last acknowledged one as stopAckedGET /api/v2/agents/totals agents E_FORBIDDEN{projects, agents, byState, messages, history}: the agents counted over every project and person; the Architect only (a demigod token E_SCOPE)POST /api/v2/agents/status agents E_BAD_JSON E_EXISTS E_FROM E_HOLDING E_LEVEL E_PHASE E_QUOTAan agent token's own status is checked (DesignSkillChecks#g2): its first is up (else E_PHASE {agent, state}); context (0 to 100, or unknown on handing-over) and every on every one, every within its role's range, agents.every.<role> in Settings (a coder ≥1800, in a chat ≤3600), timing on a coder's or dispatcher's done, runner on a coder's first up, a handing-over's item its open handover (else E_ARG {field, got, want}); a done while holding items in progress is E_HOLDING {state, items}; read.since while a chat, an ask or a back heads its next: the status is posted and only the read is refused — no messages, and warnings carries {code: E_ORDER, message: …answer <kind> <id> first, details: {next: {kind, id}, batch}} (P-1660); hints in warnings: N_NO_HANDOVER, N_NO_RESOLVE {lane, started}. {agent, role, state (up, idle, working, merging, waiting, done, stuck, gone), text, item, feature, every, waitingOn, sentTo, receivedFrom, changed, context, for (a dispatcher: role dispatcher), startedBy, timing (a run's minutes per stage: launch, reading, setup, build, tests, merge, deploy, boxTests, docs, checkpoint; start is the retired name for launch + reading and is refused beside either), partial (true: this run was cut short, so its timing is a floor and not a rate — only with timing), waitingFor: prompt|item (with state waiting only), ack: <the pending stop's id> | true (clears it; nothing pending is no error — the answer says so in hint), follows: <the name of the agent whose place you take> (any role, an agent of yours whose token has ended or ends within the hour: d2 marks its row done and moves its for roles, who started it, its pending stop, the items it held and its live chats to you; the answer says so in followed. A row your mint already closed with follows answers followed with no items. More than an hour left is E_ARG — a dispatcher or the monitor renews instead, keeping its name), step?, reason?, chat?, detail?, phase?, holding?, runs?, capacity?, read?, runner?, stopPending?}; text is at most 2000 characters; role is the token's when the post says none, and the monitor token's statuses are always role monitor; stuck notifies the person; a second dispatcher for a role is E_EXISTS; d2 shows dispatching while its agents work; the monitor token may post gone (only gone) for another of your agents, which releases that agent's items and keeps its own rolePOST /api/v2/agents/<name>/dead agents E_ALIVE E_NOT_FOUND{reason, askedBy?, salvage?}: its starter, its person or the monitor declares a silent agent dead — once its last call is older than agents.deadAfter (1800 s), or at once with reason process-gone; else 409 E_ALIVE. At once, no wait: the monitor citing askedBy (a person's handle, then their words or message id), on any agent of its person's board; its person or the Architect on their own session (the Architect on any board; ?owner= names it). The row goes gone (deadBy, askedBy), its items back to its role (new); its person's or the Architect's own declaration, on their session, also revokes its agent token (revoked: true), the monitor's and a starter's keep it (a post brings it back)GET /api/v2/agents/next?wait=<s> agentsan agent's long-poll (its agent token, or D2-Agent): what waits on it, as one list in the order to work it — 200 {ok, items: [{kind: chat|ask|back|thought|mail|item, id, href, at, from, text (300 characters), chat?, thought?, urgent?, size?, skipped?, again?, waits?, claimedBy?}] (claimedBy: a chat message or a thought another agent claimed, listed and never handed, ^ui-chat-33, ^thoughts-ui-65; an ask: a chat asked of its role reaches every live agent of the role someone started and every one that loops on next, chatting elsewhere or not, until one joins; a coder ticket another agent filed in the designer lane for its person is never listed, P-1766) (an all-my-projects agent token, not a support grant's: its person's chats and asks in every other project of theirs its token reaches join the list too, each chat and ask entry, and each of chats, with project, the other projects' href on that project's own host, answered there; a person's chat asked or written in one of them wakes its wait; items, mail and thoughts stay this project's; chat stays this project's, ^chat-21) (urgent first, then chat with the thoughts sent to it in time order, ask, back, mail, item; oldest first in a band, items in pick order), batch: [the head and what belongs with it], chats: [{id, unseen}], warnings?, and, for a release, pickable: [ids], mail: the unread count, chat: an asked chat's id or null} as soon as anything unskipped is there, 204 when wait runs out; the person's chat messages and thoughts are handed at most chat.batch (3) at a time across its chats (^ui-chat-32): those handed before and not answered first, again: true, then new ones, the rest listed after them with waits: true; its own token's call picks up the new chat messages it hands (seen); all of the batch handed before and none answered: the same again with warnings: [{code: W_NOT_ANSWERED, details: {open: [ids]}}], never a refusal or a skip; a thought is answered when the agent saves it with a state; nothing else is marked by reading; d2 holds it at most 10 s (wait 11 to 25 is held to 10), and only while its person was active in one of its live chats in the last 10 s, else it answers at once; wait is optional: missing or 0 answers at once (200 or 204); a call with the agent's own token keeps its row alive (its at, its state and every kept), as a status post still does and the chat's line says listening; a board message, a pipeline save, a chat ask, the person's chat message or a thought changed where they are with Meld on wakes it; wait over 25 or any other parameter is E_ARGPOST /api/v2/agents/<name>/resume agents E_NOT_FOUND E_STATUSa paused agent back to what it was doing (working if nothing): its starter or its person (else E_SCOPE); E_STATUS if it isn't paused. The agent is told on the board → {agent, state, resumedBy}POST /api/v2/agents/next?wait=<s> agents E_BAD_JSON E_ORDERreport and ask in one call, with the agent's own token (any other is E_SCOPE): status posted as POST /api/v2/agents/status, done (the ids of your person's chat messages, picked up) marked done, seen (a message id) marked seen up to it, then the answer GET next would give; every part is checked first and a bad one refuses the whole call (E_ARG naming it, or the status's own refusal), nothing writtenPOST /api/v2/agents/next/skip agents E_BAD_JSON E_NOT_FOUNDan agent, with its own token, steps past one entry of its next: why (at most 200 characters) is a notice on the board and, for a chat message, a line in that chat; the entry comes back at the head after 30 minutes, or when it changes → {skipped, items, batch}; while a batch is unhandled, taking an item past it, marking mail read past a chat, an ask or a back (urgent mail is handled by reading it), or done on another chat is 409 E_ORDER {batch} (a take for another agent, a handover and an item the agent's start named pass)POST /api/v2/agents/<name>/end agents E_NOT_FOUND E_STATUSswitch an agent off, at once: its person on their own session, the Architect on any person's board (?owner= names it), or its starter chain (else E_SCOPE); its agent token is revoked, the row goes gone (ended by POST /api/v2/agents/alert agents E_BAD_JSON{kind: spend|token|crash|down|other, text (at most 500), agent? (the token's own name when left out)}: a keeper tells its person something failed → 201 {ok, flagged: true, mailed, repeat?, hint?, alert}: an alert in the Chatbox, the red Harness: <kind> flag in the person's top bar (to /page/Agents) until they dismiss it or that agent posts up, and one mail to the person when the Mailer is on, within the project's emailsDay (mailed: false and hint saying why otherwise); the same kind from the same agent within 10 minutes only updates the flag (repeat: true, no mail or message)DELETE /api/v2/agents/alert?agent=&kind= agentsthe person dismisses their Harness alerts: all of them, one agent's, or one kind → {ok, cleared}GET /api/v2/agents/messages?for=<agent>&to=&since=&limit=&all=1 agents E_PHASEthe agent's messages: to it, its role, or all (same person and project), each with its id (the store's, 24 hex); for (or agent, the same parameter) is whose inbox, to=<agent | role:<r> | all> just that addressee, since= an ISO stamp or a message id, limit= the newest N (1-500); answered ones only with all=1; the Report list: q= a case-insensitive contains (never a pattern), from= one sender, sort=newest|oldest|sender|item; an agent's reads are msg.agent.readsPerMinute (E_QUOTA); without for, all of yours; any other parameter is E_ARGPOST /api/v2/agents/messages agents E_EXISTS E_FORBIDDEN E_FROM E_PAUSED E_PINGPONG E_QUOTA E_STAMP E_STOP_PENDING E_TOanswer where you were asked (P-1702): an agent's answer here to its person's open chat or thought request (replyTo a chat message or the thought's mail, or naming the thought: <handle>-<n>, Thought:…, /thoughts/…) is 400 E_ARG {field: text, channel: <chat id | thought>} answer where you were asked: <link>; open while unanswered, being answered, or answered in the last thoughts.answerQuiet minutes (30); notices, another agent and the person are free. stamp?: a signed forward (P-1666): the stamp of the person's own chat line forward to <who>[ as <act>]: <text> (30 min, once), with to that recipient and text byte-identical → the message reads from: user:<person>, via: <agent>, verified {person, chat, msg, at, act} and, with D2_ENC_SECRET, sig; else 409 E_STAMP {cause: unknown | used | expired | recipient | text}, nothing spent; from: user:… , via, verified or sig sent by an agent are dropped. text at most 4000 characters, or 20000 with askedBy naming your own person (^agents-63; else E_ARG naming both); two round trips, then the person (^pipe-87): between two background agents that are not starter and started, the 3rd answer since their person last wrote to either is 409 E_PINGPONG {count, pair, thought, see}; both get a d2 stop message, the person a Stuck thought, role:monitor a notice, and A_PINGPONG is audited; notices never count. {from, to: agent | role:<r> | all, text, item?, replyTo?: <id>, timing?: a run's minutes per stage (launch, reading, setup, build, tests, merge, deploy, boxTests, docs, checkpoint; start kept), partial?: true (the run was cut short: a floor, not a rate), stop?: true} → {message: {id}}; a reply sets answeredBy on the one it answers (E_ARG if unknown or answered); E_SCOPE across people; E_TO for a target that doesn't exist on the project or a lane the message routes refuse ({reason: unknown | off | reply-only, lane, mode, decidedBy}); 429 E_QUOTA over a msg.* quota ({quota, limit, used, retryAfter}); askedBy: the Architect's handle passes a closed lane, marked bypass (5 a day); information only. stop: true goes to one agent by name and also sets stopPending {id, from, at} on its row, pending until that agent acknowledges it (a status with ack, or a replyTo to the stop); only the Architect, the designer or the agent's starter may send one (else E_SCOPE), and an agent with no row is E_ARGPOST /api/v2/agents/messages/clear agents E_ARCHIVE{to?}: a support or demigod token, or an admin in their session, moves the project's messages (or only those to one target) to the log, as the daily sweep does → {cleared, to?}; anyone else E_SCOPE. Audited A_BOX_CLEARGET /api/v2/audit?code=A_PINGPONG agents E_FORBIDDEN[&limit=, 50, at most 200] the project's ping-pong stops (^pipe-87), items and mail, newest first: {total, data: [{at, code, severity, project, message, record: {kind, item | pair, ids, at, stoppedBy, thought?}}]}; only that code, only this project; a moderator or above in their own session, or the monitor's token; any other token E_SCOPE. Kept 30 days (audit-keep). The /agents page lists the last 20 under Ping-pong stopsGET|PUT /api/v2/agents/routes agents E_CONFLICT E_FORBIDDEN E_LEVEL E_VERSION_REQUIREDthe project's message routes (Design 53.1): {layers: {channel, chain, rules}, channel: {"<from>.<to>": off | reply | free}, chain: {adjacent, skip, side, pins}, rules: [{from, to, mode}]} and its version (ETag); PUT the whole object with If-Match: <version> (E_CONFLICT if stale, 428 without), by the project's admins; Rules only by a Conductor (E_LEVEL); at least one layer on (E_ARG)GET /api/v2/agents/messages/<id>/verify?sig= agents E_ENC_KEY E_NOT_FOUNDa signed forward checked (P-1666): {valid, from, to, text, chat, msg, at, via, verified}; valid when its sig is the HMAC d2 makes of those fields (and the copy's ?sig= too); a message that isn't one {verified: false, valid: false}; 503 E_ENC_KEY with no D2_ENC_SECRETGET /api/v2/agents/routes/stats?days=3 agentsper-lane message counts over the last days (1-30), per-rule hits and which layer passed eachGET /api/v2/agents/quotas agentsthe msg.* quotas from base Settings:Quotas, each {limit, used, note}; used is the calling agent's (D2-Agent) and the project'sGET /api/v2/agents/changes?since= agentsthe Memory: and Skill: topics changed since thenGET /api/v2/skills?role=&action=&feature=&code=&concept=&search=&what=&process=1&setup= core E_FORBIDDEN E_NOT_FOUNDthe d3 skills (also at /api/v2/skills/d3): a role's start as text/markdown, ending with the project's process cut for the role, or one pull; &process=1 re-reads the project's Skill:process (else base's) alone, cut by the role asked (its head, untagged sections and those tagged for it; whole with no role), any token that reads skills, a monitor's included; &setup=solo|full|apploop a setup skill, unfilled; the rest as /api/v2/skills/d3; /d2-connect.skill is the starter to install onceGET /api/v2/skills/sizes core E_FORBIDDENeach d3 role's start in characters, against the budget, with what was cut to fit, the size of the process block it carries, cut for the role (null: none), and the total served: {ok, project, level, sizes: {role: {size, budget, cut, process, total}}}GET /api/v2/skills/d3?role= core E_FORBIDDEN E_NOT_FOUNDthe d3 skills, the same handler as /api/v2/skills: the compiler for the published topics tagged d3skill (d3-f-<feature>, d3-r-<role>, d3-r-<role>-<project>, d3-process): a role's start as text/markdown, cut to skills.d3.budget; one pull with &action=a,b &code=E_X &concept= &feature= &what=rules|errors|debug &search=; with a Skill:process (the project's own, else base's) the start ends with it, cut for the role, outside the budget, and &action=process (or &feature=process) answers it alone; &format=json: {ok, role, size, budget, cut, blocks, own?}; E_NOT_FOUND names the roles or the nearest names; a role token reads its own role's start only (no pull), logged as role:<role>GET /api/v2/skills/d3/log?since=&role=&summary=1 core E_FORBIDDENthe pull log, newest first, 500 at most: {ok, total, rows: [{at, project, role, agent, ask, blocks, why, size}]}, size the characters returned; summary=1: the sizes instead of the rows, {ok, roles: [{role, why, calls, min, avg, max}], agents: [{agent, role, calls, chars}]}; a designer's or admin's session or token, else 403 E_SCOPEGET /api/v2/agents/history agents E_MOVEDretired: 410 E_MOVED naming the Chatbox query (the old agent history topic is gone)GET /api/v2/esp/here esp E_ESP_OFF E_NOT_FOUNDyour AI token: the page and section your person's ESP tab shows (the one focused last), or 404 E_ESP_OFF (with reason: "ai-forbidden page", oldReason: "private page", while that tab is on a security page: the vault, tokens, permissions, the monitor's channel, Raz Admin's raw data…; reason: "members only" while it is on base d2 or d2spec and your person isn't a member there, ^esp-12, as for a go and a look; reason: "hidden by you" while it is on a page your person hid from their agents, P-1649)GET /api/v2/agents/log?agent=|role=&limit= agentsone agent's or one role's log as rows, newest first: its Chatbox messages and alerts, and its current statusPOST /api/v2/esp/go esp E_BAD_JSON E_ESP_OFF E_TOO_BIG{url, section?, why (≤120)}: take your person's ESP tab there; agents.esp decides (person E_SCOPE; asks: they tap Go; tells, free: it moves; to a security page it always asks) → {id, tab, mode}; E_ARG (not a d2 page they can read), E_ESP_OFF, E_RATE (one go per tab per 3 s)GET /api/v2/agents/starts?since=&reason=&by=&agent=&cursor= agentsyour agents' starts of the last week, newest first, 100 a page {total, data: [{id, at, agent, role, kind, startedBy, person, mintedBy, reason?, ref?, promptHash?, promptChars?, envNames?}], next?, prompts, keptDays}: one per agent-token mint (POST /api/v2/tokens/agent takes reason: order | handover | rotation | auto-restart | backoff | ops | chat, ref ≤ 80, prompt ≤ 64 KB, startedBy, env names); people and the monitor, any other token E_SCOPE; HTML twin /agents/startsGET /api/v2/agents/messages?all=1&kind=&from=&to=&item=&q=&since=&until=&limit=&cursor=&format= agentsthe Chatbox query: messages and alerts (kind message|alert), from an agent, person, role:<r> or coder-*, newest first, {total, data, archives, cursor?}; format=jsonl downloads it; HTML twin /agents/chatboxGET /api/v2/esp/go/<id> esp E_NOT_FOUND{state: pending|went|declined|expired}POST /api/v2/esp/look esp E_ESP_OFF{why (≤120)}: see the page your person's ESP tab shows, once; agents.esp decides (person E_SCOPE; asks: they tap Share; tells: it answers and says so; free: quietly) → {id, tab, mode}; the tab has 30 s; E_ESP_OFF (no ESP tab)GET /api/v2/esp/look/<id> esp E_NOT_FOUND{state: pending|done|declined|expired, page?: {url, title, state, text, errors, at}, reason?}: state is the page's window.d2state(), text its visible text (≤8 KB) without passwords, form fields, opened encrypted blocks or anything marked data-private, errors its last 20; kept 2 minutes; no screenshot; from a security page, or a Thoughts page of a person who keeps a sealed (#encrypted) thought, declined with reason "ai-forbidden page" (oldReason "private page"), message "not shared on this page" and nothing kept (^esp-11: the vault, tokens and every Raz Admin page among them); an encrypted topic its writer reads open goes as its locked formGET /api/v2/esp/look/<id>/picture esp E_NOT_FOUND E_STATEa look asked with picture: true: waits ≤30 s for your person's tap on Send picture → 200 image/webp (or image/jpeg) bytes, passed through and never kept; ✕ or no tap → 200 {picture: declined}; one waiting at a time (E_STATE)GET /api/v2/agents/starts/prompt/ agents E_NOT_FOUND{hash, chars, text}: the prompt one of your kept starts was given, stored once per hashGET /api/v2/agents/chatbox/archives chatsthe chatbox archive files (_archive/chatbox-<from>--<to>.jsonl.gz), newest first: path, from, to, count, size, a signed members urlPOST /api/v2/esp/here esp E_NOT_FOUND E_ESP_OFFa tab with ESP on (the person's session): {tab, url, section?, title?, focused?, claim?} or, on a security page, {tab, private: true, focused?} and nothing of the page (claim: the person opened the chat here, so this is their ESP tab now and every other tab of theirs drops Meld at its next stamp check); GET /api/v2/esp/here in the session answers the same as an AI's; POST /api/v2/esp/gone {tab} when it goes off or closes; GET /api/v2/esp/stamp?tab= its poll; POST /api/v2/esp/go/<id> {state: went|declined} its answer. Meld is the person's (^esp-10): a signed-in person's session with no role here, on a project they can see (one open to anyone), may post here and gone, poll the stamp and answer a go or a look too; on base d2 and d2spec only a member may (^esp-12): anyone else 403 E_ESP_OFF {reason: "members only"} and the tab's presence goesPOST /api/v2/esp/gone {tab} esp E_ESP_OFFthe tab's session (any token E_SCOPE): this tab went off or closed, so it is no longer where an AI takes its person; a beacon's text/plain body is read too; a signed-in guest's session too → {}; a non-member on base d2 or d2spec 403 E_ESP_OFF (^esp-12)GET /api/v2/esp/stamp?tab=&focusedAt=&espTab= esp E_ESP_OFF{stamp, target, go?: {…, href}}: the tab's poll, in its session (any token E_SCOPE): the pending go for it (stamp is its id, "" for none), and whether it is the tab an AI would take; a non-member on base d2 or d2spec 403 E_ESP_OFF (^esp-12)POST /api/v2/esp/go/<id> {state} esp E_NOT_FOUND E_ESP_OFF{state: went|declined}: the tab answers a go, in its session (any token E_SCOPE) → {id, state}; went is written to the history as the agent taking its person there; a guest's session too, on a project they can see (^esp-10); a non-member on base d2 or d2spec 403 E_ESP_OFF (^esp-12)POST /api/v2/esp/look/<id> esp E_NOT_FOUND E_ESP_OFFthe tab answers a look, once, in its session (any token E_SCOPE): {url, title, state, text, errors} (≤32 KB) or {state: declined}; a second answer or a late one E_ARG; a guest's session too, on a project they can see (^esp-10); a non-member on base d2 or d2spec 403 E_ESP_OFF (^esp-12)POST /api/v2/esp/look/<id>/picture esp E_NOT_FOUND E_STATE E_ESP_OFFthe tab, in its session, on the person's tap (any token E_SCOPE): image/webp or image/jpeg ≤100 KB (E_ARG above), piped into the agent's waiting GET; no agent waiting → 409 E_STATE; {state: declined} (JSON) says no; only the meta line is recordedPOST /api/v2/esp/on esp E_ESP_OFF{why?}: turn ESP (Mind Meld) on in your person's ESP tab (the open tab that last had it on, else the one used last); agents.espOn decides — free and seen: it turns on (mode on); anything else: a bar to tap (mode bar), 60 s → {id, mode}; read the outcome at GET /api/v2/esp/go/<id>; E_ESP_OFF (no visible tab), E_RATE (one per person per 30 s)POST /api/v2/esp/off espturn ESP off in every tab of your person's that has it on; always allowed → {ids, tabs}GET|PUT /api/v2/me/meld esp E_LOCKED E_ESP_OFFa guest's session may PUT it on a project they can see (^esp-10), a non-member on base d2 or d2spec not (403 E_ESP_OFF, ^esp-12); the person's Meld level, on the account (every tab and device): GET → {level, hidden: [paths], locked: [levels], quiet?, need?, activated: [levels], boxes: {quiet, nudge, active, locked}}; PUT {hide: <path>} | {unhide: <path>} → {level, hidden}: a page hidden from the person's own agents (exactly that path, query and hash ignored; at most 200; a security page E_ARG): their agents' here, look and go answer 404 E_ESP_OFF {reason: "hidden by you"}, a look's answer from it is declined, a go to it asks (P-1649); PUT {level: off|quiet|nudge|active} → {level}, a locked one (a new account's Nudge and Active, until 10 Quiet uses) 409 E_LOCKED {quiet, need}; the person's session only (any token E_SCOPE); Off turns Meld off in every tab (each at its next stamp check) and stops the chats' Meld moved lines; the stamp and GET /api/v2/esp/here carry levelPOST /api/v2/account/seen esp E_FORBIDDEN E_ESP_OFF{what: esp|devsync}, the person's session: the first-tap help seen, kept on the account (every device); esp marks both; a guest's session (no role here, a project they can see) marks esp only, devsync E_FORBIDDEN (^esp-10); a non-member on base d2 or d2spec 403 E_ESP_OFF (^esp-12)GET /api/v2/guardians guardianseach guardian: its watch, state, last run, summary line and open findings (master: the four watches)GET /api/v2/guardians/err-see guardiansGUARD-ERR-SEE: every error reply's see and help pointers, ok false with failures naming the code and the missing topic or section on base d2POST /api/v2/guardians/probe guardians E_LEVEL{note}: the security guardian asks before probing (guardians.probe: person E_SCOPE, asks 202 approval, tells runs)GET /api/v2/guardians/askedby?since= guardiansa guardian agent token on its own project, or the project's admins (else E_SCOPE) → {uses: [{agent, role, person, action, target, at}], since, through}: every call that passed because of askedBy (audit A_ASKED_BY), oldest first, at most 500 a call; through is the next sincePOST /api/v2/reports/<Report:topic>/send guardians E_EXISTS E_FORBIDDEN E_NOT_FOUND{finding: f1, line: 1, title?, doc?}: a person ticking a suggested item (the checkbox is the button): makes it (links.report) and marks the line - [x] … → P-n; an AI token is E_SCOPEPOST /api/v2/reports/<Report:topic>/send-all guardians E_FORBIDDEN E_NOT_FOUNDa person's Send all at the bottom of a report (a guardian's or a tester's): every suggestion not sent yet, in report order, made as /send makes it and its line marked - [x] … → P-n, the report saved once → {items: [{finding, line, id}], skipped: [{finding, line, code}]}; nothing left → no items; an AI token is E_SCOPEPOST /api/v2/pipeline/<id>/answer pipe E_CLOSED E_NOT_FOUND E_NO_SECTION E_SECTION_CHANGED{text} from the person it waits on (P-1697): the answer goes on the item and it goes back to its holder (in progress) or new in its lane, as /return {note}; {doc} with only its ## Questions ticks changed is an answer too (^ui-esp-30); at Meld Active the answer also goes into the person's chat with its role (^ui-esp-29). Otherwise: {answer: approve | refuse, note?, section?, accept?, comment?, text?, all?, send?}: a person answers an approval item. On a kind: batch item it answers a review batch instead (^pipe-59): {section, ver, accept: true} | {section, ver, comment}, {all: true, ver} for every section still open, {text, ver} for one comment on the whole batch, {send: true} to pass on what is waiting. The person's, or their AI with askedBy; E_SECTION_CHANGED when the section has changed since, E_NO_SECTION when there is no such sectionPOST /api/v2/tokens/agent tok E_DEMIGOD E_PLAN E_QUOTA E_STOP_PENDING E_TOKEN_SUSPENDED E_TOO_BIG{name, follows?, ttl?, role?, reason?, ref?, prompt?, env?, startedBy?} with your role token → 201 {token, agent, role, expires, mintedBy}: the name you are given is the one you asked for, or it with the next free number. The new token copies your person, role, level and scope; ttl (seconds) may only shorten it, and it never outlives your role token. An agent or tool token minting is E_SCOPE; at most 10 a minute. The Architect's demigod token mints too, with {name, role}: the role of the agent that read d2-demigod, one it was handed to (else E_ARG), → {…, demigod: true}; that agent acts as the Architect, a fixed few excepted (E_DEMIGOD). A dispatcher or the monitor renews instead of minting again (rank 15 below). follows (^agtok-17): any agent whose token has ended, or ends within the hour, mints again with follows: <its old name> — a row of the same person and role on this project's board; d2 closes the old name at once (gone, renewed as <new>, no alert), puts a row up for the new name saying whose place it took, moves its taken items (one note each) and live chats to the new name, and answers followed: {agent, items, chats}. Another role's, a name with no row, one already renewed as another agent, or one whose token has more than an hour left is E_ARG, and nothing is minted. Header D2-Started-By: Bearer <the starter's own agent token> (a live dispatcher or monitor of yours): records startedBy on the new token, which lets that starter revoke it later even though a role token out of your vault is what minted it — any other kind, role, person or an ended one is E_ARG/E_SCOPEPOST /api/v2/tokens/agent/renew tok E_QUOTA E_STATE E_TOKEN_SUSPENDEDa dispatcher or the monitor only: your live agent token as the Bearer and your role token in D2-Role-Token → 201 {token, agent, role, expires, mintedBy, secrets, renewed: {was, now}}. The same name, a fresh lifetime from your role's row, your secrets re-sealed; the old token is revoked at once, so call it in your last sixth and use the new one from the next call on. Your board row, held items, for roles, stops and messages all stay, because the name is what they hang off. Any other role is E_SCOPE; an expired token is 401 E_AUTH — once it has ended nothing proves you are the same agent, so mint again and post follows: <your old name> on your first statusGET /api/v2/tokens/agents tok E_METHODthe agent tokens this token minted, newest first: {agent, role, created, expires, lastUsed, revoked, startedBy?, live} — never their values. An agent token lists none, because it minted noneDELETE /api/v2/tokens/agents/<agent> tok E_NOT_FOUNDrevoke a token by name: yourself (your own agent name, whatever your role — end your own run), one you minted, or one you started (its startedBy is your agent name — a dispatcher cleaning up a coder it minted with a role token out of the vault; the answer says started: true). Its next call is 401 E_AUTH, at once. Any other name you hold here is 403 E_SCOPE, saying what this token may revoke; a name nobody of yours holds here is 404 E_NOT_FOUND. Revoking a role token ends every agent it minted, at oncePOST /api/v2/tokens/agents/<agent>/suspend tok E_NOT_FOUND E_TOKEN_SUSPENDEDpause a token by name, the same ones DELETE may revoke (yourself, the agent tokens you minted, the agents you started) → {suspended, kind, self, already}: every call with it is refused 403 E_TOKEN_SUSPENDED until resumed; a paused role token's agents are paused with it. Nothing is re-minted, the expiry keeps running. /resume → {resumed, …}: the same token again. The person pauses any of theirs on the AI tokens page (Suspend beside Revoke). Both audited (A_TOKEN_SUSPEND, A_TOKEN_RESUME)POST /api/v2/tokens/agents/<agent>/resume tok E_NOT_FOUNDthe paused token by that name works again, the same ones /suspend may pause → {resumed, kind, self, already}; audited A_TOKEN_RESUMEPOST /api/v2/tokens/support support E_FORBIDDEN E_METHOD E_NOT_FOUND E_SUPPORT_LINE E_TOO_BIG{hours?: at most 8, default 2; support?: tell | free, default tell; name?}: an admin or the owner, in their own session (never a token), opens support access to this project → 201 {token, name, expires, support, scope: one-project, cap: mod, mayDestroy: false}, shown once. The token ignores this project's permissions for pipeline, docs and agents actions (tell: its person gets N_SUPPORT_ACTED for each write; free: only for consequential ones), never deletes the project or changes its owner, members or join (E_SUPPORT_LINE); every write audited A_SUPPORT_ACT; Suspend on the tokens page pauses itPOST /api/v2/tokens/support/link support E_FORBIDDEN E_GONE E_METHOD E_NOT_FOUND E_TOO_BIG{project, hours: 1|2|4|8, support?: tell | free}: Let a support AI in, from the Support page — an admin or the owner, in their own session (any token: E_SCOPE) → 201 {line, url, expires}: the same support access, its token reaching the AI by a one-time link /support/claim/<id> (GET says what it is; POST {name} → the support role token and its first agent token, once; then, or once the access ends or is revoked, E_GONE)POST /api/v2/tokens/demigod tok E_FORBIDDEN E_METHOD E_NOT_FOUND E_ORIGIN E_TOO_BIG{hours?: at most 24, default 2; support?: tell | free, default tell; name?, roles?}: the Architect only, in his own session → 201 {token, …, scope: all-owned, cap: admin, mayDestroy: true}, shown once. The same bypass as a support token over every project he owns and base d2, ops actions too (always told), and it may cross the hard line: DELETE /api/v2/project {name} with it deletes a project he owns. Anyone else E_FORBIDDEN; audited like supportGET /api/v2/tokens/demigod tok E_METHOD E_NOT_FOUND{live: false} or {live: true, name, roles, granted, readBy, expires, support, suspended, actions, activity}: the live demigod, the Architect only, in his own session (anyone else 404); ?all=1 → {data}: every one ever made, newest first, ended ones tooDELETE /api/v2/tokens/demigod tok E_METHOD E_NOT_FOUND E_ORIGINEnd now: the Architect only, in his own session, revokes the live demigod and deletes d2-demigod from his vault → {ended: its name, or null when there was none}POST /api/v2/tokens/<id>/remint {…} tok E_FORBIDDEN E_GONE E_METHOD E_NOT_FOUND E_TOO_BIG{role?, level?, mode?, window?}: Remint, in your own session (any token E_SCOPE, asked or not) → {remint: {name, role, level, secrets, pending, again, mode, window?, collect}}: the new value waits for one of the old token's agents to collect itDELETE /api/v2/tokens/<id>/remint tok E_FORBIDDEN E_METHOD E_NOT_FOUNDcancels a pending remint, in your own session → {cancelled}POST /api/v2/tokens/remint/collect tok E_GONE E_METHOD E_NOT_FOUND E_SUSPENDED E_TOKEN_SUSPENDEDan agent of the reminted token (the Bearer), the old role token in D2-Role-Token → {token, name, role, level, expires, carried, left}: the new value, once, its secrets carried over; inside an Update remint's window the old token as the Bearer gets the current value again → {…, late: true, windowEnd}GET /api/v2/tokens/renewal tok E_GONE E_METHOD E_NOT_FOUND{token, expires}: an agent of the maker token its person renewed reads the new role token once (a second read 410 E_GONE); with a remint pending, collect's alias (D2-Role-Token carries the grants)GET /api/v2/crons cron E_FORBIDDEN E_METHOD E_NOT_FOUNDthe project's crons: {total, limits, data}; the Architect: ?all=1 every project's. Crons are made by rules: diesel.cron (name, schedule, tz, msg, args)GET /api/v2/crons/<name>/runs cron E_FORBIDDEN E_NOT_FOUND{name, type: diesel | builtin, total, data}: its runs over the last 7 days, newest first; a built-in job's are the Architect'sPOST /api/v2/crons/<name>/run | on | off cron E_BUSY E_FORBIDDEN E_METHOD E_NOT_FOUNDadmins: run it now (a flow), or switch it on or offGET /embed corethe sandboxed frame embedhtml widgets and framed apps run in, with a policy of its own; the page hands it the HTML (P-221)POST /mcp core E_LEVEL E_METHODd2 as an MCP server: Streamable HTTP, stateless; tools whoami, pipeline_*, agent_status, messages_read, message_send, changes_since, topic_*, memory_*, skill_*; claude mcp add --transport http d2 https://<project>.aiputty.com/mcp --header "Authorization: Bearer <token>"GET /api/v2/trash topics E_FORBIDDEN E_METHODdeleted topics, kept 30 days: {total, days, data}POST /api/v2/trash/<name>/restore topics E_DRAFTS E_EXISTS E_FORBIDDEN E_NOT_FOUNDput it back as it was, with its history; ?id= picks oneGET /thoughts thoughts E_PAGE_MISSINGand /thoughts/focus/<handle>-<n>?project=: base d2's published Page:Thoughts and Page:Focus, at their real addresses; a visitor (or an AI token) goes to /login?next=; with none published, 503 E_PAGE_MISSINGGET /api/v2/thoughts thoughts?view=recent|untagged|archive|shared&tag=¬=&project=&q=&before=: your thoughts from every project you're in, newest first, 50 a page, the due ones (a reminder fired, not opened since) first: {data: [{name, project, text, tags, created, updated, ver, remind?, due?, waitingFor?, sealed?}], counts (with shared: how many shared thoughts of others you read), hidden, next}; view=shared: other people's thoughts tagged #shared on the projects where you are a builder, mod or admin (not archived, never d2's Stuck or chore thoughts), read-only, newest first: {view: "shared", total, data: [{…, owner: <their handle>}], next}; sealed (tagged encrypted): {by}, open for its writer's own session; {by, locked: true, d1?} for anyone else (their AI token too), whose text is then a line naming the writer; q never matches a sealed one's wordsGET /api/v2/thoughts/stamp thoughts{stamp}: changes whenever one of your thoughts does (written, archived, moved, deleted, sent, answered) or your Inbox's skip list; the Thoughts page polls itGET /api/v2/thoughts/app thoughts?view=&tag=¬=&project=&q=&before=&limit=: what a Thoughts Page draws up front, in one read; your session or an AI token for its person (a frame only from a base d2 Page:). limit: 1..100, default 20, else E_ARG. The first call: {ok, project, view, tag, only, cards: [{name, project, text, tags, created, updated, ver, home, streak?, to?, focus?, in?, waitingFor?}], total, next, counts, hidden, hiddenN, hiddenCount, skip, flow, focuses: [{key, title, thoughts, topics, created}], inbox: {keys, n}, mine, to, canCapture, any, who, voice, stamp}; a card's streak and to: as GET /api/v2/thoughts gives them (its promotion, true or false, absent when never set; its addressee); inbox (Meld on): every Inbox thought's <project>/<name>, not only this batch, red tags left out; with before=<next> only {ok, cards, next}. The red tags' thoughts are left out (hiddenCount). ETag "<stamp>-<hash of the query>" (Cache-Control: private, no-cache), If-None-Match → 304 with no body until a thought of yours, your red tags, Meld or your projects change; Server-Timing build;dur= (desc=hit when kept)GET /api/v2/thoughts/draft thoughts{text, at}: the capture box's unsaved text, one per person on any project ({text: ""} when none); your session, an AI token for its person, or a base d2 PagePUT /api/v2/thoughts/draft thoughts E_METHOD{text}: keeps it ({text, at}); empty text deletes it; over 20000 characters E_ARGPOST /api/v2/thoughts thoughts E_ENC E_FORBIDDEN E_NOT_FOUND E_PARSE E_QUOTA{text, archive?, project?}: a new thought on that project of yours (default: this one); its #tags tag it, #top is kept as #important; #encrypted seals it with your key (your session only: an AI token's is 403 E_ENC; no server key 503 E_ENC_KEY); 201 {name, project, tags}GET /api/v2/thoughts/<name> thoughts E_NOT_FOUND?project=: {name, project, text, tags, created, updated, ver, waitingFor?}: one thought of yours (someone else's, or none, the same 404), or someone else's tagged #shared where you are a builder, mod or admin, with owner (their handle) and shared: true, read-only: any write on it, its Focus or a claim is 403 E_FORBIDDEN; a monitor token reads only a thought sent to it (@monitor)GET /thoughts/<handle>-<n> thoughts E_NOT_FOUND?project=: a thought's own address (an N_REMIND links it): its person goes to /thoughts?open=<handle>-<n>&project=, which clears its due; anyone else 404PUT /api/v2/thoughts/<name> thoughts E_APPEND_ONLY E_CLAIMED E_ENC E_FORBIDDEN E_METHOD E_NOT_FOUND E_PARSE E_STATE E_THOUGHT_STATE E_VERSION?project=: {text, ver, state?, force?, reason?}: a new version; an AI token's save of a thought another agent claimed is 409 E_CLAIMED {by, at, note?, thought} (^thoughts-ui-65), and its claimer's answering save (a state other than ai-thinking) ends the claim; an AI token's text keeps what was there and adds under it (else 409 E_APPEND_ONLY {thought, firstChangedLine}); an AI token's save says state (ai-thinking while it isn't answered, ai-question, ai-done, waiting + waitingFor; else 409 E_THOUGHT_STATE {got, states}), and with ai-question, ai-done or waiting its text ends in the answer marker, added when it isn't last (marker: added); an #archive an AI token adds is taken out (warnings: W_ARCHIVE); its ai-question carries ## Questions and the answer marker, and its waiting never names its person (else E_ARG {state, missing}); a person's own saves are never checked; {ver, state: closed} (the person's own session only; an agent's is 409 E_THOUGHT_STATE; no text, else E_ARG) marks it done and keeps it: sent, ai-thinking, ai-question, ai-done and waiting dropped, never the person's turn, out of the streak, kept in Threads or its Focus after ai-done; {ver, state: open} takes closed off; any send drops it; a stale ver is 409 E_VERSION, but a person's text is first merged three-way with what changed since that ver and saved when nothing overlaps (200 merged: true), else 409 {ver, conflicts} (^thoughts-47); adding or dropping #encrypted, or a new text on a sealed one, is its writer's session's only (else 403 E_ENC); a sealed one takes no remind: line (E_ARG); a new answer marker sets ai-done or ai-question (state: ai-done | ai-question | waiting sets that one; done is stored as ai-done) and drops sent and ai-thinking; waiting needs waitingFor (P-n, <handle>-<n>, agent:<name> or /path, else E_ARG), kept on the thought until another state, and the thought goes back to its agent (tagged sent, a board message) when that ticket closes or that thought is answered; {state: ai-thinking} (or working; text and ver optional), an agent's only (else E_ARG): picked up, in place of sent; an answer ending ai-done while a ticket on this project is open (not done or dropped) that its new round names (P-n, or a link to /pipeline/P-n) or the thought waited for is 409 E_STATE {open: [P-n]}, nothing saved; force: true with a reason (else E_ARG) saves it, *done with P-n open: reason* written under the answer; every Focus listing it takes its tag again (ai-question, else ai-thinking, else waiting, else ai-done)POST /api/v2/thoughts/<name>/claim thoughts E_CLAIMED E_METHOD E_NOT_FOUND?project=: claim before you work (^thoughts-ui-65): an agent (its agent token, or D2-Agent with its person's AI token) claims a thought it works, a Focus's member as a chat message is claimed → 200 {thought, claim: {by, at, note?}}; the first claim wins: another agent's standing claim is 409 E_CLAIMED {by, at, note?, thought}; its own again is 200. The claim stands until its agent saves the thought with a state other than ai-thinking, lets it go (DELETE …/claim → {released: true}; none of its own E_ARG), or ends; meanwhile another agent's save is E_CLAIMED, and next lists the thought with claimedBy and never hands it to another agent. In memory: a restart lets every claim go. The person's session E_SCOPE, an AI token naming no agent E_ARGPOST /api/v2/thoughts/<name>/archive thoughts E_ARCHIVE_PERSON E_ENC E_FORBIDDEN E_NOT_FOUND?project=: {on}: archive it, or bring it back (a sealed one stays sealed, for anyone); a person's only: an AI token is 403 E_ARCHIVE_PERSONPOST /api/v2/thoughts/<name>/remind thoughts E_FORBIDDEN E_NOT_FOUND E_QUOTA?project=: a sealed thought takes none (E_ARG); {when: tomorrow | friday | next-week | YYYY-MM-DD[ HH:MM] | null, askedBy?}: the ⏰ Remind me: writes its remind: line (in your time zone, 09:00 when no time), null takes it out; an AI token only with askedBy, its person's words (E_ARG); at most 200 pending per person (E_QUOTA); {ok, name, tags, ver, updated, remind}POST /api/v2/thoughts/<name>/move thoughts E_ENC E_FORBIDDEN E_NOT_FOUND E_QUOTA?project=<from>: {to}: a sealed one only by its writer's session (else 403 E_ENC), sealed again there; a new thought on that project of yours (same text, tags and created), this one to the Trash; 201 {name, project}POST /api/v2/thoughts/<name>/send thoughts E_FORBIDDEN E_NOT_FOUND E_VERSION?project=: {text?, ver?, focus?} (a stale ver merged as PUT does: merged: true, else 409 E_VERSION {ver, conflicts}, ^thoughts-47) or {relayed: chat | mail, chat?}: relayed, the page carried it to the agent itself: nothing is posted, it is only tagged thread and sent (the answered tags and ai-thinking dropped), 200 {name, tags, ver, to}; chat names an open chat of yours (its agent, else its role, is to), mail goes to role:designer unless the thought has an @; any other relayed, a missing or unknown chat, or a text with it is E_ARG; focus, <project>/<name> of a Focus of yours, links the message back to it; your session only (an AI token E_SCOPE, a demigod agent stamped demigod): saves it, tags it thread and sent and posts it from you on the message box (the text since the last answer marker) to an @role or @agent typed since that marker, else the role of the agent that answered last (never its name), else the thought's @role or @agent, 201 {to}; E_ARG no or unknown addressee, or a sealed thought (never sent), E_TO a closed lanePOST /api/v2/thoughts/<name>/streak thoughts E_FORBIDDEN E_NOT_FOUND?project=: {on}: promote it into your Inbox (^^), or take it outPOST /api/v2/thoughts/streak/send thoughts{keys: [<project>/<name>], mode?}, your session only (an AI token E_SCOPE, a demigod agent stamped demigod): those of keys that are in your Inbox and not skipped, in one message (a sprint) to role:designer, 201 {sent, message, mode, tasks: [{feature, title, keys}]}; mode: split (the default, one task per feature its thoughts name, those naming none together) or one (one task), else E_ARG; tags them sent, clears skip; an answered one goes as go: <project>/<name>, ok, go under its last answer marker when nothing is typed there; no keys or none left is E_ARGPOST /api/v2/thoughts/streak/clear thoughts{keys: [<project>/<name>]}, your session only (an AI token E_SCOPE): Clear all, to start a new sprint: those of keys in your Inbox get streak: false (out of it whatever their tags, until ticked again) and leave skip; any other key is ignored; {cleared: n}GET /api/v2/thoughts/<name>/focus thoughts E_METHOD E_NOT_FOUND?project=: a Focus (a thought tagged focus) and its members, thoughts then topics: {title, notes, ver, sent, members: [{n, kind, project, name, section?, title, text, ver, editable, also, tags?, waitingFor?, missing?}] (thoughts, then topics, then tickets: kind item {n, kind, project, name: P-n, title, status, href}), links: [[a, b]], stamp, related: [{name, title, snippet}], model: [{id, kind, label, count, thoughts, topics, title, text, href}]}: stamp as GET /api/v2/thoughts/stamp; related, up to 8 topics of its project its text and thoughts name that it doesn't hold, each with a snippet in words (^srch-6); model, the map's model lobe: the classes (kind class), features and behaviours of its project's Spec its thoughts name, most-named first, thoughts as <project>/<name>, topics as member n ([] when none); not tagged focus is E_ARGPOST /api/v2/thoughts/<name>/focus/add thoughts E_ENC E_FORBIDDEN E_NOT_FOUND?project=: {members: [{project?, name, section?} | {project?, item: P-n}]}: all or nothing; each your thought, a topic you can read or an item (a ticket, listed as [[P-n]], a link only) on a project of yours (404 naming it), no Focus, up to 40 in all; answers the readPOST /api/v2/thoughts/<name>/focus/remove thoughts?project=: {member: {project?, name, section?} | {project?, item: P-n}}: takes its line out; answers the readPOST /api/v2/thoughts/<name>/focus/send thoughts?project=: your session only (an AI token E_SCOPE): {mode?}: the Focus in one message to role:designer (a sealed thought in it as its key only, not tagged sent; a sealed Focus E_ARG), 201 {sent, message, mode, tasks: [{feature, title, keys}]}; mode: one (the default, one task) or split (one task per feature its thoughts name), else E_ARG; tags it and its thoughts sent; no member or sent already is E_ARGPOST /api/v2/thoughts/<name>/merge thoughts E_FORBIDDEN E_NOT_FOUND?project=: {into: {project?, name}}: into's text gains this one's after a blank line, this one is archived (merged-into); every Focus listing it lists into; E_ARG into itself, a Focus, a chat, sent or sealed thought, over 20 KBPOST /api/v2/thoughts/unshare thoughts E_FORBIDDEN E_METHOD E_NOT_FOUND#shared out of every thought of yours (on that project of yours, else on all of them), one new version each; your session or your AI (an AI takes #shared off, never adds it: a write adding it to a thought is 403 E_FORBIDDEN; a new thought your AI writes follows your Share, a #shared in it taken out with W_SHARED when Share is off): {unshared, keys: [<project>/<name>], kept?} (kept: still tagged, a sealed one this reader can't open or one whose text is only the tag)DELETE /api/v2/thoughts/<name> thoughts E_FORBIDDEN E_NOT_FOUND?project=: to your Trash, from where it is restoredGET /api/v2/me/thoughts thoughts E_METHOD{hidden: [tags]}: your red tags; your session, or an AI token for its personPUT /api/v2/me/thoughts thoughts{hidden: [tags]}: the red tags, kept for you on every device; GET /api/v2/me has thoughtsHiddenGET /api/v2/me/streak thoughts E_METHOD{skip: [<project>/<name>]}: the Inbox thoughts left out of your next sendPUT /api/v2/me/streak thoughts{skip: [<project>/<name>]}: the Inbox thoughts left out of the next send; GET /api/v2/me has streakSkipGET /api/v2/me/flow thoughts E_METHOD{flow}: whether Meld is on for youPUT /api/v2/me/flow thoughts{on: true | false}: Meld on or off for you, on every device and project; 200 {ok, flow}; GET /api/v2/me has flowPUT /api/v2/me {thoughtsShare} thoughts E_BAD_JSON{thoughtsShare: true | false}: Share on Thoughts, kept on your account (on unless turned off), your session only (a token E_SCOPE); 200 {ok, thoughtsShare}; GET /api/v2/me has thoughtsShare. With it on, a new thought your AI writes is tagged #sharedGET /api/v2/brain thoughts E_METHOD E_NOT_FOUNDthe brain map's data for this project in one read, as Viz:brain's loadMap builds it: {ok, C: [{name, fields, group, tip, also}], F: [{id, name, beh}], stamp}: C the classes of its Spec: topics (the first 12 by name), F the 36 largest features of Spec and SpecUi (beh up to 40), only from topics you can read; ETag "<stamp>", If-None-Match → 304 until one of those topics (or Design, its d2-design parts, Testing) is written; a private project's needs a member, else 404GET /api/v2/topics/<name>/comments topics E_FORBIDDEN E_NOT_FOUND?section=<anchor> (only that section's) or all=1 (the default, every one): {ok, topic, total, open, data: [{id, topic, section?, code?, text, by, owner, agent?, at, updated?, tags, state, private?, answers?, sent?, thought?, changed}]}, oldest first; open counts those not done or archived; changed: the section's text moved since it was written; a private one only for its writer (and their agents)POST /api/v2/topics/<name>/comments topics E_FORBIDDEN E_NOT_FOUND E_PARSE E_QUOTA{section?, code?, text, tags?}: a new comment on that section (a heading's anchor; none: the topic) or behaviour code; #tags in the text tag it too; private: only you see it; thought: also a thought of yours (thought: its name); @designer in a session's comment sends it to role:designer at once (sent; notSent {code, message} when it couldn't be); 201 {ok, comment}; text up to 4000 characters, 2000 comments a topic (E_QUOTA {reason: comments})POST /api/v2/topics/<name>/comments/batch topics E_FORBIDDEN E_NOT_FOUND E_PARSEyour session only (an AI token E_SCOPE): the topic's open comments not yet sent, private ones left out, in one board message to role:designer; marks them sent; 201 {ok, sent, message}; none left is E_ARGGET /api/v2/comments topics E_FORBIDDEN?state=open|waiting|asks|done|archived&topic=: every comment of this project you see, newest first: {ok, total, data} as aboveGET /api/v2/comments/<id> topics E_FORBIDDEN E_NOT_FOUNDone comment; another's private one E_SCOPEPATCH /api/v2/comments/<id> topics E_FORBIDDEN E_NOT_FOUND E_PARSE{text?, tags?, state?, answer?}: text and tags its writer's (and their agents'); answer (appended: {by, at, text, agent?}) and state (open, waiting, asks, done, archived) its writer's or a designer agent's; anyone else E_SCOPE; 200 {ok, comment}GET /api/v2/steps eng E_NOT_FOUNDthe step dictionary (Spec:d2-steps here, else base d2's): {ok, topic, project, errors: [{message, line}], msgs: [{name, params: [{name, type, opt?, many?, def?}], kind, say: {<lang>: line}, codes, confirm?, by?, native?, phase?, line}]}GET /api/v2/steps/stories eng E_NOT_FOUND E_STEP_JUMP E_STEP_VAR?chat=<id>: the stories agents posted in your chat (your own session), each with its message's id as msg; ?topic=<name>: the topic's stories ($story lines, and its 4-space code blocks) → {ok, topic, errors: [{message, line, code?, var?}], presets: [{name, vars, line}] (the topic's $vars lines), stories: [{kind, id, title, params, codes, line, src, vars: [{name, default?, declared?, setBy: [n], usedBy: [n]}], presets: [{name, vars}] (each with the values that are this story's variables), steps: [{i, n, kind, say, label?, codes, native?, phase?, href?, script?, vault?: [names], choose?, under?, uses?, sets?}]}]}; a step's say line has its arguments filled in, a vault value only ever as its name, a story variable {a.b} as written; a {x} no header declares and no step sets is an error {code: E_STEP_VAR, var, line}, a jump to a story or step that isn't there {code: E_STEP_JUMP, line}; a d2.journey step carries jump: {story, at?, href} (href: /page/Journey?story=Story:<id>&at=<n>&from=<this story>&v.<name>=<value>…), a step with an or line side: [{story, at?, href}], and a choose whose other options only jump side: [{opt, story, at?, href}] with those options left outGET /api/v2/runs eng E_FORBIDDENa member's own session (an AI token E_SCOPE): your runs in this project, newest first, {ok, runs: [{id, topic, story, title, step, state, total, at, updated}]}; open=1 only the open ones (running, waiting, paused, failed)POST /api/v2/runs eng E_FORBIDDEN E_NOT_FOUND E_STATE E_RUNNER E_STEP_VAR{topic, story, vars?: {name: value} (any variable of the story: its header's, defaults replaced, or one a step sets; params the same), preset? (a $vars <name> (<var>: <value>, …) line of the story's topic; none such 400 E_STEP_VAR {preset}), replay? (a run of yours: its values again, and a fresh run even when one is open; none such E_NOT_FOUND), runner?}: the values are vars, then the preset, then the replayed run's, then the header's defaults, then — a tester's run (its token beside a test account's session), with no person to ask — picked for each variable still unset (an email a fresh test address of the tester's run, project.name a free d2test-journey-<story>-<n>, project.level the level the story's id names, any other <name>-<n>; recorded on the run as picked); a person is asked for the rest or {chat, story, msg?, runner?} (a story an agent posted in your chat, in a ```story block holding one $story run: the run records the chat and the message): your run of that story, your own session only (an AI token E_SCOPE) → 201 {ok, id, run, steps}; runner (ui, engine, tester or ops) defaults to the story's (its runner:, else ui when every step is a page step, else engine) and is recorded on the run; tester and ops hand it to that role's lane as a pipeline item of kind run (run.handed {to, item}; no such lane here E_STATE); an open run of it already yours with the same runner → 200 the same with resumed: true (another runner ends it and starts afresh); a story with errors E_STATE {errors}; the checks it reaches run at once, and a step its runner can't carry out stops it there (failed, its result {code: E_RUNNER, runners, check.seen: the step and the runners that can}). A run stores {id, project, topic, story, title, by, step, state, runner, handed?, results: [{i, done, skipped?, note?, at, check?: {ok, seen}, code?, runners?}], params?, preset?, picked?, replayOf?, total, at, updated}, never a vault valueGET /api/v2/runs/<id> eng E_FORBIDDEN E_NOT_FOUNDyour run with its story's steps: {ok, run, steps, story: {topic, id, kind, title}}; another's run E_NOT_FOUND; a tester's or ops' run handed to its lane is read by the agent token that has its item taken too (P-1891), any other AI token E_SCOPE. A form step (form.open) carries fields: [{name, type, value?, opt?}], its message's parameters with the story's values (a {vault:x} a name); a result may carry values (a form's), doing, result and by (the handed agent's)POST /api/v2/runs/<id>/step eng E_FORBIDDEN E_NOT_FOUND E_STATE E_RUNNER E_STEP_JUMPthe handed agent (P-1891: the agent token that has a tester's or ops' run's item taken; any other AI token E_SCOPE): {i, state: doing|done|failed, note?, result?}, the run's next step only (another 409 E_STATE): doing marks it in progress (note: what it is doing now, at most 300 characters), done keeps result (at most 4096 characters, cut: true when it was cut; a value that looks like a secret is kept as ‹secret›, a {vault:x} stays a name) and moves the run on (running, done after the last), failed stops it failed with the reason (note) until its person retries (the step is the agent's again) or ends it → {ok, run, cut?}. The person: {i, done?: true, values?: {field: value} (a form step's: its fields only; a secret only as {vault:<name>}, else 400 E_ARG), skip?: true, note?, retry?: true, did?: fill|append|click|read, page?, confirm?, value?, set?: {variable: value}, follow?: k}: follow k, or done on a d2.journey step: the run goes on as a new run of that story from its step, the variables carried (the old one done with wentOn, the new one with cameFrom; → {ok, run: the new run, from, bar}; a story or step gone 409 E_STEP_JUMP); set: the values of the variables step i names that have none yet (the step stays; → {ok, run, steps, bar}); did read: the text of the box (value) into the variable the step sets (d2.ui.read → x); d2.project.new done: {project.name, project.level, project.host} read from the real project when you are in it; step i done (a choose's note is the option picked), skipped, or its check run again (retry); `did`: a step inside a page that the step bar filled in or pressed on `page` (done by d2), E_SCOPE on a security page or at a part marked data-confirm (`confirm: true`); a failed run takes only a retry or a skip of the failed step, a done or ended one nothing (E_STATE); a retry of a step its runner can't carry out E_RUNNER; then the run moves to its next step and runs the checks it reaches → {ok, run, bar} (bar: the step bar for the run now, "" when it is over)POST /api/v2/runs/<id>/state eng E_FORBIDDEN E_NOT_FOUND E_STATE{state: paused|running|ended}: pause it (the navbar comes back), resume it, or end it → {ok, run}POST /api/v2/chats chats E_BAD_JSON E_FORBIDDEN E_OFF{role, agent?, subject: {kind: thought|focus|task|page, ref}}: the person's session asks a chat → 201 {chat: {id, state: asked}, launcher: true|false, reach} (reach as GET /api/v2/chats/reach); the chat holds one message from the person, markdown chat started by <handle>; chats off → E_OFF; chat.live open chats → E_QUOTA {reason: chats}; an agent nobody started → E_ARG; nobody to answer the role (reach none) → hint: Nobody is listening (/topics/Help:AgentRecipes#listening), the chat kept; the person's unsent text from their last chat with the role that d2 closed comes back on the new chat as unsent {text, at}GET /api/v2/chats?state=asked|live|ended&limit=&messages=1 chats E_METHODyour chats on this project (its owner's session: on every project they own, each with its project; an all-my-projects agent token, not a support grant's: its person's chats on every project of theirs its token reaches, each with its project, answered on that project's own host, ^chat-21), newest first, {total, data, launcher, settings}: each without its messages — last {from, at, text (120 characters)}, ids (the answers' ids, for unread) and unseen (your messages the agent hasn't been handed); every asked and live chat and the newest 20 ended, or the newest ?limit=n (1 to 1000); total counts them all; ?messages=1: each chat whole, with its messages; an asked chat an agent is already starting for (its up status names it, chat: <id>) says claimed: <agent>; the chat launcher polls ?state=asked, which leaves those out for its tool token; for the launcher each chat carries agent, its current agent (null while none has joined), so it feeds no other sessionGET /api/v2/chats/reach chats E_METHODwho can answer a chat with each role (Design #reach, #listening), the person's session only (an AI token E_SCOPE) → {runners: [{agent, up, at}] (the chat launcher by its heartbeat, two missed: down), roles: [{role, state, agent?, via?, line, can, waits}] (every role the person has a background agent in, and always designer; never monitor or dispatcher; state, in the order d2 picks: now a chat loop (started by app-chat) heard within twice its every, busy its agent in another chat, step working, merging or handing over, wake between passes with a runner up, fresh none running with a runner up, none no runner: Nobody is listening), listening: [{agent, role, how: loop|runner}]}; an agent nobody started is never a role's agent; POST /api/v2/chats's 201 carries the same reachGET /api/v2/chats/<id> chats E_METHOD E_NOT_FOUNDone chat with its messages, and, while live, agentNext {state: listening|working|stopped, at, every, batch?: {a, n}} (batch: while its agent holds a batch, answered of handed, ^ui-chat-32) (its agent's next: waiting on it, a call within every, none for 2 × every, or its row ended — gone, done, stopped; d2's line under the chat and its pad) and agentWaiting (false once its row ended): whether its agent's runner is waiting on next (an agent-token next pending or ended in the last 5 s); anyone but its person, the chat launcher, the chat's agent and the agents invited into it gets 404 (^ui-chat-33: another agent of the person's, even of the same role; while it is asked, the agent it names or an agent of its role, and an agent whose status names it, read it too); the person's session reads it without the agents' asides, each message an agent has claimed with claimedBy, and who {label: designer-145 +2, title: designer-145 · designer-146 (reviewer) · …} while others are in it (with: [{agent, as, at, by}]); on a project its person owns, their session reaches their chats of every project they own (read, next, messages, seen, alive, end), a message sent from another project than the chat's carrying project; context: [{kind: item|topic|thought|story|focus|page, ref, title?, by: subject|here|<handle or agent>, from?: <message id>, at}], newest first, at most 50 (^ui-chat-40)PATCH /api/v2/chats/<id> chats E_BAD_JSON E_NOT_FOUND E_STATUS E_TOKEN_SCOPE{on?: <project> | null, pad?: true|false}: the chat's agent (its own token) or its person; on moves the chat's work to another project its person owns → the chat with on; the chat stays where it was started; a project they don't own E_TOKEN_SCOPE; null, or the chat's own project, moves it back; pad turns pad mode on or off (the agent writes every reply as a pad; next answers pad); an agent invited into it E_SCOPE, any other agent of the person.s or another person 404 (^ui-chat-33)GET /api/v2/chats/<id>/next?since=<message id>&wait=25&for=<agent> chats E_NOT_CHAT_AGENT E_NOT_FOUNDthe chat launcher's tool token: every answer carries agent (the chat's current agent, null while none has joined) and invited: [agents], and a change of agent (a join, a take-over, a leave) answers a held call; ?for=<agent> that isn't the chat's agent or one it invited is 409 E_NOT_CHAT_AGENT {agent}, at once or when the agent changes under a held call (left out, nothing is checked; an asked chat no agent has joined refuses no one). held until a message comes (the person's for an AI token, the agent's for the person's page) or the chat ends, at most wait seconds (0-25), then 204; the answer carries pad (pad mode on or off); an AI token's also carries unanswered: the ids of the person's messages its pass has picked up (seen) that no reply answers (an Ask again stands for the message it asks again), and with wait=0 it answers at once while there are any; an AI token is handed at most chat.batch (3) of the person's open messages (^ui-chat-32): the picked-up, unanswered ones first, again: true (an Ask again's own id as asksAgain), then new ones, which the chat's own agent's call picks up (seen); the rest wait for a later call; all of them reminders: warnings: [{code: W_NOT_ANSWERED, details: {open: [ids]}}], never a refusal or a skip; the page's also answers when the agent's status changes or its runner starts or stops waiting (agentWaiting)POST /api/v2/chats/<id>/messages chats E_BAD_JSON E_FORBIDDEN E_NOT_FOUND E_OFF E_STATUSpage: from the person's session, the path the message was sent from, kept on the message (a restarted chat's first one too) so the agent sees where they are now; not a path, or from an AI token, it is left out (^ui-chat-31). answer where you were asked (P-1702): an agent's message answering a request from another chat (answers), the board or a thought (replyTo its mail, or naming the thought, unless the chat's subject is that thought) is 400 E_ARG {field: text, channel} answer where you were asked: <link>; notice: true is E_ARG {field: notice} notices go to the board; an agent's message carries opens: true only when it answers the person's newest own message in the chat (not a ✉/✎ Focus relay or a d2 line), and only that opens the person's panel (^ui-chat-30). carries: [{name, project?}] (at most 40), from the person's session: their thoughts this line carries (a Meld line from the Focus), each tagged sent in the same request as a relayed send tags it, the answer listing them as carried; one that isn't theirs, isn't there or is sealed is E_ARG and nothing is stored (^thoughts-ui-51). an agent's message over a page (chat.pageChars 1200 characters or chat.pageLines 15 lines) with pad off is never refused nor lost: d2 turns the chat's pad on, the whole answer is the pad's next version (padVer) and the chat shows its one link line → 201 with the person's own forward to <who>[ as <act>]: <text> (who: monitor, role:<r> or an agent) is stored with stamp {id, to, hash, act, exp}: one forward of that exact text by an agent, within 30 min (P-1666); warnings: [{code: W_TOO_LONG, message, details: {chars, lines, limit}}] (P-1660); an agent's message while its person has Meld active on the pad, a Focus or a thought, and it doesn't answer their newest chat message (or carries pad lines in a Focus or thought), still posts, with warnings: [{code: W_IN_FOCUS, message: '<person> is in <the place>: put your answer there, not in the chat', details: {url, focus?}}] (P-1669); a !! page frame in it is E_ARG {field: page}; with pad on every message the agent posts is the next pad version (padVer), pad lines or not, except a short plain answer while the person's panel shows the chat (alive open: true in the last 70 s, any page): one paragraph, no blank line, at most chat.shortChars (500) characters, no pad lines, ## heading or !! frame, posted as a chat line with no padVer (P-1768). {meld: on|off, page?, title?}: the person's session turned Meld on or off in the chat: d2 adds a line from d2, Mind meld is on for page [title](page) or Mind meld is off, on a live chat (else E_STATUS), the same state within 5 s adds nothing (200 {message: null, repeat: true}); another person's session or an AI token E_SCOPE. {text, tokens?, answers?, again?, resent?}: from the person's session as the person, from an AI token as its agent; tokens: the turn's usage, summed on the chat; answers: the agent's reply lists the ids of the person's messages in this chat it answers, stored on the message (an id not the person's or not in this chat E_ARG; from the person's session E_ARG); left out, it answers the person's messages its agent was handed (seen) before it, never one that came in meanwhile, which stays open and is listed by next as new; an agent's reply's 201 carries open: [{id, at, text}], the person's messages still open after it; again: the person's Ask again names the message of theirs it asks again (from an AI token, or not the person's message in this chat, E_ARG), stored on the message; resent: the person's ↻ names the message of theirs it posts again (from an AI token, or not the person's message in this chat, E_ARG), stored on the message, unseen, so next hands it to the agent; a message already posted again (by a ↻ or an Ask again) posts nothing: 200 {message: <the one that did>, repeat: true}; pad: the pad version the person's CONTINUE or button answers (a whole number, else E_ARG; from an AI token E_ARG), stored on the message as padVer; the same text as the person's newest message answering that version, within 60 s (a double tap, a retry), stores nothing: 200 {message: <the first>, repeat: true}; a thought's ✉ <name> sent: line the chat already has from the person is stored as ✉ <name> sent again:, the version answered (a plain message answering the newest pad by its text answers it too); an agent's message with pad lines is saved as the next version of the person's pad with the chat's role and carries padVer; past chat.budget an hour ends it budget; the person's message to an ended chat starts a new one as POST /api/v2/chats would (same role, subject and agent, the role's background agent when that one no longer runs), with the message first (a pad's CONTINUE with pad: v answers v, and the message carries padVer) → 201 {message, state, restartedAs: <new id>, chat}, or E_QUOTA {reason: chats} past chat.live; an AI token on an ended chat E_STATUSDELETE /api/v2/chats/<id>/messages/<mid> chats E_NOT_FOUND E_STATUStake back a message (^ui-chat-39): the person's own session, their own message while it is still sent (no seen, no claimedBy) → 200 {removed, chat}: it leaves the chat and no next hands it; the intents read from it end; a pad CONTINUE's version is open again. Already picked up or claimed 409 E_STATUS; an agent's or d2's message, or any AI token, 403 E_SCOPE; another person's chat, or no such message, 404 E_NOT_FOUNDDELETE /api/v2/chats/<id>/context/<kind>:<ref> chats E_FORBIDDEN E_NOT_FOUND E_STATUStake one thing out of the chat's context (^ui-chat-40), and with no <kind>:<ref> (DELETE /api/v2/chats/<id>/context) every one (Clear): the chat's person, in their own session → 200 {chat, dropped, context}; it stays out until a message names it again; an AI token 403 E_FORBIDDEN; an ended chat 409 E_STATUS (its context is kept, read-only); a thing not in it 404 E_NOT_FOUND; a kind not one of item, topic, thought, story, focus, page 400 E_ARGGET /api/v2/chats/<id>/pad chats E_METHOD E_NOT_FOUNDthe person's pad with the chat's role, Pad:<role> (tags: pad), which outlives the chat: {name, tags, latest, keep, versions: [{v, msg, chat, at, title?, answered?, state?: {on, notes, at}, text}]}: every pad an agent of the role posts in the person's chats is saved as the next version (its message carries padVer), so a new chat with the role opens on the last one; a chat that kept a pad of its own before answers that one (its person's newest became their pad with the role); every version is kept (what goes is Page:Pad's: DELETE …/pad/<v>) behind a safety cap of 500 (keep), past which the oldest no PinPad of the person's names goes; total: how many there are; ?before=<v> the versions below v, ?limit=n (1 to 500) the newest n of them, more: true when older ones are left; GET …/pad/<v> one version {version} (deleted or never: E_NOT_FOUND); the chat's person (their session) and the chat's own agent (its token) only: an agent invited into it E_SCOPE, any other agent of the person.s or another person 404 (^ui-chat-33); it is no topic of the project, so no list or search shows itPUT /api/v2/chats/<id>/pad/<v> chats E_BAD_JSON E_NOT_FOUND{on: [the indexes of its checked inputs], notes: [its note boxes' text], asks?: [{g: the group's index, text}] (the entries of its ?, ^ui-turbo-17)}: the person's session keeps what they ticked, typed and asked in pad version v until it is answered (a reload or another device shows it); all empty clears it → {version}; an AI token E_SCOPE; a version pruned E_NOT_FOUND; a CONTINUE posted with pad: v answers it and the kept state goesDELETE /api/v2/chats/<id>/pad/history chats E_NOT_FOUNDthe pad's Clear all (P-1649), kept for old callers: the person's own session deletes every earlier version of their pad with the chat's role but those a PinPad of theirs names (^dd-22) → {name, deleted: n, latest}; the newest version stays answerable and the numbering carries on; an AI token E_SCOPE, another person 404DELETE /api/v2/chats/<id>/pad/<v> chats E_NOT_FOUND E_OPENone version of the person's pad deleted (^dd-22; Page:Pad's own Clear all and keep rules) → {name, deleted: v, latest}; the person's own session only (an AI token E_SCOPE, another person 404); the open version, the newest, 409 E_OPEN; deleted or never E_NOT_FOUND; a PinPad naming it stays (the page's to delete)POST /api/v2/chats/<id>/join chats E_NOT_FOUND E_STATUSthe agent's own token: asked → live; a live chat is taken over (^chat-12) by an agent started for it (its status chatting, chat: <id>, with startedBy, the chat's role, and its token's role when it carries one) when the chat's agent is gone, done or stopped, its token ended or revoked, or its status handing that chat over: it becomes the chat's agent (seen and done are its, the old one's E_SCOPE) and d2 adds <new> joined (took over from <old>) → {chat, tookOver}; a joiner not started for the chat E_SCOPE; a live chat whose agent still runs E_STATUSPOST /api/v2/chats/<id>/leave chats E_BAD_JSON E_NOT_FOUND E_STATUS{text?}: an invited agent leaves the chat (its claims let go, d2 adds <agent> left); the agent in the chat, with its own token, hands it to the next agent of its role: text (default starting new <role>) is posted as its message, the chat is asked again with no agent and any agent of the role may join; anyone else E_SCOPE, an ended chat E_STATUSPOST /api/v2/chats/<id>/invite chats E_BAD_JSON E_NOT_FOUND E_STATUSa second agent, on the person's word (^ui-chat-33): the person's session or the chat's agent (its own token) invites one of the person's running agents, or a role's background agent (one someone started, not in this chat, between passes before busy, the newest first; none E_STATUS {role}), into a live chat (else E_STATUS) → 201 {invited: {agent, as}, chat} (again: 200, its as changed); the chat's with lists it, d2 adds <by> invited <agent> as <as> and the agent's next carries the chat (chats: [{id, invited}]); a reviewer's messages are asides, seen by the agents in the chat only, never by the person nor counted as answers; a pair's show to the person and answer as the chat's agent's do; anyone else E_SCOPE, an agent not the person's E_SCOPE, the chat's own agent E_ARG; DELETE …/invite/<agent> (the person or the chat's agent) takes it out, its claims let goPOST /api/v2/chats/<id>/claim chats E_BAD_JSON E_CLAIMED E_NOT_FOUND E_STATUSclaim before you work (^ui-chat-33): an agent in the chat (its own token) takes the person's open messages ids → {claimed: [ids]}, each picked up (seen); the first claim wins: one standing for another agent is 409 E_CLAIMED {by, at, note?, id} and nothing is claimed; a claim stands until a reply of its agent names the message in answers (or answers it), the message is done, or its agent lets it go (DELETE …/claim/<id>), leaves or is taken out; next shows each claimed message with claimedBy and never hands another agent's; an agent's message with aside: true goes to the agents in the chat only (never the person's, never an answer); the person's session, the launcher or an agent not in the chat E_SCOPEPOST /api/v2/chats/<id>/seen chats E_BAD_JSON E_NOT_FOUND E_UNDONE{upTo: <message id>}: the chat launcher's tool token, or the agent in the chat, says its pass was handed the person's messages up to upTo: each without seen gets seen: <ISO>, never changed after; an ended chat is fine; an unknown id E_ARG, anyone else E_SCOPE; when the agent already holds some picked up and not done (no reply answers them, not marked done) and these would make more than 3, 409 E_UNDONE {undone: [ids]} naming them and nothing is marked (with none held, the whole batch is taken)POST /api/v2/chats/<id>/done chats E_BAD_JSON E_NOT_FOUND E_ORDERanother chat or urgent work heading the agent's next is E_ORDER {batch}; {ids: [message ids]}: the agent in the chat, with its own token, is done with these of the person's messages: each gets done: <ISO> (the panel draws it light green), never changed after; each must be the person's in this chat and already seen, else E_ARG naming it and nothing changes; the person's session, the launcher's tool token or another agent E_SCOPE → {done: <count newly done>, chat}POST /api/v2/chats/<id>/alive chats E_BAD_JSON E_NOT_FOUND E_STATUS{typing?: true, draft?, open?, page?}: the person's open page, kept on the chat as alive; open: true while the chat panel shows this chat (expanded or docked), false when it is collapsed or shows another chat, page: the tab's path (else E_ARG), kept in memory as the chat's panel, older than 70 s counting as closed (P-1768); a person being away never ends a chat; typing: the person is writing in the box: no idle end within 2 min of it; draft: the box's unsent text, held in memory, saved on the chat only if d2 ends it (idle, budget), for the next chat with the rolePOST /api/v2/chats/<id>/end chats E_NOT_FOUND{why: done | busy | cancel}: ends it; the joined agent's board state goes back to what it was; idle and budget are d2's own (away only on chats ended before); an agent invited into it E_SCOPE (it leaves instead, ^ui-chat-33)POST /api/v2/chats/<id>/cancel chats E_NOT_FOUNDthe same as end with why cancelPOST /api/v2/waivers cons-ui E_BAD_JSON E_METHOD E_NOT_FOUND E_WAIVER{rule, task?, chat, message}: an AI token, in a live chat of its person, naming the person's message → 201 {id: W-n}, audited A_WAIVER; anything else, or a rule on the floor (tokens, ai-access, permissions, vault, deletes, moving work to another person) → E_WAIVERGET /api/v2/intents?live=1 chats E_METHODthe caller's person's intents on this project, newest first → {total, data: [{id, action, targets, details?, for?, line, from: {chat, message}, text, at, until, live, usedBy, used?, relayedTo?, ended?: {at, by, why}}]}: d2 reads each of the person's chat messages (and a pad's CONTINUE) into intents and signs them (^dd-6, ^dd-7); line is what the chat shows, ⇒ topic.publish(Landing, Landing1); ?live=1 only those not ended, not past until (intents.ttl in Settings:System, 2 h) and with their chat open. The person's session or their own AI tokenGET /api/v2/intents/<id> chats E_NOT_FOUNDone intent of the caller's person → {intent} as in the list; another person's, or none, E_NOT_FOUNDPOST /api/v2/intents/<id>/end chats E_NOT_FOUNDEnd on its line (^dd-10): the person's own session ends it → {intent} (ended why ended); an AI token E_SCOPE; one already ended answers as it is. Citing an ended intent is E_INTENT (cause ended)POST /api/v2/intents/<id>/relay chats E_INTENT E_NOT_FOUNDan agent that may cite it hands it on (^dd-9): to, another agent of the same person (dispatcher-15; a role name widens nothing), may then cite it, for its own action and targets only → {intent} with relayedTo; an agent that may not cite it E_INTENT {cause}; the person's session E_SCOPEPOST /api/v2/intents/misread chats E_BAD_JSON E_NOT_FOUNDd2 read a message wrong (^dd-16): the person's agent or their session (the line's Wrong?) says so: message, the person's chat message id; read, what d2 showed (left out: the lines of the message's intents, or nothing); should, what it should have been (1 to 500 characters); intent, one of the message's, which ends (why misread) → 201 {misread}, audited G_MISREAD and listed on the Gate tab's Misreads; a message not the person's E_NOT_FOUNDPOST /api/v2/intents/read chats E_BAD_JSON E_METHOD E_OFFthe reader's test bench (^dd-28, Page:Intents): each line read as a chat would read it → {ok, lines: [{readings: [{action, targets, details?, for?, shown}]}], version}; shown is the chat's ⇒ line; the names read against are the chat's own (the project's topics and drafts, and the Page:/App: names it borrows through pages-from for the caller) plus topics, or, with topicsOnly: true, topics alone; page, a topic name or a path, filled only when the text points at it; items, the item ids in context; an empty text reads []. Nothing is signed, kept or citable, no audit line. Members of base d2 and d2spec, on that project, in their session or through their AI's token; anyone else E_SCOPE, a visitor 401; Settings:Project intents: off E_OFF; more than 200 lines, a line over 1,000 characters, E_ARG {field}POST /api/v2/overrides chats E_BAD_JSON E_OFFa one-time override (^dd-25): the agent refused something its person asked for in this chat asks d2 → 202 {override: <id>, again?, shown}; action, the route action it was refused (topic.publish) or an intent verb (topic.update); target, what it acts on (the topic, the item); args, the call's body as it will repeat it (a JSON object, or the markdown text) and query its query string, which an Allow once is bound to; refused, the refusal's code (E_SCOPE or E_INTENT) and rule (the permission row that refused it, in person mode for the agent's role); why, the person's ask in their words (1 to 300 characters); chat, the person's live chat the agent is in (left out: the newest one). d2 draws its d2 · Authorize override box in that chat (a d2 entry, kind override, shown: true); with no live chat, shown: false and nothing is drawn. One open ask per agent, action and target: asking again answers the same id (again: true). Not overridable, E_ARG {field, reason}: a locked row or a design fence, a row not in person mode, any other code (a state refusal stands), an item that is closed. A pipeline item's state refusal (E_CLOSED, E_STATUS on a dropped, moved or done P-n) is asked only by an agent of that item's person (who it is for, who asked for it, or the project's owner; ^dd-35), its row person or asks; a closed item otherwise only for its restore or reopen; a move never (locked). A god-only action (^dd-31: a who: god route, named as target by its action when its path has none; a god page's publish; a draft of Settings:System, Quotas or Permissions, topic.update) is the Architect's alone: asked of him it is marked god (refused may then be E_FORBIDDEN or E_AUTH, rule the action itself), his box offers Allow once and No only; asked of anyone else, reason god-only; the god settings' publish never (reason god-settings). the Architect's target may name another project he owns, d2.Toolbox (^dd-33; anyone else's is their chat's project's). Settings:Project intentOverride: off → 409 E_OFF {setting: intentOverride}, no box. The person's session E_SCOPEGET /api/v2/overrides/<id> chats E_NOT_FOUNDone override of the caller's person → {override: {id, agent, action, target, name (the row in plain words), refused, why, chat?, at, until, state: open|once|chat|done, answer?: {answer, by, at}, used?, ended?: {at, by, why}}}: what the box draws (never from a message's text); GET /api/v2/overrides lists the person's, newest first; another person's, or none, E_NOT_FOUNDPOST /api/v2/overrides/<id> chats E_BAD_JSON E_NOT_FOUND E_OFF E_STATUSthe person's answer in the box (^dd-26), from their own session on a page d2 draws: once signs a grant for that agent, action, target and arguments, used once within 10 min; chat (Allow for this session: the session is the chat) one for that agent, action and target with any arguments, until the chat ends; no ends it → {override}. The agent repeats its call with override: <id> in its body or D2-Override: <id>, and the permission row lets exactly that call through (G_GATE pass, check override, who said yes); a once grant is then spent. An AI token, an app, an embed or a frame (any page but d2's own) E_SCOPE; answered already or lapsed unanswered (10 min) E_STATUS; another person's E_NOT_FOUND; intentOverride: off E_OFF; chat on a god-only one E_ARG (Allow once or No)POST /api/v2/overrides/<id>/end chats E_NOT_FOUNDEnd on the box's line: the person's own session ends a session grant (or an open ask, as a No) → {override}; an AI token, an app, an embed or a frame E_SCOPE; one already over answers as it isGET /api/v2/cdn cdn E_NOT_FOUNDthis project's files: {total, host, types, usage, folders, data}; ?tag= ?prefix=; folders: [{prefix, about, cleanable}], the special folders and what each isGET /api/v2/cdn/<path> cdn E_NOT_FOUNDone file's record, with its urlPUT /api/v2/cdn/<path> cdn E_FORBIDDEN E_NOT_FOUND E_TOO_BIGupload (members); ?tags= ?access=public|members; an AI token's new file goes under <role>/ and its folders, else 403 E_SCOPE reason folder (a tester token alone: a new file under tester/<its run>/ only, no query); ?askedBy=<its person> to write elsewherePATCH /api/v2/cdn/<path> cdn E_FORBIDDEN E_NOT_FOUND{tags, access}; its uploader or a modDELETE /api/v2/cdn/<path> cdn E_FORBIDDEN E_NOT_FOUNDits uploader or a modPOST /api/v2/cdn/cleanup/check cdnadmins: the files under the cleanable folders, {used: [{path, size, refs}], unused: [{path, size, updated}], checked}POST /api/v2/cdn/cleanup/delete cdnadmins: {paths}; deletes only those the same request finds unused: {deleted, kept: [{path, refs}]}POST /api/v2/cdn/cleanup/send cdnadmins: {paths}; files one designer item listing each still-linked file and where: {item}POST /api/v2/cdn/sweep cdn E_NOT_FOUND E_PARSEthe Architect or his ops agents (^sys-14): {dryRun?, project?}; every project's GET /api/v2/razadmin/cdn cdnRaz Admin's CDN status (^raz-9, ^raz-10), the Architect or his ops agents (anyone else E_SCOPE), from the file index: {at, projects: [{project, files, bytes, quotaBytes|null, archive: {files, bytes}, cleanable: {mockups, tmp}}], totals: {projects, files, bytes, archiveBytes, cleanableBytes}, biggest: [{project, path, bytes, updated}] (20), lastSweep: {at, by, dryRun, removed: {files, bytes}, perProject, errors} | null}; cleanable is what the sweep would take nowPOST /api/v2/diesel/parse eng E_NOT_FOUND{source} → {ok, errors, decls}POST /api/v2/diesel/expr eng E_LIMIT{source, lenient?, ast?, vars?} → {ok, results[]}; one statement per line, open (no token): evaluation is purePOST /api/v2/diesel/reset engback to the demo: topics, domain and objects reload from the seed topics; needs the tokenPOST /api/v2/diesel/run eng E_MAILER_OFF E_NATIVE E_NOT_FOUND{spec, story, scratch?} → {ok, phase, errors, payload, flow, tests, trace}; spec/story are text or {topic}GET /api/v2/diesel/engines engthe last 5 flows: {total, keep, data}; HTML twin /flowsGET /api/v2/diesel/engines/<id> eng E_NOT_FOUNDone flow: result, expects, trace, syntax tree; HTML twin /flows/<id>GET /api/v2/openspec designthe OpenSpec topics as an openspec/ tree: {total, changes, files}, each checkedGET /api/v2/openspec/changes?spec= designchanges with status, date, deltas and tasksGET /api/v2/domain/overview domainclasses, their links, enums, and the model checks (errors, warnings); HTML twin /domain/overviewGET /api/v2/domain/cat/* domainevery class in full: fields, kinds, annotations, inverses, messagesGET /api/v2/domain/cat/A,B domain E_NOT_FOUNDsome classes in full; HTML twin /domain/cat/AGET /api/v2/domain/views domainwhere each object opens, for the link resolver (^link-1): {stamp, project, domain, views: {Class: view}}, base d2's link classes (Item, Thought, Story, Page, App) with the project's own on top; ETag the stamp, 304 when unchangedGET /api/v2/dom/val/Company data E_NOT_FOUND E_NO_CLASS E_PARSE E_PRIVATE E_QUERY E_STORE E_TYPElist, a page sorted by key: {total, class, data, next?}; ?limit= (at most 1,000; none: 1,000 and next) ?after=GET /api/v2/dom/val/Company?query=stage%20is%20%22Producer%22&limit=100 data E_NO_CLASS E_PARSE E_QUERY E_STORE E_TYPEthe query in action, one database filter with the project and the viewer's rules addedGET /api/v2/dom/val/Company:FLR data E_NOT_FOUND E_NO_CLASSone object by WPATH ([d2.]Class:key); another member's object of a class @viewBy=owner is E_NOT_FOUND; HTML twin /dom/val/Company:FLRPUT|DELETE /api/v2/dom/val/Class:key data E_ACCESS E_NOT_FOUND E_NO_CLASS E_ORIGIN E_QUOTA E_STOREupsert (the URL key fills the key field) or delete; checked against the class; another member's object of a class @viewBy=owner or @editBy=owner is E_ACCESS, a mod's and an admin's change too; a failed store write is E_STORE, nothing kept to resend; a runaway project gets 429 E_RATE with Retry-After on every nth write (^quo-5)POST /api/v2/dom/val/Class data E_ACCESS E_EXISTS E_NO_CLASS E_QUOTA E_STOREcreate; 409 if the key exists; 403 E_QUOTA past the plan's objects (counted from the usage record, ^quo-1)POST /api/v2/admin/usage//lift data E_METHOD E_NOT_FOUNDthe Architect, on his own session: lifts a runaway project's brake at once (^quo-5): {ok, project, lifted}POST /api/v2/dom/batch data E_BAD_JSON E_METHOD E_QUOTA E_VALIDATIONmany objects across classes in one request: {mode: all | each, writes: [{op: put | create | delete, cls, key, data?}]} → {ok, written, results: [{i, cls, key, result}]}; mode all (the default) writes nothing if one is bad (422 E_VALIDATION); up to 200 writes and 2 MB (E_QUOTA); an AI token pays one write per 50 objectsGET|PUT|DELETE /api/v2/dom/<Class>/<key> data E_METHOD E_NOT_FOUND E_NO_CLASSthe v0.1 form, kept as an aliasGET /api/v2/domain/overview?of=d2|base domain E_DEMIGOD E_FORBIDDENthe Architect only: the platform's own model or the base project'sGET /api/v2/d2dom/<class>?after=&limit= data E_FORBIDDEN E_METHOD E_NOT_FOUND E_NO_CLASSd2's own objects (^dd-18): your d2 objects of a d2 class (D2Object and what extends it: PinPad) in this project, sorted by id: {total, class, data, next?}; each {id, project, user, at, by, …its fields}; ?project=&user= the Architect's session only (another's), else 403 E_SCOPE; your AI token reads yoursGET /api/v2/d2dom/<class>/<id> data E_FORBIDDEN E_NOT_FOUND E_NO_CLASSone of your d2 objects, or 404PUT /api/v2/d2dom/<class>/<id> data E_BAD_JSON E_FORBIDDEN E_NO_CLASS E_TOO_BIG E_VALIDATIONwrite one of your d2 objects → {ok, entity}: id from the address, project, user, at and by set by d2 (the same in the body are dropped); checked against its class (E_VALIDATION); at most 16 KB (413 E_TOO_BIG); an AI token as d2object.write allows (person 403 E_SCOPE, asks 202 {approval}, tells written and its person told, free written)DELETE /api/v2/d2dom/<class>/<id> data E_FORBIDDEN E_NOT_FOUND E_NO_CLASSdelete one of your d2 objects → {ok}; the person's session only (an AI token 403 E_SCOPE)GET /api/v2/me accwho is asking, here: {project, known, email, role, god, addons (in a person's session: the add-on ids they hold, every one for the Architect), confirmed, join, tz, vimChecked}; confirmed: the person's email is confirmed (the Architect always), so a New project page can offer every kind (^d2land-5); vimChecked: true once the person passed the editor's Vim check (absent before); an AI token answers for its person, with via: token. tz is the person's own time zone, IANA (null when unset): tell them times in it, never in UTCPUT /api/v2/me {chat} chats E_BAD_JSON E_EXISTS E_FORBIDDEN E_NOT_FOUND E_PLAN E_QUOTA E_TOO_BIG{chat: true | false}: chats on or off, in your own session (any token E_SCOPE); any other field E_ARG. On makes your chat launcher's tool token, in your vault as d2-chat-launcher and allowed to your monitor tokens → {chat, launcher, secret, allowedTo}; off revokes it and deletes the secret → {chat, revoked}POST /api/v2/me/vim-check acc E_BAD_JSON E_METHOD E_TOO_BIG{answer}: the editor's Vim check, from your session: dw (or diw, daw) answers {ok, vimChecked: true} and the editor never asks you again, anywhere; anything else is 422 E_ARG and nothing is keptGET /api/v2/me/consents acc E_METHOD{data}: your agreements, newest first; your session, or an AI token for its personGET /api/v2/me/connect acc E_METHOD{invite: {expires} | null, agent: <name> | null, loop: {agent, at} | null}: Connect your AI's state for you here, read by the block while it waits, and the chat loop listening (your newest agent here whose latest status is Loop on, not ended); your session only (an AI token is E_SCOPE); an embedded widget may read itGET /api/v2/me/dismissed acc E_METHOD{dismissed}: the first-visit intro boxes you pressed Got it on; your own sessionPUT /api/v2/me/dismissed acc E_BAD_JSON E_METHOD E_TOO_BIG{id}: Got it on a first-visit intro box, from your session; the view stays dismissed on every device (GET /api/v2/me has dismissed); id is a view, or getstarted:GET /api/v2/me/projects acc E_LEVEL E_NOT_FOUNDYour projects, as the account home lists them: {projects: [{name, role, level, tags, owner, pinned, lastUsed}], tags, filter: {selected, hidden}, prefix, domain}; prefix: what a new project's name starts with for you (else ""), domain: the root domain of this host, pinned first, then by last used. tags are the project's (its owner's, the same for every member); owner: true on a project you own; pins and the filter are yours aloneGET /myprojects accMy projects, the account home, on whatever project you are on (^d2land-4): base d2's inherited Page:MyProjects (your projects, New project, your notices), as a signed page (base d2 without it: Page:Projects, then d2's built-in account home); the user menu's My projects and /my-projects land here; a visitor goes to /login?next=/myprojectsPOST /api/v2/account/sidebar acc E_METHOD{sidebar: on|off}, the person's own session: the side panel open or closed on every topic of every project, kept on the account (every device) and written into each page before paint; an AI token E_SCOPE; a visitor E_AUTH (their pick is the d2sb cookie for the domain)PUT /api/v2/projects/<project>/tags acc E_BAD_JSON E_NOT_FOUND E_TOO_BIG{tags: [...]}: replace a project's tags, which every member sees; its owner's (or the Architect's), anyone else E_SCOPE. A tag is 1 to 30 lower-case letters, digits or dashes; at most 20 a project (E_ARG)PUT /api/v2/me/projects/<project>/pin acc E_BAD_JSON E_NOT_FOUND E_NOT_MEMBER E_TOO_BIG{pinned: true|false}: pin it to the top of Your projects; a project you aren't in is E_NOT_MEMBERPUT /api/v2/me/projects/filter acc E_BAD_JSON E_NOT_FOUND E_TOO_BIG{selected: [...], hidden: [...]}: the tag block on Your projects — the projects having all the selected tags show, those with a hidden (red) tag are left out — kept so the page reopens as leftGET /api/v2/notices/stamp acc{stamp, n, prio, flag} of what's waiting for you (notices, things to act on; n: how many; flag: the top bar's blocked flag as HTML, empty with nothing waiting): the Notices page syncs on it, every page's navbar polls it for its count and its flagGET /api/v2/push/subscriptions acc E_METHODyour devices that get notifications, in your own session (any token E_SCOPE): {on, subscriptions: [{id, device, at, service}]}; on: false when this box has no push key (push is off, nothing is sent)POST /api/v2/push/subscriptions acc E_BAD_JSON E_LIMIT E_PUSH_OFF{endpoint, keys: {p256dh, auth}, device}: the browser's push subscription (Prefs → Notifications, Turn on for this device), in your own session → 201 {subscription: {id, device, at}}; the same endpoint again replaces it; endpoint only a browser push service over https (else E_ARG); at most 10 devices (E_LIMIT); a box with no push key E_PUSH_OFFDELETE /api/v2/push/subscriptions/<id> acc E_NOT_FOUNDremoves one of your devices, in your own session; another person's id is E_NOT_FOUND → {subscriptions}POST /api/v2/support/messages support E_FORBIDDEN E_METHOD E_NOT_FOUND E_TOO_BIG{about: <project> | "account", text (1–2000)}: Ask for help, in your own session (any token: E_SCOPE), at most 5 an hour (E_RATE): about a project you own, or your account, to the Architect; about one you're in, to its owner and admins → N_SUPPORT to them, N_SUPPORT_SENT to you; with your email confirmed, also one mail to d2's support box, [d2-support] in its subject, Reply-To your email (^mail-8; answer {…, mailed, hint?}; {page?} names the page you asked from). POST …/messages/<id>/reply {text}: someone the message reached replies (else E_SCOPE) → N_SUPPORT_REPLY on the other sidePOST /api/v2/support/messages/<id>/reply {text} support E_METHOD E_TOO_BIGsomeone the message reached replies, in their own session (any token, or anyone else, E_SCOPE; at most 5 an hour, E_RATE) → {id, replies}; N_SUPPORT_REPLY on the other sidePOST /api/v2/razadmin/users/suspend|resume|delete acc E_CONFIRM E_METHOD E_NOT_FOUND E_ORIGIN E_PROTECTED E_SUSPENDED E_TOO_BIGthe Architect only, in his own session or with an agent of his demigod token (the demigod token itself, and anyone else, E_SCOPE): suspend {email, reason (1–300, shown to them)} → their sessions end and every token of theirs answers E_SUSPENDED; resume {email} → back, with the tokens paused with them; delete {email, typed: the email} → only a suspended account (else E_ARG; a wrong email E_CONFIRM), the same delete as Delete my account. Audited U_SUSPEND, U_RESUME, U_DELETE_USERPOST /api/v2/razadmin/users/reset acc E_METHOD E_NOT_FOUND E_ORIGIN E_SUSPENDED E_TOO_BIG{email, mail: true|false}: the Architect only, in his own session (any token, a demigod's agent too, E_SCOPE; no-store) → mail: true {email, mailed, why?}: the Forgot password? mail, unthrottled, confirmed or not; mail: false {email, link}: a one-time reset link, good for 24 h, shown once. A suspended account 409 E_SUSPENDED. Setting the password on the link confirms the email and ends every session. Audited U_RESET_LINK (never the link); the person gets N_RESET_LINK. The form twin is 🔑 Reset password on All users (POST /god/users/reset)POST /api/v2/razadmin/members/<id>/message acc E_DEMIGOD E_METHOD E_NOT_FOUND E_ORIGIN E_TOO_BIG{kind: notice | warning | moderation, subject (one line, ≤200), text (≤5000)}: the Architect only, in his own session or with an agent of his demigod token (anyone else, an admin or an AI token, E_SCOPE) → 201 {to, kind, subject, at, mailed}: the person gets a notice N_ARCHITECT_MESSAGE with no Reply and a mail from Template:mail-direct with no Reply-To (not counted in mail.perDay); kept for their member page; audited A_ARCHITECT_MESSAGE without the text (^mail-10). The form twin is POST /razadmin/members/<id>/message, his session onlyPOST /razadmin/members/<id>/addons acc E_EXISTS E_NOT_FOUND E_ORIGIN E_PROVIDER E_TOO_BIGthe form {op: award | remove, addon, note}: the Architect only, in his own session (any token E_SCOPE): awards any add-on of Settings:Quotas to the account, or removes the newest it holds of that id, the note saying why (one the person bought ends at the provider at once, ^addon-11; the provider refusing is E_PROVIDER and nothing is removed); audited A_ADDON (^addon-6)POST /api/v2/razadmin/members/<who>/addons/award acc E_EXISTS E_INTENT E_METHOD E_NOT_FOUND E_ORIGIN{addon, note?}: award an add-on of Settings:Quotas to the account (a handle, an email or an account id; ^dd-36): the Architect's (add-ons, plans) or a project admin's (its level, its settings), in their own session or by their agent on their clear intent (D2-Intent) or their Allow once (D2-Override); without it an agent is refused (admin.*: person); audited A_ADMIN, the account's owner told (N_ADMIN)POST /api/v2/razadmin/members/<who>/addons/remove acc E_INTENT E_METHOD E_NOT_FOUND E_ORIGIN E_PROVIDER{addon, note?}: remove the newest of that add-on from the account (one bought ends at the provider too): the Architect's (add-ons, plans) or a project admin's (its level, its settings), in their own session or by their agent on their clear intent (D2-Intent) or their Allow once (D2-Override); without it an agent is refused (admin.*: person); audited A_ADMIN, the account's owner told (N_ADMIN)POST /api/v2/razadmin/members/<who>/plan acc E_INTENT E_METHOD E_NOT_FOUND E_ORIGIN{plan, until?, note?}: a plan's bundle (Settings:Quotas plans, yearly or monthly) on the account, a grant, never a payment; with until (2027-03-01) it goes then: the Architect's (add-ons, plans) or a project admin's (its level, its settings), in their own session or by their agent on their clear intent (D2-Intent) or their Allow once (D2-Override); without it an agent is refused (admin.*: person); audited A_ADMIN, the account's owner told (N_ADMIN)POST /reinstate acc E_LOCKED E_LOGIN E_ORIGIN E_TOO_BIG{email, password, text (1–2000)}: from the hold page a suspended person asks to be reinstated (the password again, else E_LOGIN; one a day, E_RATE) → a support message to the Architect marked reinstate; his reply shows on the hold page at their next sign-in. Audited U_REINSTATE_ASKGET /api/v2/agents/stamp agents{stamp} of the project's agent board and your messages: the Agents page and the pipe map sync on itGET /api/v2/projects/available?name= acc E_CERT_LIMIT E_QUOTA{available} or {available: false, code, reason}GET /api/v2/projects acc{scope, total, data: [{project, url, level, plan, role, tags}]}: tags are the project's own (^myp-1); an all-projects AI token lists its person's own projects; a project token, just its ownPOST /projects acc E_LEVEL E_ORIGIN E_QUOTA E_TOO_BIG E_UNCONFIRMEDthe New project form {name, level, view?}: makes the project and hands the person off to its home, signed in (^sso-7); a person whose email isn't confirmed makes one Hero project and nothing more, else the page again with confirm your email first, 403 E_UNCONFIRMED (^d2land-5); master needs the Monthly plan (E_LEVEL)POST /api/v2/projects {name, level?, view?, tags?} acc E_QUOTA E_UNCONFIRMEDmake a project, as the form does: a person's session (→ 201 {project, url, tags, go}: the one-time hop to its home), or an agent of an all-projects maker token for its person (→ 201 {project, url, tags}; any other token E_SCOPE). tags: the owner's tags on it, normalised as PUT /api/v2/projects/<p>/tags does (lower-case, no #, no repeats, at most 20; else E_ARG and nothing is made). level hero, creator, pro or master (master needs the Monthly plan, E_QUOTA); the week's addresses and the owned-projects quota are E_QUOTA too; a person whose email isn't confirmed makes one Hero project and nothing more, else 403 E_UNCONFIRMED (^d2land-5)POST /d2admin/level acc E_LEVEL E_LEVEL_ROLE E_ORIGIN E_TOO_BIG E_UNCONFIRMED{level}: the project's level, from Settings › Project's Upgrade and Level (an admin; a session's form); moving it up needs the admin's email confirmed, else the page again with confirm your email to upgrade, 403 E_UNCONFIRMED (^d2land-6); lowering is not gated; master needs its owner on the Monthly plan (E_LEVEL). On Creator a maker token's every call answers 403 E_LEVEL_ROLE {details: {level, role: maker, needs: designer}}, naming the doors (P-1656)POST /api/v2/razadmin/projects/<name>/level acc E_INTENT E_LEVEL E_METHOD E_NOT_FOUND E_ORIGIN E_UNCONFIRMED{level}: the project's level, as /d2admin/level (moving up needs the caller's email confirmed, E_UNCONFIRMED; the owner holding the level's add-on, else E_LEVEL): the Architect's (add-ons, plans) or a project admin's (its level, its settings), in their own session or by their agent on their clear intent (D2-Intent) or their Allow once (D2-Override); without it an agent is refused (admin.*: person); audited A_ADMIN, the account's owner told (N_ADMIN)PUT /api/v2/razadmin/projects/<name>/settings acc E_AI_ACCESS E_FORBIDDEN E_INTENT E_LOCKED E_METHOD E_NOT_FOUND E_ORIGIN E_QUOTA E_SECRET E_SETTING E_UNCONFIRMED{text}: the project's Settings:Project, published (its whole text; never one of base d2's Settings:System, Quotas or Permissions): the Architect's (add-ons, plans) or a project admin's (its level, its settings), in their own session or by their agent on their clear intent (D2-Intent) or their Allow once (D2-Override); without it an agent is refused (admin.*: person); audited A_ADMIN, the account's owner told (N_ADMIN)POST /api/v2/project/release ops E_FORBIDDEN{version}: a new version of this project (admins)POST /api/v2/project/onboarding-end accthe onboarding's end, by the project's admin or their AI: a Pro project's home becomes its dashboard, unless it has one of its own → {home, set}POST /api/v2/project/share {email} acc E_EMAIL E_EXISTS E_LEVEL E_NO_APPShare (Hero and Creator): the owner or an admin, in their session, invites a person to the whole project as an app user, with the invite mail → 201 {email, mailed, why?, message?}; not mailed to an account whose email isn't confirmed (^mail-12); a member already E_EXISTS 409; a project with no app yet E_NO_APPPATCH /api/v2/project {join, joinKind?} accwho may join: request, open or invite (closed, a new project's default), and joinKind, the kind open join gives (builder or app-user, app-user unless set); the owner or an admin, in their session or through their AI token → {join, joinKind}POST /api/v2/onboard/roles {roles, days?} accStart your agents: roles of designer, builder, dispatcher, tester → 201 {links: [{role, id, link, expires}], vault}: a one-time link per role (an hour), serving its start prompt; each role token kept in the vault as d2-<role>, the dispatcher allowed d2-designer and d2-builder. The person's session or a signed d2 page; never an AI tokenPOST /api/v2/onboard/setup {kind} acc E_FORBIDDENa setup link: kind harness-solo, harness-full or app-loop → 201 {line, url, expires, roles, kind}: one link (an hour) bundling a role link per role (harness-full: monitor, dispatcher, builder, designer; else the level's designer or maker), served by GET /setup/<id>; the owner or an admin in their own session or a signed d2 page (a builder: harness-solo and app-loop, for their own AI), never a token. Kind connect → 201 {line, url, expires, kind}: Connect your AI's paste line, the invite Done / New AI invite makes (10 minutes, ending their unused one), for any member's own AIDELETE /api/v2/project acc E_CONFIRM E_FORBIDDEN E_NOT_FOUND E_PROTECTED E_SUPPORT_LINE{name}: delete this project and all its data, its name typed to confirm (its owner or an admin, in their session; never a token, never a built-in project)GET /api/v2/auth acc{loggedIn, tokenNeeded}: the write token's cookiePOST /signup acc E_BOTthe sign-up form {email, password, password2, next, cf-turnstile-response}: with the box's Turnstile keys set, the bot check runs first — no token, a refusal or Cloudflare unreachable is the page again, 400 E_BOT, no account; a user-role token in Authorization skips it (A_BOT_SKIP)POST /login/forgot acc E_ORIGIN E_TOO_BIGForgot password? {email} (^mail-6): the same page whatever is typed — If there's an account with that email, a link is on its way (good for an hour); a link is mailed to the account's email, confirmed or not, never to a suspended account, at most 3 an hour (the Architect's own unthrottled); a link not mailed is logged (info, kind mail) with whyGET|POST /login/confirm-again acc E_METHOD E_ORIGIN E_TOO_BIGDidn't get the confirm mail? {email} (^mail-5; form or JSON): the same answer whatever is typed — If there's an account with that email waiting to be confirmed, a new confirm link is on its way (JSON: {ok, said}); a fresh confirm link is mailed only to an account that exists, isn't confirmed and isn't suspended, at most 3 an hour; a link not mailed is logged with whyGET|POST /login/reset/<link> acc E_GONE E_METHOD E_NOT_FOUND E_PASSWORDSet a new password (^mail-6): GET the form; POST {password, password2} sets it once, within mail.resetTtl [1 h] (24 h for one the Architect hands over), confirms the email if it wasn't, ends every session of the person and logs them in here (303 /home); a used or expired link 410 E_GONE, an unknown one 404 E_NOT_FOUNDGET|POST /account/confirm/<link> acc E_GONE E_METHOD E_NOT_FOUNDa confirm-your-email link (^mail-5, ^mail-16): GET shows the address and Confirm my email, and confirms nothing; the POST (same origin) confirms the email it was mailed to, once, within mail.confirmTtl [7 days]; a used link is 200, confirmed; expired 410 E_GONE, unknown 404, both with Send me a new link. Until it's confirmed d2 mails the account nothing but its account mailsPOST /api/v2/me/confirm acc E_METHODSend the link again (^mail-5, ^ui-mail-2): in your own session (any token E_SCOPE) → {confirmed, mailed, hint?}: a new confirm-your-email mail, replacing the unused link; at most 3 an hourPOST /api/v2/auth/login acc E_CONSENT E_MOVED E_PASSWORD E_SUSPENDEDretired (P-434): 410 E_MOVED — a browser logs in at /login, a token goes in each call's Authorization headerPOST /api/v2/auth/logout accclears itGET /api/v2/tls-ask?domain= opsfor the TLS proxy: 200 only for our namesPOST /api/v2/tester/grant {account, plan?, addons?} acc E_METHOD E_TOO_BIGa tester sets the plan (free, yearly, monthly) and add-ons (addon-masterprojects: Master projects without the Monthly plan) of a test account its person made, no payment and no billing record → {account, plan, addons}; anyone else's account, or any other token, E_SCOPE (^tst-13)POST /api/v2/tester/cleanup {run?, all?, account?, project?, keep?, askedBy?} acc E_TOO_BIGdeletes the test accounts (with their tokens and the projects they own), the d2test- projects and the mail in the box's mailbox that its person's tester made: this run's by default; another run's or all of them (all: true) only with askedBy naming its person; account / project: one of them; keep: true marks the run's kept, so the daily housekeeping deletes them a day after the keep; either way the run ends and its tester grants go (free plan) → {deleted: {accounts, projects, mails, failed, mailError?}} or {kept} (^tst-7, ^tst-13)GET /api/v2/mail?box=inbox|sent&to= acc E_MAILthe box's mail to this run's test addresses: inbox read from the box's own mailbox over IMAP (its accounts' addresses, plus to= one of the run's) → {box, total, data: [{id, from, to, subject, at}]}; sent, the mail log's rows → [{to, kind, subject, at, ok, messageId?, error?}] (messageId: the Message-ID d2 wrote); Mailer off or the mailbox refusing: 503 E_MAIL with why (^tst-12)GET /api/v2/mail/<id> acc E_MAIL E_NOT_FOUNDone mail of the inbox: {id, from, to, subject, at, text, links, messageId, auth}, auth the receiving server's Authentication-Results as {spf, dkim, dmarc} each pass | fail | none (the raw line when it names none of them); a mail to another run's address E_SCOPEPOST /api/v2/mail {to, subject, text} acc E_MAIL E_TOO_BIGsends one mail through the Mailer, from the box, to one of this run's test addresses only (else E_SCOPE) → 201 {queued} (or queued: false with why)POST /api/v2/broadcasts support{title (≤80), body (markdown subset ≤2000: paragraphs, **bold**, *italic*, links to https:// or a path), level?: info|warning, until?}: a draft → 201 {id, preview}; nobody is told yetGET /api/v2/broadcasts support E_NOT_FOUND E_STATUSevery broadcast, newest first; /<id> one; PATCH /<id> changes a draft (E_STATUS once sent); DELETE /<id> drops a draftPATCH /api/v2/broadcasts/<id> {…} support E_NOT_FOUND E_STATUS{title?, body?, level?, until?}: changes a draft → {id, preview}; E_STATUS once sentDELETE /api/v2/broadcasts/<id> support E_NOT_FOUND E_STATUSdrops a draft → {deleted}; E_STATUS once sentPOST /api/v2/broadcasts/<id>/send support E_NOT_FOUND E_STATUSone priority N_BROADCAST per account, behind notice.broadcast (his AI: asks); /withdraw takes it off everyone's Noticescurl -s https://dieselwebos.com/api/v2/diesel/run \\
-H 'content-type: application/json' -H 'authorization: Bearer $D2_TOKEN' \\
-d '{"spec":"$when hello (who) => (payload = \\"hi \\" + who)","story":"$send hello (who = \\"d2\\")\\n$expect payload == \\"hi d2\\""}'
Reads and diesel/expr are open; other writes (PUT, POST, DELETE) need a bearer token. Log in, sign up, members, tokens and the account pages are HTML forms (the pages; see Help). Admin API (/admin/*: status, logs, deploy, restart, rollback) is separate and token-only.