Skip to content
Pilotoo
v0.1

HTTP API

Brain serves JSON over HTTP. A browser calls it with the session cookie; an agent calls POST /mcp with Authorization: Bearer pb_.... Request and response shapes are the zod schemas of @pilotoo/memory-protocol.

Every failure is { "error": { "code": "...", "message": "..." } }.

Status code When
400 invalid The body or query does not match its schema.
401 unauthenticated No session, or a token that matches nothing.
403 forbidden You can see the memory but may not do this.
404 not_found The thing does not exist, or you cannot see it. A hidden memory answers 404, never 403, so its existence does not leak.
405 method_not_allowed GET or DELETE on /mcp.
409 conflict The change clashes with existing state.
413 too_large An upload over 20 MiB.
415 unsupported_media_type A file type that cannot be read.
500 internal A defect; the message says nothing about its cause.

Creating routes answer 201, deletes and decisions answer 204, and the rest answer 200.

Route Purpose
GET /api/session {user, signIn}. user is null when signed out. Needs no session.
GET /auth/google/start, GET /auth/google/callback The Google sign-in.
POST /auth/logout Ends the session (204).
GET /health {status: "ok"}.
GET /api/people The people of the workspace.
Route Purpose
GET /api/memories The memories you can see, each with myRole and counts.
POST /api/memories {kind, name, description, access, language?, schema?, labeling?, models?}. models is {extract?, compile?, answer?, embedding?}, each a model id of GET /api/models (embedding may be none); a field a kind does not use is refused. See Models. labeling is auto (the default: Brain labels passages on its own and asks when unsure) or ask (every proposal waits in Review). language is source (the default: each page in the language of its sources) or a two-letter code such as it that fixes it. schema is {about, types, generic} and only a graph memory takes one; without it the memory gets the company starter. See Schema.
GET, PATCH, DELETE /api/memories/:mid Read, rename, change access, language, labeling, models (the whole setting is replaced) or schema, delete (with everything it owns). A schema that removes a type which has pages answers 409 unless retype maps that type’s key to a key of the new schema.
POST /api/memories/:mid/reembed Memory admins only, memories that have an embedding model: queues the job that embeds the passages the current model has not embedded. Answers the memory; its embedding is {model, pending, passages}.
GET /api/models Any signed-in person: the models a memory may choose, [{id, label, kind: "chat" | "embedding", dimensions?, runs?, available}]. runs is worker for a model that only a worker answers; it is available while one that serves it was seen in the last minute. Never a credential.
POST /api/memories/:mid/relabel Memory admins only, graph memories: the model labels the passages of every current document again. Answers {sources}. Passages a person labelled are skipped and nothing is compiled again.
GET, POST /api/memories/:mid/members List, or add {userId, role: "admin" | "member"}.
DELETE /api/memories/:mid/members/:uid Remove a member.
PUT /api/memories/:mid/members/:uid/roles {roleIds}: the label roles of a member.
Route Purpose
GET /api/memories/:mid/sources?q=&label=&cursor= A page of sources: {items, nextCursor}. label matches the sources with a passage the reader can read that has that label, and only a label the reader’s roles grant. A source’s passages counts the passages the reader can read, and ownLabels is true when a person or a labeling rule gave the document labels of its own.
POST /api/memories/:mid/sources A multipart file with optional comma-separated labels and externalId, or JSON {title, text, labels?, externalId?}.
GET, DELETE /api/memories/:mid/sources/:sid Read or delete a source. Deleting retires what only that source supported and rebuilds what other sources still support.
POST /api/memories/:mid/sources/:sid/erase Memory admins only: remove the source and everything derived from it, history and revisions included (204).
POST /api/memories/:mid/sources/:sid/compile Memory admins only: compile it again; answers the source.
PUT /api/memories/:mid/sources/:sid/labels {labelIds}: the labels of the document. Its passages that the document labelled take them; passages a person or Brain labelled keep theirs.
PUT /api/memories/:mid/sources/:sid/passages/labels Memory admins only: {locator, labelIds} labels one passage. A locator names exactly one passage, and no later labelling overwrites the labels. Answers the source.
POST /api/memories/:mid/sources/:sid/relabel Memory admins only: the model labels this document’s passages again. Answers {sources: 1}.

