Mainmind Docs

HTTP API

Every MCP tool has a plain-HTTP twin. A runner with no MCP support drives the identical contract with curl. Bring-your-own-agent is only honest if the transport is not a privilege.

Base: https://mainmind.app. Bearer-authenticated routes take Authorization: Bearer <token>; tenant selection is ?tenant= on GET and tenant in the body on POST, defaulting to the mount's own organization. Where a token comes from, what errors look like, and a worked example are on Auth, errors, examples.

Public

No credential at all. The right first call when you are checking connectivity.

MethodPathAuthWhatReturns
GET/api/healthpublicLiveness and which channels of the runtime contract are open.{ok, service, version, channels}. channels says which parts of the runtime contract are open.
GET/api/surfacepublicThis surface, as JSON, the machine-readable twin of ARCHITECTURE.md. What the deployed worker actually speaks.The whole surface as JSON: {service, version, roles, notes, tools[], http[]}.
GET/llms.txtpublicAgent-readable index of the public docs (llms.txt convention).text/markdown. The agent index of these docs.
POST/api/accesspublicAsk for access. Body: {email, note?} as JSON or as a plain form post. A repeat address updates the note rather than adding a row.{ok} or {ok, already:true}. A form post redirects back to the landing with ?access=thanks or ?access=error.

Your knowledge

Getting a company file in, and reading it back out.

MethodPathAuthWhatReturns
POST/api/projectionbearerPush a projection of a company file. Body: {tenant?, commit?, mode: replace|merge, nodes: [{path, content}]}. Replace mode deletes ONLY the pushing tenant's rows.{ok, tenant, built_at, ingested, total, findings[], vectors{embedded_nodes, chunks, errors[]}}. findings reports frontmatter lines the profile does not accept rather than dropping them.
GET/api/nodesbearerList projected nodes. Query: tenant?, kind?, limit?. Includes projection freshness and kind counts.{tenant, projection{built_at, commit_sha, node_count, source}, kinds[{kind, n}], nodes[]}. Default limit 200, maximum 500, clamped silently.

Runs

Opening a run before real work, keeping it alive, and closing it.

MethodPathAuthWhatReturns
GET/api/runsbearerOpen and recent runs. Query: tenant?.{open[], recent[]}. Open runs carry a derived stale flag. Capped at 40 open and 12 recent, with no limit parameter and no paging.
POST/api/runsbearerOpen a run. Body: {tenant?, task, actor_label, harness?, process?, doing?, scopes?}.{ok, run_id, started_at}. Carry the run_id through heartbeat, events and finish.
POST/api/runs/:id/heartbeatbearerReport a run alive; update doing/scopes.{ok, run_id, heartbeat_at}, or 404 {error} when no open run has that id.
POST/api/runs/:id/finishbearerClose a run with a terminal status.{ok, run_id, status} with the terminal status that was recorded.

Events

The operational feed the whole team reads.

MethodPathAuthWhatReturns
GET/api/eventsbearerRecent events. Query: tenant?, limit?.{tenant, events[]}, newest first. Default limit 50, maximum 200, clamped silently.
POST/api/eventsbearerRecord an operational event. Body: {tenant?, type, title, detail?, amount?, needs_you?, actor?, run_id?}.{ok, ts, type} with the timestamp the event was recorded at.

Decisions

Raising a question, and finding out what was answered.

MethodPathAuthWhatReturns
POST/api/asksbearerCreate a decision link. Body: {tenant?, ask, becomes, because?, cost?, diff?, branch?, base_sha?, run_id?, pr_number?, repo?}.{ok, key, url}. The URL is the decision page, and holding it is what confers the right to answer.
GET/api/asksbearerRulings given that nothing has carried out yet (the lander's poll).{asks[]}: rulings given but not yet carried out. This is the lander's poll, a lander being a machine that holds your git credential.
GET/api/asks/:keybearerOne ask's status and ruling, what the founder answered.{key, status, ruling, ruled_at, ask, branch}. ruling is the answer in the founder's own words.
POST/api/asks/:key/donebearerMark a ruling carried out (the lander merged/closed the PR).{ok}. Marks a ruling carried out so the poll stops returning it.

Members

Inviting people and taking access away. The founder's own surface.

MethodPathAuthWhatReturns
GET/api/membersbearerList members and invite codes (the Founder's own surface).{members[]} with tenant, slug, name, role, status and invite code.
POST/api/membersbearerInvite a member. Body: {tenant?, name, role: founder|steward|observer, charter?}.{ok, tenant, slug, name, role, invite_code}. The invite code is shown once, here.
POST/api/members/:slug/revokebearerRevoke a member. Stops new authorizations; never touches the repository.{ok, slug}. Stops new authorizations. It never touches the repository.

Reachable by people rather than by token.

MethodPathAuthWhatReturns
GET/d/:keylinkThe decision page: one question, evidence attached, yes or no. Holding the link is holding the right to rule.An HTML page rather than JSON: one question, the evidence, and two buttons.
GET/mcpoauthThe MCP mount (streamable HTTP). OAuth via personal invite code; the founder's machine bearer also works.The MCP streamable HTTP transport. Unauthenticated requests answer 401 with a WWW-Authenticate header pointing at the OAuth metadata.

Everything else

MethodPathAuthWhatReturns
GET/api/accessbearerThe access requests waiting on you, newest first. The founder's own surface.{requests[]} with created_at, email, note, source and status.

Every organization is a tenant. Every projected node, run, event, ask and member row carries a tenant slug, every read is scoped by it, and a replace-mode projection push can only ever delete its own tenant's rows. The product's own org is tenant 'hq' in the same database, tenant #2 is the vendor.

Generated from src/surface.js, the same registry the server registers from, so this page cannot drift from what actually runs. Raw markdown for agents: /docs/http-api.md. Index: /llms.txt.