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.