The text of a source (GET /api/memories/:mid/sources/:sid/text) lists its passages with labelIds, the labels of the passage that the reader’s roles grant, and labeledBy, who set them (document, model or person; null while nobody has, and for a passage the reader cannot read), and carries hidden: true when the reader may not see every passage, which are then replaced by █ of the same length so offsets stay valid. A source lists the union of its passages’ labels, a page fact the labels of the passages it cites and mentions, the pages it is about as {id, title} (only pages the reader can see), and a search result the labels of its passage, all for display only.

A source is reading, ready or failed, and carries its origin (upload, text, agent or connector).

externalId is your own stable id for the document, up to 200 characters. Adding the same id with the same content changes nothing; with different content it creates the next version, and the source then carries previousVersionId or supersededBy.

Route Purpose
POST /api/memories/:mid/search {query, limit?, as?} returns {passages}.
POST /api/memories/:mid/ask {question, as?} returns {answer, citations, modelOff?}.
GET /api/memories/:mid/pages?type=&q=&as= Pages of a graph memory.
GET /api/memories/:mid/pages/:pid?as=&history=1 One page with sections (its facts), citations, related pages and conflicts. Each conflict carries citations, the passages both sides stand on, in the shape of a fact’s. history=1 also returns retired facts, with invalidAt, invalidReason and supersededBy; admins only.
GET /api/memories/:mid/graph?center=&depth=&as= Nodes (page, source) and edges (link, cites). depth is 0 to 4.

as is a user id: an admin sees the result as that person, and the use is audited. limit is 1 to 50, default 8.

All of these are admin-only, audited, and undoable through the audit log.

