Receipts
Every read — /context, /search, /query — returns a receipt id:
The receipt snapshot includes branchId. For a branch read, this is the
branch’s immutable identity and the head served at read time. Pass
?branch=<branchId> to receipt listing, inspection, now, pinned-document,
and who-was-served reads when using a main-scoped key. A branch-scoped key is
limited to its own receipts, even if it guesses a receipt id from main.
rcp_019fWhat the read consumed{ "data": { "text": "Acme renewed at $120k for 24 months on 2026-03-14.", "citations": [ { "source": "hubspot:deal:412", "slug": "brain-evidence-0dd3e2449851d9ba41924c42bd7a61375a8b78050bfe2ca7192eb918d626236a", "quote": "Acme renewed at $120k for 24 months." } ], "staged": 0, "warnings": [], "receipt": "rcp_019ffefe", "truncated": false }}Log it next to whatever your agent said. It is the difference between “our model probably had the renewal note” and “here is the exact version of the renewal note it was given, at 11:02 on 14 August.”
Every serve is its own receipt, with its own time and consumer. The same request made twice, against the same state, returns two receipts, so a listing by time window and who was served count each read. Retrying one request after a dropped connection is a new read.
Fetching one
Section titled “Fetching one”A receipt records which key made the read — key — and that binding is what
makes it evidence rather than a log line anyone could have written. It never
changes, whoever reads the receipt back. Fetch a receipt with the Brain key
that made the read, or with a key that holds review or admin — your
management key does. Who was served needs review and hands
back receipt ids, so a reviewer can open the receipts it lists; an operator
auditing their own Brain is the reader receipts are for. Any other key gets a
404, the same answer a wrong id gets. Receipts outlive their Brain: after
DELETE /v1/brains/{handle} the management key still fetches the receipt (a
Brain key answers 404 once its Brain is deleted, as it does for every read). What changed since and the
text it pinned need a live Brain, and answer 404
once it is deleted.
curl "$API/v1/brains/tenant:acme/receipts/rcp_019ffefe" \ -H "authorization: Bearer $NICIA_BRAIN_KEY"{ "data": { "receipt": { "id": "rcp_019ffefe", "kind": "context", "consumerId": "user:4471", "key": { "id": "key_7f3a1c", "name": "Acme web app" }, "snapshot": { "brainId": "brain_3a91c7", "generation": "6f1e4d6b-9f0c-4a21-8f3e-1f1a2b3c4d5e", "stateVersion": "sv_019ffefe", "schemaVersion": "nicia-base", "policyVersion": "pol_nicia_open" }, "committedAt": "2026-08-14T11:02:44Z", "items": [ { "source": "hubspot:deal:412", "version": 2, "status": "active" } ] } }}items pins the exact version of every source. A document of a named
source also carries document: { source, id }. If the
document has been rewritten twice since, or retired, the receipt still names the
version that was served, and the key that made the read can still fetch the
text that version held.
kind says what the read was: context, search, query, plan (a
snapshot plan page), or document (a read of the
text an earlier receipt pinned).
What changed since
Section titled “What changed since”Same scoping: the key that made the read, or a key with review or admin.
curl "$API/v1/brains/tenant:acme/receipts/rcp_019ffefe/now" \ -H "authorization: Bearer $NICIA_BRAIN_KEY"{ "data": { "changed": [ { "source": "hubspot:deal:412", "shownVersion": 2, "currentVersion": 4, "status": "superseded" } ], "unchanged": 3, "deleted": 0, "evidence": [], "answers": [] }}This answers the question that actually comes up in an incident: the agent told a customer $120k — was that wrong at the time, or has it changed since? Here it was right when it was said.
changed[] names versions rather than text: currentVersion is the version
the Brain serves now, and a document that was retired reports deleted. The
text the receipt pinned is its own read. No read
returns a document’s current text on its own — GET /documents/{id} reports its
status and version, and /context and /search serve its passages. Re-sending
a document under the same id counts — it replaces the document, and every
receipt that cited the old version reports superseded from that moment. A cited source can also report held: under a legal hold,
every item a receipt cited reports held rather than superseded or
unchanged, so a hold is never mistaken for “nothing changed.”
An answer whose grounds moved since
Section titled “An answer whose grounds moved since”A version number can stay the same while what it rests on moves. For every
standing answer a
receipt served, answers[] compares the validity it was served with (as the
receipt recorded it) with its validity now. since is one of:
grounds_moved: something the served version rests on moved after the read. This includes an unchanged version whose claim changed, whose quoted source text changed, whose source was retired, or that gained new evidence.movedSincenames each reason.replaced: a new version was accepted.unchanged: nothing monitored moved after the read. An answer served while alreadyneeds_reviewstaysunchangedif nothing further moved.unknown: the receipt recorded no validity (it predates validity), or the check cannot tell now.
It reports what changed after the read, never that the answer was wrong when it was served.
{ "data": { "changed": [], "unchanged": 2, "deleted": 0, "evidence": [], "answers": [ { "id": "refund-answer", "shownVersion": 1, "currentVersion": 1, "since": "grounds_moved", "servedValidity": { "state": "current", "reasons": [], "affirmed": { "position": 9, "at": "2026-09-28T09:00:00.000Z" }, "monitored": { "claims": ["refund-window"], "sources": ["refund-policy"] }, "observedThrough": { "position": 9, "at": "2026-09-28T09:00:00.000Z" } }, "validity": { "state": "needs_review", "reasons": [ { "kind": "input_changed", "recordId": "refund-window", "changed": { "position": 14, "at": "2026-09-28T10:30:00.000Z" } } ], "affirmed": { "position": 9, "at": "2026-09-28T09:00:00.000Z" }, "monitored": { "claims": ["refund-window"], "sources": ["refund-policy"] }, "observedThrough": { "position": 15, "at": "2026-09-28T11:00:00.000Z" } }, "movedSince": [ { "kind": "input_changed", "recordId": "refund-window", "changed": { "position": 14, "at": "2026-09-28T10:30:00.000Z" } } ] } ] }}The text a receipt pinned
Section titled “The text a receipt pinned”A receipt names versions; this read returns what they held. It answers the full text of every document version the receipt pinned, in the receipt’s own order, even when the document has since been rewritten or retired — the passages the answer quoted came from that text.
curl "$API/v1/brains/tenant:acme/receipts/rcp_019ffefe/documents" \ -H "authorization: Bearer $NICIA_BRAIN_KEY"{ "data": { "documents": [ { "source": "hubspot:deal:412", "version": 2, "contentHash": "sha256:4be1f07d5b8a3c1e9f2a6d0b7c4e8a13f5d9b2c6e0a7f4d1b8c3e6a9f2d5b0c7", "text": "Acme renewed at $120k for 24 months. Signed 2026-03-14." } ], "receipt": "rcp_019fff2a" }}- Only through the receipt, only for its key. The key that made the read,
or a key with
revieworadmin, gets the text; any other key gets404. A version no read was ever served — a snapshot’s document that was never applied — is pinned by no receipt, so there is nothing to read it through. - Exact or nothing.
contentHashissha256:and the digest oftext. Stored content that no longer matches the version it was written as fails the read with an error; it is never served in the pinned version’s place. - Documents only. Records a receipt pinned are not included: a record’s earlier values are not served, and the record read returns its current one.
- A read of its own. Handing text back is content leaving the Brain, so each
page commits a receipt of kind
documentover the versions it returned and answers its id asreceipt. It takes aconsumerIdlike any other read. - Paged. Up to 25 documents per page, 10 by default (
limit); walk the rest withcursor=<nextCursor>.
Correlating by consumer
Section titled “Correlating by consumer”Pass a consumerId on any read — /context, /search, or /query — and it
is recorded on the receipt:
curl -X POST "$API/v1/brains/tenant:acme/context" \ -H "authorization: Bearer $NICIA_KEY" \ -H "content-type: application/json" \ -d '{ "prompt": "…", "consumerId": "user:4471" }'consumerId is a label you choose — user:4471, job:nightly-scoring,
session:abc — 1 to 512 characters; empty or longer is a 400 naming the
field. It is correlation only. It never restricts what a call can reach, so
scoping still comes from the key.
Listing receipts
Section titled “Listing receipts”List receipts newest first, without their items. Filter by consumerId,
kind, key, and a committed-time window — since inclusive, until
exclusive, so adjacent windows never count a receipt twice. A key lists the
receipts it committed; a key with admin lists every key’s, or one key’s with
key. review opens a receipt it is handed the id of, but does not list other
keys’ receipts. That is how a batch eval walks exactly the reads one run made:
curl "$API/v1/brains/tenant:acme/receipts?consumerId=job:nightly-scoring&since=2026-09-01T00:00:00Z&until=2026-09-02T00:00:00Z" \ -H "authorization: Bearer $NICIA_BRAIN_KEY"{ "data": { "receipts": [ { "id": "rcp_019ffefe", "kind": "context", "consumerId": "job:nightly-scoring", "key": { "id": "key_7f3a1c", "name": "Scoring job" }, "snapshot": { "brainId": "brain_3a91c7", "generation": "6f1e4d6b-9f0c-4a21-8f3e-1f1a2b3c4d5e", "stateVersion": "sv_019ffefe", "schemaVersion": "nicia-base", "policyVersion": "pol_nicia_open" }, "itemCount": 3, "committedAt": "2026-09-01T02:14:09Z" } ], "nextCursor": "rcp_019ffefe" }}A receipt is identified by what the read served, not when it happened. A read
that serves exactly what an earlier read served, under the same request, returns
the earlier receipt and its original committedAt rather than a new one, so a
since window and the who-was-served reads below list each distinct serve once.
A job that reads unchanged state every day appears in these listings on the day
it first read it. Count reads from your own consumerId log, not from receipt
counts.
Each receipt’s items are GET /receipts/{id}, and the text its documents held
is GET /receipts/{id}/documents — so an eval can pin every outcome to the
exact context it was given, even after the documents changed. Up to 250
receipts per page; walk the rest with cursor=<nextCursor>. A listing carries
no content: nothing a read handed out comes back through it.
Who was served
Section titled “Who was served”After a correction, the question is who was told the old version. Ask the
record, with a key that holds review:
curl "$API/v1/brains/tenant:acme/records/acme-co/reads?stale=true" \ -H "authorization: Bearer $NICIA_BRAIN_KEY"{ "data": { "reads": [ { "receipt": "rcp_019ffefe", "kind": "query", "consumerId": "user:4471", "key": { "id": "key_7f3a1c", "name": "Support bot" }, "committedAt": "2026-08-14T11:02:44Z", "servedVersion": 1, "currentVersion": 2, "status": "superseded" } ] }}Every receipted read that served the record, from any key, newest first, with
the version it served, the version the Brain serves now, and status:
current, superseded, retracted (a retracted statement), deleted (a
deleted record or a retired document, with no currentVersion), or held
under a legal hold. A record’s version numbers restart when it is deleted and
created again at the same address, so a read of the deleted record is deleted
even though the address serves again: currentVersion is absent and
recreatedVersion names the new record’s version, which is not a later version
of the one served. GET /receipts/{id}/now reports the same item as deleted
with recreatedVersion. stale=true keeps only the reads whose version is no longer
current — a correction’s blast radius. The same read exists for a document,
GET /documents/{id}/reads, and for a document of a named
source, GET /sources/{name}/documents/{id}/reads. Reading the text a key reads back through a receipt is a read too, of
kind document, so it appears here beside the read that pinned it.
This is metadata: receipt ids, keys, consumers and version numbers, never field
values or text. It needs review because it describes every key’s reads; the
content stays behind the receipt reads above.
Corrections
Section titled “Corrections”When something turns out to have been wrong, write the correction — same
document id, or a new warranted fact — rather than quietly forgetting the
old number. The next /context is current. The receipt from the old read is
stale: GET /receipts/{receiptId}/now reports superseded, and
?stale=true on the record’s or document’s reads lists every
receipt that was served the old version, with its consumer and key — who needs
to hear about it. To learn that a correction landed without polling each
address, follow the event stream: record.accepted,
record.deleted, statement.retracted, document.served and
document.retired name the address that changed, which is the address to ask
for its reads. Reads themselves are not events; this page is where they are.
The context check
Section titled “The context check”A receipt records what was served; the context check grades it. In the product, open a served context and run the check: it reports where the sources behind that answer contradict each other (each contradiction finding is judged by a live model before it is shown), where the same wording was sent twice, and where the payload spent budget on words that carried no information.
The check is a report, run after the fact against a payload you already served — it is not a gate in the read path, and nothing blocks or rewrites an answer. It runs on the knowledge you have now, from the receipted reads you have already made: reads served over the API, over MCP, or under a Brain key. Reading a Brain by clicking around the console is not one of them and mints no receipt, so a Brain nobody has queried through a credential yet has nothing here to grade. Once one has, there is nothing to migrate before finding out what your agents are actually reading.
Retention
Section titled “Retention”Receipts are retained indefinitely — nothing deletes one. They survive
a document’s retirement (a document is never deleted) and Brain deletion, and outlive the Brain they are evidence
about — an answer you gave a customer stays explainable after they churn.
GET /receipts/{id} keeps answering for the management key after the Brain is
deleted. The
Brain export carries them as receipts.json: every
receipt the exporting key can list, with its snapshot, request and render
hashes, and the version of every source it pinned — but not the text a read was
handed, which stays behind the receipt reads.
- Review — deciding what lands in the first place.
- Context — where receipts come from.
- Events and webhooks — what changed, to find the reads a change made stale.