Review
A Brain is either open or reviewed. In both modes an admission policy
decides each write — not the caller. On a reviewed Brain most writes wait
for a decision; your own edits and additive schema still land immediately;
destructive changes always wait; unbacked agent claims wait until you admit
them. On an open Brain the default is to land, with the same holds on
destructive writes. mode is a property of
the Brain, never of the caller.
rcp_019fWhat the read consumedcurl -X PATCH "$API/v1/brains/tenant:acme" \ -H "authorization: Bearer $NICIA_KEY" \ -H "content-type: application/json" \ -d '{ "mode": "reviewed" }'New Brains are open, because a Brain that waits on everything answers nothing
on its first day. Turn review on when the knowledge starts mattering — typically
when an agent you did not write starts producing it, or when a customer will see
the answers.
What changes
Section titled “What changes”Nothing about how you write. The same call returns a different status:
{ "data": { "change": { "id": "chg_44a1e0", "status": "staged", "createdAt": "2026-08-14T10:59:41Z" } }}Branch on change.status, always:
| Status | Meaning |
|---|---|
applied |
It landed. Readable now. |
staged |
Waiting for a decision. Not readable yet. |
rejected |
The Brain declined it. The request was fine; the content was not. |
A rejection is a 200. The request was well-formed and the Brain made a
decision — that is not an HTTP error.
A write only ever returns one of those three. A change you read back — from
GET /changes, or from the response to approve/reject — can also carry
partially_accepted (what a partial approve, below, leaves behind) or
superseded (a staged change whose target moved on before it was reviewed).
The ?status= filter on /changes still only selects the three admission
outcomes above.
Reads tell you the same thing from the other side: every /context response
carries staged, so an empty answer distinguishes “nothing known” from “waiting
on you.”
Settling the queue
Section titled “Settling the queue”curl "$API/v1/brains/tenant:acme/changes?status=staged" \ -H "authorization: Bearer $NICIA_KEY"{ "data": { "changes": [ { "id": "chg_44a1e0", "status": "staged", "author": { "kind": "key", "id": "key_7f3a1c", "name": "support-agent" }, "records": [ { "id": "bob-smith", "kind": "person", "before": { "role": "Head of Platform" }, "after": { "role": "VP Platform" }, "evidence": [ { "source": "zendesk:ticket:8821", "quote": "Bob Smith, now VP Platform, confirmed…" } ] } ], "createdAt": "2026-08-14T10:55:12Z" } ], "nextCursor": "eyJrIjoi…" }}Every staged change carries the diff and the passage it came from, so a reviewer is deciding about evidence rather than about a model’s assertion.
curl -X POST "$API/v1/brains/tenant:acme/changes/chg_44a1e0/approve" \ -H "authorization: Bearer $NICIA_KEY" \ -H "content-type: application/json" \ -d '{}'
curl -X POST "$API/v1/brains/tenant:acme/changes/chg_44a1e0/reject" \ -H "authorization: Bearer $NICIA_KEY" \ -H "content-type: application/json" \ -d '{ "reason": "Title is from a signature block, not a statement." }'Both take a JSON body, and {} is the one that approves the whole change. A
POST carrying no body at all is a 400, not a whole approve.
Approve part of a change by naming what you want:
-d '{ "records": ["bob-smith"], "fields": { "bob-smith": ["role"] } }'Naming part of a change is a decision about the rest of it, too: any record — or, within a named record, any field — you did not name is declined in this same call. A partial approve settles the whole change at once, so this is final: the producer has to write again for anything declined.
Edit before approving when the substance is right and the wording is not:
curl -X POST "$API/v1/brains/tenant:acme/changes/chg_44a1e0/approve" \ -H "authorization: Bearer $NICIA_KEY" \ -H "content-type: application/json" \ -d '{ "edits": { "bob-smith": { "role": "VP, Platform Engineering" } } }'The edit is attributed to you, not to the agent that proposed it.
Undoing a change that has already landed is a console action today, not an operation on this API surface. When it runs, it either restores the content or refuses the whole revert up front — it never marks a change reverted while leaving the content in place.
Who approved what
Section titled “Who approved what”Attribution is derived from the key that made the call — never from a field somebody filled in. “Who approved this” will be answered by the Brain, not by your logging.
Reviewing without a person
Section titled “Reviewing without a person”Review is a checkpoint, not necessarily a human. A key with the review
capability can settle changes, so you can put your own evaluator in front of the
queue and only escalate what it is unsure about.
const changes = `${API}/v1/brains/tenant:acme/changes`;const auth = { authorization: `Bearer ${NICIA_KEY}` };
// One page of the queue; walk the rest with `data.nextCursor`.const { data } = await fetch(`${changes}?status=staged`, { headers: auth,}).then((response) => response.json());
for (const change of data.changes) { const verdict = await myEvaluator(change); if (verdict.confident) { await fetch(`${changes}/${change.id}/approve`, { method: "POST", headers: { ...auth, "content-type": "application/json" }, body: "{}", }); } // leave the rest for a person}The Brain decides, not the caller
Section titled “The Brain decides, not the caller”mode sets the policy’s default outcome — it is not a per-write switch. A key
cannot ask for its writes to skip review, and no request body can either — the
Brain’s admission policy decides every write, whoever sent it. That is
deliberate: an outcome a caller can select is an outcome an agent can select.
So there is exactly one lever, and it is on the Brain. What each write does depends on its kind:
| Write kind | open |
reviewed |
|---|---|---|
| Destructive — schema, identity, delete | wait | wait |
| Human STAKE | land | land |
| Additive schema | land | land |
| Agent STAKE | land | wait |
| Everything else | land | wait |
“Wait” means the change stages until a human or a trusted service settles it;
“land” means it is admitted immediately and shows up in /changes. On
reviewed, most agent output waits — not all of it.
If you want a trusted pipeline writing straight in while agents are reviewed, give them separate Brains rather than separate trust levels inside one. A Brain is cheap, its handle is yours to choose, and two Brains with different modes say plainly what one Brain with per-caller exceptions only implies.
Notifications
Section titled “Notifications”Register a webhook so a staged change reaches your queue instead of waiting to be polled:
curl -X POST "$API/v1/brains/tenant:acme/webhooks" \ -H "authorization: Bearer $NICIA_KEY" \ -H "content-type: application/json" \ -d '{ "url": "https://ops.example/nicia", "events": ["change.staged"] }'See Webhooks for signature verification and delivery semantics.
- Receipts — proving what a read was shown.
- Handing out keys — who gets to write without review.