Route Purpose
POST /api/memories/:mid/pages/:pid/merge {intoPageId}: merge the page into another. Answers the target page; the old id keeps answering it.
POST /api/memories/:mid/pages/:pid/split {factIds, intoPageId} or {factIds, newTitle, type?}: move facts to an existing or a new page. Answers {fromPageId, intoPageId}.
PATCH /api/memories/:mid/pages/:pid {title?, locked?}: rename (the old title stays an alias) or lock.
POST /api/memories/:mid/pages/:pid/facts {text, labelIds?, mentions?}: add a fact by hand. mentions are the ids of the other pages it is about; a link between two pages exists while a live fact mentions the other page.
PATCH /api/memories/:mid/pages/:pid/facts/:fid {text?, locked?, mentions?}: edit, lock or change the mentions of a fact. A locked page or fact is not rewritten by a compile.
GET /api/memories/:mid/health {duplicates, orphans, emptyPages, stale} across the memory.
Route Purpose
GET, POST /api/memories/:mid/families Label families.
GET, POST /api/memories/:mid/labels Labels {familyId, name, description?}. description is what the label means, up to 500 characters; Brain reads it to choose labels.
PATCH /api/memories/:mid/labels/:lid {name?, description?}: give at least one.
DELETE /api/memories/:mid/families/:fid, DELETE /api/memories/:mid/labels/:lid Delete one.
GET, POST /api/memories/:mid/roles Roles {name, grants: [{familyId, mode, labelIds}]}, mode being none, some or all.
PATCH, DELETE /api/memories/:mid/roles/:rid Change or delete a role.
GET /api/memories/:mid/roles/:rid/preview {visibleSources, totalSources} for a role.
GET /api/memories/:mid/review?type= Review items, type being passage-labels, conflicts, pages, duplicate or orphan. A passage-labels item is one document: sourceId, sourceTitle and passages, each with locator, textStart, the proposed labelIds and a reason of proposal, wrong-label or unsure.
POST /api/memories/:mid/review/:id {decision: "accept" | "reject" | "edit", labelIds?, intoPageId?}. On a passage-labels item, accept gives every passage the proposed labels and reject gives each the labels of the document, or none. On a duplicate, accept merges (intoPageId picks the direction) and reject keeps the pages apart.
POST /api/memories/:mid/review/:id/passages {decisions: [{locator, decision: "accept" | "reject" | "edit", labelIds?}]}: decide single passages of a passage-labels item; edit needs the labels to give. The item goes when no passage is left (204).
Route Purpose
GET, POST /api/memories/:mid/checks Checks {asUserId, question, mustSourceIds, mustNotSourceIds}.
POST /api/memories/:mid/checks/run Run them: [{checkId, pass, leaked, missing}].
GET /api/memories/:mid/audit?cursor= The audit log: who, through the web or a token, what and its effect. An entry about the labels of a document carries document, labelIds and passages, the number of passages it covers.
POST /api/memories/:mid/audit/:id/undo Undo an event that is undoable.
GET /api/memories/:mid/connectors {connectors, rules}.
GET /api/memories/:mid/fed-by {connectors}: the connectors writing here; memory admins.
PUT /api/memories/:mid/fed-by/:cid {pausedHere}: stop or resume one connector for this memory only.
Route Purpose
GET /api/connect/link {available, linked, email?, linkedAt?} for the signed-in person.
GET /api/connect/link/start Redirects to Connect’s consent page; callback brings the person back to /#/connectors.
DELETE /api/connect/link Forget the stored Connect token.
GET /api/connectors/kinds The steps the editor offers, including one per tool of your Connect connections.
GET /api/connectors/templates {templates}: workflows a new connector can start from.
POST /api/connectors/validate {definition} → {issues, ports}.
GET, POST /api/connectors List the connectors you can see, or create one {name, definition, template?}; it starts off.
GET, PATCH, DELETE /api/connectors/:id Read, change {name?, definition?, enabled?, dailyCap?} or delete; changes are the owner’s.
POST /api/connectors/:id/test Run without writing anything; the run lists what it would add.
POST /api/connectors/:id/run Run now.
GET /api/connectors/:id/runs {runs}: the latest runs with steps and items.
Route Purpose
GET, POST /api/memories/:mid/facts List, or add {text}. The answer carries operation: add, update (an older fact was rewritten), delete (a contradicted fact was retired) or noop (already known), and replacedFactId when one is replaced.
PATCH, DELETE /api/memories/:mid/facts/:fid Edit or delete.
POST /api/memories/:mid/facts/:fid/keep Keep a fact that was waiting (pending). If it carries replaces, keeping it retires the fact it replaces.
POST /api/memories/:mid/facts/:fid/share {memoryId}: copy it as a source into a graph memory, where it waits in review.
GET, PUT /api/memories/:mid/write-policy {policy: "auto" | "ask" | "off"}: how agent writes are handled.
Route Purpose
GET, POST /api/tokens List, or create {name, grants: [{memoryId, access, until?}]}. access is read, write, remember, connectors or configure; until (a time) ends a connectors or configure grant, which only an admin of the memory may give. The secret is returned once.
PATCH /api/tokens/:id The owner, from the web: change {name?, grants?} under the same checks as creation. The secret and the last use stay. A change applies from the agent’s next call.
DELETE /api/tokens/:id Revoke.
POST /mcp The six memory tools, the workflow tools with a connectors grant and the configuration tools with a configure grant. The only route that accepts a pb_ token. See Use it from an agent.
Route Purpose
GET, POST /api/worker-tokens A workspace admin, from the web: list, or create {name, memoryIds?}. Without memoryIds the token serves every memory. The secret (pbw_) is returned once.
DELETE /api/worker-tokens/:id Revoke. The worker stops at its next request.
POST /api/workers/claim A worker token only. {models, waitSeconds?}. Waits up to 25 seconds and returns {call}: one call of a model in models and a memory the token serves, oldest first, with its lease, or {call: null}.
POST /api/workers/calls/:id/extend A worker token only. {lease} holds the call a little longer.
POST /api/workers/calls/:id/result A worker token only. {lease, output} or {lease, error}. A lease that is not the worker’s, or ran out, answers 404 and changes nothing.

A worker token is accepted on the three worker routes and on no other. See Workers.