The documented, standardized API — every named route in api_routes with its method, declared params and a response shape derived from its SQL, plus the generic ?api=<table> CRUD layer and the admin denylist. Rendered through the <<<ApiReference>>> tag.
The data-driven API surface — 62 named routes, each a row in api_routes. Call one as ?route=<name>: it runs that row's parameterized SQL with your declared :params bound. A SELECT route replies {"route":…,"rows":[…]}; a write route replies {"route":…,"affected":n,"rowid":id}. auth is public for reads, token/admin for writes.
| route | method | auth | params | response shape | meta |
|---|---|---|---|---|---|
binding.list | GET | public | {"ticket_id":"int"} | {"route":"binding.list","rows":["clause_seq", "guarantee_label"]} | list a ticket's declared guarantee-label -> clause bindings |
binding.record | POST | token | {"ticket_id":"int","clause_seq":"int","guarantee_label":"str","digest":"str"} | {"route":"binding.record","affected":<n>,"rowid":<id>} | declare that a shadow guarantee label satisfies a mandate clause (applied by crankd at harvest) |
guarantee.held | GET | public | {"component":"str"} | {"route":"guarantee.held","rows":["guarantee"]} | every guarantee label a component has recorded — the contract crankd will not let a crank erode |
guarantee.list | GET | public | {"ticket_id":"int"} | {"route":"guarantee.list","rows":["version", "shadow", "guarantee", "clause_seq"]} | list a ticket's harvested guarantees and the clauses they bind to |
guarantee.record | POST | token | {"ticket_id":"int","version":"str","shadow":"str","guarantee":"str","clause_seq":"int","digest":"str"} | {"route":"guarantee.record","affected":<n>,"rowid":<id>} | record one harvested shadow guarantee, attributed to a ticket and bound to a mandate clause |
mandate.list | GET | public | {"ticket_id":"int"} | {"route":"mandate.list","rows":["seq", "clause", "digest"]} | list a ticket's mandate clauses in order |
mandate.record | POST | token | {"ticket_id":"int","seq":"int","clause":"str","digest":"str"} | {"route":"mandate.record","affected":<n>,"rowid":<id>} | record one falsifiable clause of a ticket's mandate |
ticket.approval | GET | public | {"component":"str"} | {"route":"ticket.approval","rows":["id", "component", "title", "body", "patch", "status", "approval_sig"]} | the latest ticket for a component (content + patch + approval_sig) for crankd to verify + apply |
ticket.approve | POST | token | {"id":"int","approval_sig":"str"} | {"route":"ticket.approve","affected":<n>,"rowid":<id>} | attach a wallet signature over the ticket content digest -- the external approval the agent cannot forge |
ticket.assign | POST | token | {"ticket_id":"int","agent_id":"str","role":"str"} | {"route":"ticket.assign","affected":<n>,"rowid":<id>} | authorize a REGISTERED agent on a ticket in a role (idempotent: re-assign is a no-op, never an error) |
ticket.create | POST | token | {"component":"str","title":"str","severity":"str","body":"str","patch":"str"} | {"route":"ticket.create","affected":<n>,"rowid":<id>} | file a crank ticket with its change (patch); status REQUESTED, approval is a separate wallet signature |
ticket.crew | GET | public | {"ticket_id":"int"} | {"route":"ticket.crew","rows":["ts", "agent_id", "role", "status"]} | the agents authorized on a ticket, with roles |
ticket.journal | GET | public | {"ticket_id":"int"} | {"route":"ticket.journal","rows":["ts", "agent_id", "event", "status", "note", "steps_remaining"]} | the full append-only journal for a ticket (status changes + progress reports) |
ticket.kind | POST | token | {"kind":"str","id":"int"} | {"route":"ticket.kind","affected":<n>,"rowid":<id>} | classify a ticket (set-once: the identity trigger refuses re-kinding; must exist in ticket_kinds) |
ticket.lesson | POST | token | {"ticket_id":"int","agent_id":"str","note":"str"} | {"route":"ticket.lesson","affected":<n>,"rowid":<id>} | harvest a settled ticket: append the distilled lesson (attributed; append-only like all events) |
ticket.manifest | GET | public | {"ticket_id":"int"} | {"route":"ticket.manifest","rows":["uid", "ts", "kind", "ref", "note"]} | the ticket as a descriptive manifest: every UID'd item it authorized |
ticket.progress | POST | token | {"ticket_id":"int","agent_id":"str","note":"str","steps_remaining":"int"} | {"route":"ticket.progress","affected":<n>,"rowid":<id>} | append an attributed progress report (+ steps until the goal); the journal never edits |
ticket.queue | GET | public | {} | {"route":"ticket.queue","rows":["id", "kind", "status", "component", "title", "body"]} | the dispatch queue: open/requested tickets, oldest first (brake state checked per-ticket via ticket.stops) |
ticket.stops | GET | public | {"ticket_id":"int"} | {"route":"ticket.stops","rows":["active_stops"]} | active (unreleased) stop tickets aimed at this ticket or any ancestor — >0 means HALT |
| route | method | auth | params | response shape | meta |
|---|---|---|---|---|---|
agent.prompt | GET | public | {"agent_id":"str"} | {"route":"agent.prompt","rows":["agent_id", "parent", "tools", "status", "ts", "prompt"]} | one agent's full birth prompt + registration record |
agent.register | POST | token | {"agent_id":"str","parent":"str","prompt":"str","tools":"str"} | {"route":"agent.register","affected":<n>,"rowid":<id>} | record an agent spawn: id + parent + prompt() + tools() |
agent.roster | GET | public | {"n":"int"} | {"route":"agent.roster","rows":["agent_id", "parent", "status", "ts", "prompt"]} | registered agents, newest first (LIMIT :n; omit n for all) |
agent.trail | GET | public | {"agent_id":"str"} | {"route":"agent.trail","rows":["ts", "tool", "intent", "note"]} | the propagation trail for an agent id (tool + intent events, in order) |
artifact.list | GET | public | {"ticket_id":"int"} | {"route":"artifact.list","rows":["name", "version", "forge_sha", "kind"]} | list the forge artifacts a ticket produced |
artifact.record | POST | token | {"ticket_id":"int","name":"str","version":"str","forge_sha":"str","kind":"str"} | {"route":"artifact.record","affected":<n>,"rowid":<id>} | reference a forge artifact (name:version + sha) a crank produced, against its ticket |
children.list | GET | public | {"parent_id":"int"} | {"route":"children.list","rows":["child_id", "kind", "title", "status", "component"]} | list a ticket's child tickets (the owned tree) |
edge.record | POST | token | {"parent_id":"int","child_id":"int","kind":"str"} | {"route":"edge.record","affected":<n>,"rowid":<id>} | record a parent->child ticket edge (the owned tree) |
journey.list | GET | public | {"ticket_id":"int"} | {"route":"journey.list","rows":["kind", "text", "ts"]} | list a ticket's journey (blunders and successes, in order) |
journey.record | POST | token | {"ticket_id":"int","kind":"str","text":"str"} | {"route":"journey.record","affected":<n>,"rowid":<id>} | append a journey event (success/blunder/pivot/note) to a ticket's history |
policy.active | GET | public | {"component":"str"} | {"route":"policy.active","rows":["id", "component", "max_version", "expires", "sig"]} | the newest standing policy for a component (for crankd to verify + apply bounds) |
policy.create | POST | token | {"component":"str","max_version":"str","expires":"int","sig":"str"} | {"route":"policy.create","affected":<n>,"rowid":<id>} | file a wallet-signed standing crank policy for a component (scope: max_version, expiry) |
run.get | GET | public | {"digest":"str"} | {"route":"run.get","rows":["id", "ts", "component", "version", "ticket_id", "authorized_by", "ok", "shadow_output", "digest"]} | fetch crank run(s) by the content address of their shadow output |
run.recent | GET | public | {"component":"str","n":"int"} | {"route":"run.recent","rows":["id", "ts", "version", "ticket_id", "authorized_by", "ok", "digest"]} | list a component's recent crank runs (metadata; use run.get for the full output) |
run.record | POST | token | {"component":"str","version":"str","ticket_id":"int","authorized_by":"str","ok":"int","shadow_output":"str","digest":"str"} | {"route":"run.record","affected":<n>,"rowid":<id>} | persist a crank run with its full shadow output (content-addressed by sha256(shadow_output)) |
| route | method | auth | params | response shape | meta |
|---|---|---|---|---|---|
doc.list | GET | public | {"repo":"str"} | {"route":"doc.list","rows":["path", "kind"]} | list a repo's documentation files |
doc.read | GET | public | {"repo":"str","path":"str"} | {"route":"doc.read","rows":["body", "kind"]} | read one repo documentation file body |
doc.record | POST | token | {"repo":"str","path":"str","kind":"str","body":"str"} | {"route":"doc.record","affected":<n>,"rowid":<id>} | ingest a repo documentation file |
doc.repos | GET | public | {} | {"route":"doc.repos","rows":["repo"]} | list repos that have ingested documentation |
repo.list | GET | public | {} | {"route":"repo.list","rows":["name", "forge_remote", "head_commit"]} | list the registered repos |
source.commits | GET | public | {"repo":"str"} | {"route":"source.commits","rows":["commit_sha", "ts", "is_current", "forge_remote"]} | list a repo's source commits, newest first |
source.record | POST | token | {"repo":"str","commit_sha":"str","is_current":"int","forge_remote":"str"} | {"route":"source.record","affected":<n>,"rowid":<id>} | record a source commit for a repo |
| route | method | auth | params | response shape | meta |
|---|---|---|---|---|---|
authorize_process | POST | token | {"name":"str","body":"str","meta":"str"} | {"route":"authorize_process","affected":<n>,"rowid":<id>} | |
bug.list | GET | public | {"source":"str","severity":"str"} | {"route":"bug.list","rows":["*"]} | list bugs, optionally scoped by source and/or severity, ordered by severity |
bug.record | POST | token | {"bug_id":"str","source":"str","title":"str","severity":"str","location":"str","repro":"str","success":"str","fanout":"int","repos":"int","status":"str","pattern":"str","meta":"str"} | {"route":"bug.record","affected":<n>,"rowid":<id>} | upsert one bug (congruency curated repro or corpus consistent-bug pattern), keyed (source, bug_id) |
bug.stats | GET | public | {} | {"route":"bug.stats","rows":["facet", "value", "n"]} | bug counts grouped by severity and by source |
error.recent | GET | public | {} | {"route":"error.recent","rows":["id", "ts", "type", "message", "file", "line", "route", "sig"]} | recent fatal-monitor error_log rows, newest first |
error.stats | GET | public | {} | {"route":"error.stats","rows":["sig", "n", "last_ts", "message"]} | fatal counts grouped by signature |
job.get | GET | public | {"hash":"str"} | {"route":"job.get","rows":["hash", "ts", "spec"]} | fetch a content-addressed crank job by hash |
job.put | POST | token | {"hash":"str","spec":"str"} | {"route":"job.put","affected":<n>,"rowid":<id>} | store a content-addressed crank job (hash = sha256 of the canonical spec) |
kind.roster | GET | public | {} | {"route":"kind.roster","rows":["kind", "pattern", "worker_role", "adversary_role", "purpose"]} | the ticket kinds: pair (adversarial network) vs solo, with role names |
lessons.recent | GET | public | {"n":"int"} | {"route":"lessons.recent","rows":["ticket_id", "ts", "agent_id", "note"]} | harvested lessons across all tickets, newest first (LIMIT :n; omit n for all) — playbook input |
lifecycle.steps | GET | public | {"kind":"str","version":"int"} | {"route":"lifecycle.steps","rows":["seq", "step"]} | the enumerable lifecycle steps for a kind (versioned; steps-remaining counts down these) |
process_status | POST | token | {"status":"str","id":"int"} | {"route":"process_status","affected":<n>,"rowid":<id>} | |
processes.running | GET | public | {} | {"route":"processes.running","rows":["id", "component", "status", "updated"]} | |
search.facets | GET | public | {} | {"route":"search.facets","rows":["facet", "value"]} | distinct repo + kind facet values for the multi-axis search form |
search.record | POST | token | {"repo":"str","path":"str","chunk_seq":"int","section":"str","text":"str","dim":"int","model":"str","vector":"str"} | {"route":"search.record","affected":<n>,"rowid":<id>} | ingest one L2-normalized doc-chunk embedding into the vector store |
search.stats | GET | public | {} | {"route":"search.stats","rows":["model", "chunks", "docs"]} | vector-store coverage: chunk + doc counts per embedding model |
search.text | GET | public | {"q":"str","repo":"str"} | {"route":"search.text","rows":["repo", "path", "snippet"]} | keyword (full-text) search over the documentation corpus, optionally repo-scoped |
telemetry.recent | GET | public | {} | {"route":"telemetry.recent","rows":["id", "ts", "kind", "route", "detail", "ua", "sig"]} | recent front-end telemetry beacons (page views / events / client errors), newest first |
telemetry.record | POST | public | {"kind":"str","route":"str","message":"str","detail":"str"} | {"route":"telemetry.record","affected":<n>,"rowid":<id>} | front-end telemetry beacon: page views / interaction events land a front_telemetry row; client JS errors also log to error_log + auto-file a de-duped front-end bug (boot/monitor.php) |
tickets.recent | GET | public | {"status":"str","n":"int"} | {"route":"tickets.recent","rows":["id", "title", "severity", "status"]} | |
work.record | POST | token | {"ticket_id":"int","kind":"str","ref":"str","note":"str"} | {"route":"work.record","affected":<n>,"rowid":<id>} | mint a PERMANENT work UID into a ticket's manifest (returns nothing; read ticket.manifest) |
?api=<table> CRUDBeyond the named routes, every non-denylisted table is reachable through a generic CRUD layer (the fast internal path). Table and column names are validated against the schema; reads are public, writes need a token / admin session:
| call | does |
|---|---|
GET ?api=tables | list the reachable tables |
GET ?api=<table>[&<col>=<v>&order=<c>.<dir>&limit=&p=&per=] | filtered / paginated rows |
GET ?api=<table>&id=<pk> | a single row by primary key |
POST ?api=<table> | create a row from the JSON body (real columns only) |
PUT|PATCH ?api=<table>&id=<pk> | update a row by primary key |
DELETE ?api=<table>&id=<pk> | delete a single row by primary key |
These tables — the self-hosting archive plus the auth / key tables — are admin-only and never reachable through ?api= or generic writes:
code_blobs — admin-only, not in RESTcode_refs — admin-only, not in RESTdoc_blobs — admin-only, not in RESTdoc_refs — admin-only, not in RESTLogin_Password — admin-only, not in RESTUser_Group_Mappings — admin-only, not in RESTGroup_Privileges — admin-only, not in RESTapi_keys — admin-only, not in REST