Snapshots
A snapshot is one source’s current set of documents — all of it, or an explicitly partial part. It is the operation for “here is everything this customer gave us now”: you send the set, the Brain compares it with what the source said before and with what the Brain has accepted, and you get a plan before anything changes what a read serves.
Nothing a snapshot holds is served — by /context, /search, /query or the
document listing — until you apply its plan, and applying goes through the
Brain’s own admission policy, so a document that
disappeared from the set is retired only through a decision.
The journey:
- Open a snapshot of the source.
- Add its documents, up to 100 per request.
- Seal it as
completeorpartial. - Poll until it is
planned. - Read the plan — what each document changes — and the findings — where the snapshot’s own documents disagree.
- Apply the plan revision you read.
- Review what the policy held back.
- Every read after that is receipted with the exact versions it served.
Open a snapshot
Section titled “Open a snapshot”curl -X POST "$API/v1/brains/tenant:acme/sources/acme-content/snapshots" \ -H "authorization: Bearer $NICIA_KEY" \ -H "idempotency-key: acme-content-2026-09-23"{ "data": { "snapshot": { "id": "01a0cdbb-66e1-75be-a056-c758c4b3ee4c", "source": "acme-content", "status": "open", "documents": 0, "bytes": 0, "openedAt": "2026-09-23T10:06:45.984Z", "expiresAt": "2026-09-24T10:06:45.984Z" } }}The Idempotency-Key is required: a retry with the same key answers the
snapshot it already opened, with 200 instead of 201. A source has at most
one open snapshot, so opening another supersedes the first. An open snapshot
expires after 24 hours.
Add documents
Section titled “Add documents”curl -X POST "$API/v1/brains/tenant:acme/sources/acme-content/snapshots/01a0cdbb-66e1-75be-a056-c758c4b3ee4c/documents" \ -H "authorization: Bearer $NICIA_KEY" \ -H "content-type: application/json" \ -d @documents.json{ "documents": [ { "id": "catalog", "title": "Product catalog", "text": "# Catalog\n\nAcme Widget costs $11.", "metadata": { "drive_file": "1x9Qa" } }, { "id": "gizmo", "text": "# Gizmo\n\nGizmo costs $35." } ]}{ "data": { "snapshot": { "id": "01a0cdbb-66e1-75be-a056-c758c4b3ee4c", "source": "acme-content", "status": "open", "documents": 2, "bytes": 58, "openedAt": "2026-09-23T10:06:45.984Z", "expiresAt": "2026-09-24T10:06:45.984Z" }, "documents": [ { "id": "catalog", "status": "changed" }, { "id": "gizmo", "status": "new" } ] }}A snapshot document is a document with a required
id — your id for it within the source, which is how the next snapshot is
compared with this one. Each is acknowledged against the version the source
serves now: new, changed, or unchanged. An unchanged document stores
nothing new.
Documents are Markdown text; converting a PDF or a slide deck is your step, as
it is for POST /documents. An id may appear once per snapshot: repeating one
is 409, and nothing in that request is stored.
curl -X POST "$API/v1/brains/tenant:acme/sources/acme-content/snapshots/01a0cdbb-66e1-75be-a056-c758c4b3ee4c/seal" \ -H "authorization: Bearer $NICIA_KEY" \ -H "content-type: application/json" \ -H "idempotency-key: acme-content-2026-09-23-seal" \ -d '{ "completeness": "complete" }'{ "completeness": "complete" }Sealing fixes what the snapshot is compared with — what the source serves at
that moment and the Brain’s schema — and answers 202. completeness is the
one decision you make here:
completesays the snapshot is the source’s whole content. A document the source serves that the snapshot leaves out is planned asmissing, and applying the plan proposes retiring it.partialsays nothing about documents the snapshot leaves out. They keep being served, exactly as they are.
Wait for the plan
Section titled “Wait for the plan”curl "$API/v1/brains/tenant:acme/sources/acme-content/snapshots/01a0cdbb-66e1-75be-a056-c758c4b3ee4c" \ -H "authorization: Bearer $NICIA_KEY"{ "data": { "snapshot": { "id": "01a0cdbb-66e1-75be-a056-c758c4b3ee4c", "source": "acme-content", "status": "extracting", "completeness": "complete", "documents": 2, "bytes": 58, "openedAt": "2026-09-23T10:06:45.984Z", "sealedAt": "2026-09-23T10:06:46.025Z", "expiresAt": "2026-09-24T10:06:45.984Z" }, "acks": { "new": 1, "changed": 1, "unchanged": 0 } }}The status read is safe to poll: it returns no content and commits no receipt.
A sealed snapshot is extracting while a model reads each added or changed
document into the fields your Brain’s schema declares,
then planned. A Brain whose kinds declare no fields beyond their identity has
nothing to extract, and plans at once — its plan compares documents only.
A planned snapshot expires after 7 days if nobody applies it.
Read the plan
Section titled “Read the plan”curl "$API/v1/brains/tenant:acme/sources/acme-content/snapshots/01a0cdbb-66e1-75be-a056-c758c4b3ee4c?plan=true" \ -H "authorization: Bearer $NICIA_KEY"{ "data": { "snapshot": { "id": "01a0cdbb-66e1-75be-a056-c758c4b3ee4c", "source": "acme-content", "status": "planned", "completeness": "complete", "documents": 2, "bytes": 58, "openedAt": "2026-09-23T10:06:45.984Z", "sealedAt": "2026-09-23T10:06:46.025Z", "plannedAt": "2026-09-23T10:06:46.068Z", "expiresAt": "2026-09-30T10:06:46.068Z" }, "acks": { "new": 1, "changed": 1, "unchanged": 0 }, "plan": { "revision": 1, "counts": { "added": 1, "changed": 1, "unchanged": 0, "missing": 1 }, "observationCounts": { "new": 0, "restated": 0, "changed": 1, "contradicts": 1, "disagrees": 0, "withdrawn": 1, "unknown": 0 }, "findingCounts": { "duplicate": 0, "disagrees": 0, "contradicts": 0 }, "entries": [ { "id": "catalog", "class": "changed", "contentHash": "sha256:5b1f0c…", "baseContentHash": "sha256:9e47d2…", "observations": [ { "class": "changed", "kind": "product", "identity": "Acme Widget", "field": "price_usd", "record": { "id": "acme-widget", "version": 1 }, "value": 11, "quote": "Acme Widget costs $11.", "prior": [10], "supportedBy": [{ "kind": "source", "source": "acme-content", "keyId": "key_7f3a1c" }] } ] }, { "id": "gizmo", "class": "added", "contentHash": "sha256:c2d8a1…", "observations": [ { "class": "contradicts", "kind": "product", "identity": "Gizmo", "field": "price_usd", "record": { "id": "gizmo", "version": 1 }, "value": 35, "quote": "Gizmo costs $35.", "prior": [30], "supportedBy": [{ "kind": "human" }] } ] }, { "id": "launch", "class": "missing", "baseContentHash": "sha256:07ae3f…", "observations": [ { "class": "withdrawn", "kind": "product", "identity": "Bolt", "field": "price_usd", "record": { "id": "bolt", "version": 1 }, "prior": [50], "supportedBy": [{ "kind": "source", "source": "acme-content", "keyId": "key_7f3a1c" }], "effect": "delete_record" } ] } ], "receipt": "rcp_01a0cdbb" } }}?plan=true pages the plan, up to 100 documents at a time; walk the rest with
cursor=<nextCursor>. revision is what you apply. Each page is a read of
what the Brain has accepted, so it commits a receipt of kind plan and returns
its id.
Every document has one class:
| Class | Means |
|---|---|
added |
A document id the source has not sent before. |
changed |
Its content differs from the version the source serves. |
unchanged |
Same content. Nothing to do. |
missing |
Served now, absent from a complete snapshot. |
Every observation — one record field a document states — compares the
value, and the quote it was read from, with what the Brain has accepted.
supportedBy says who stands behind the accepted value: sources whose served
documents state it, or human when none does.
| Class | Means |
|---|---|
new |
Nothing is accepted at this record and field yet. |
restated |
Equal to the accepted value. Nothing to write. |
changed |
Replaces a value only this source supported, which none of its documents states any more. |
contradicts |
Differs from a value a person or another source supports, on a field declared single. |
disagrees |
The same, on a field with no single declaration. The accepted value is kept. |
withdrawn |
This source no longer states a value it supported. effect says what that would do. |
unknown |
Extraction, identity, or comparison failed. The source’s earlier support is kept, and reason says what failed. |
A value a person or another source supports is never overwritten by a
snapshot. Only changed writes over an accepted value, and only when this
source was its sole support and has stopped stating it in every one of its
documents. A document whose extraction failed makes its entries unknown,
never withdrawn.
Three rules keep a plan from choosing for you:
- A source that disagrees with itself picks nothing. When the source’s own
documents — this snapshot’s, and those a
partialsnapshot leaves out — state different values for onesinglefield, every document is listed ascontradictswith its own value and quote andreason: "intra_source". No value is written; the entries wait for review until the documents agree again, or a person sets the field. - A contradiction is listed under every document that states it, and those documents wait for review together — no fewer, no more.
- A decided contradiction is not asked again. Once a reviewer has decided a
contradiction, a later snapshot restating the same value is
disagreeswithreason: "already_decided", and nothing waits for it. A different value asks again.
Read the findings
Section titled “Read the findings”curl "$API/v1/brains/tenant:acme/sources/acme-content/snapshots/01a0cdbb-66e1-75be-a056-c758c4b3ee4c?findings=true" \ -H "authorization: Bearer $NICIA_KEY"?findings=true pages what the snapshot’s own documents say about each
other, grouped by record and field: duplicate (documents restate one
value), disagrees (they state different values), and contradicts (they do,
on a field declared single). Each finding lists every document, value and
quote behind it. Like a plan page, a findings page commits a plan receipt.
The first complete snapshot of a source into a Brain that holds nothing from
it yet is an onboarding scan: every document is added, every observation
new — except where the documents contradict each other on a single field,
which is contradicts and lands nothing — and the findings are the
substance: the places a customer’s own material disagrees with itself before
any agent reads it.
curl -X POST "$API/v1/brains/tenant:acme/sources/acme-content/snapshots/01a0cdbb-66e1-75be-a056-c758c4b3ee4c/apply" \ -H "authorization: Bearer $NICIA_KEY" \ -H "content-type: application/json" \ -d '{ "planRevision": 1 }'{ "planRevision": 1 }{ "data": { "snapshot": { "id": "01a0cdbb-66e1-75be-a056-c758c4b3ee4c", "source": "acme-content", "status": "awaiting_review", "completeness": "complete", "documents": 2, "bytes": 58, "openedAt": "2026-09-23T10:06:45.984Z", "sealedAt": "2026-09-23T10:06:46.025Z", "plannedAt": "2026-09-23T10:06:46.068Z", "expiresAt": "2026-09-30T10:06:46.068Z", "appliedPlanRevision": 1, "appliedAt": "2026-09-23T10:06:46.099Z", "reviewChangeId": "47b38dd1-48d1-88b1-82c7-1d73ab8fc25a" }, "planRevision": 1, "admitted": { "changeIds": ["brn_9c2a1f:6:1:0", "brn_9c2a1f:6:1:1"] }, "review": { "changeId": "47b38dd1-48d1-88b1-82c7-1d73ab8fc25a" }, "rejected": [], "counts": { "admitted": 1, "review": 2, "rejected": 0 } }}Apply decides the revision you read, document by document, under the Brain’s
write policy. A document and every field and statement
read from it share one outcome, and so does every write to one record, so no
passage is served while part of what was read from it waits for review.
counts are documents:
- Admitted documents are served at once and their records written.
- Review documents wait in one review change,
review.changeId. Until a reviewer publishes it, the source keeps serving what it served before. - Rejected documents are listed with the policy’s reasons and never staged.
At the default rung of the write policy a routine
update is admitted, and these wait for a person: a contradicts entry, a
withdrawn entry, a missing document, and an unknown entry — and a
substantive change to a field declared consequence: review (review
declarations). A
contradiction is staged as a statement recording what the source said and a
separate proposed value, so the reviewer can keep the accepted value and record
the disagreement, or adopt the new one. When the source contradicts itself, each
candidate is staged as a statement and no value is proposed: the record is
created or kept without the contested field, and the documents stating the
candidates wait with them.
Applying the same revision again returns the same body. 409 whose message
starts snapshot_plan_stale means something the plan read has moved since —
what the source serves, a record it compared with, a record it expected to
create, or the Brain’s schema. Nothing was applied: seal again (with a new
Idempotency-Key) and read the new revision. Documents already read are not
read again.
Review what waits
Section titled “Review what waits”The snapshot reports awaiting_review with the reviewChangeId it waits on.
Inspect it with GET /v1/brains/{handle}/changes/{changeId}/lifecycle: every
operation names the document ({ source, id }) it serves or retires, or the
record ({ kind, id }) it writes, and the operations it depends on. A record
read from a document depends on the operation that serves that document.
Approve and publish the whole change, reject it, or select part of
it — for a contradiction, the statement alone keeps the
accepted value and records the disagreement, and the statement with the value
adopts it. Once the change is decided, the snapshot settles applied the next
time it is read.
Applying a later snapshot of the same source recomputes, in an earlier one’s
still-pending review, whatever the later snapshot covers: a complete one
covers every document, a partial one the documents it holds.
Cancel
Section titled “Cancel”curl -X POST "$API/v1/brains/tenant:acme/sources/acme-content/snapshots/01a0cdbb-66e1-75be-a056-c758c4b3ee4c/cancel" \ -H "authorization: Bearer $NICIA_KEY"Cancelling ends a snapshot that has not been applied. Its documents are never served.
Receipts after a refresh
Section titled “Receipts after a refresh”Every /context, /search and /query receipt pins the exact version of each
document it served, named by the source and your id, and the receipt’s
snapshot carries an evidenceSetDigest that changes whenever the set of served
documents does. After a refresh, /receipts/{receiptId}/now reports which
cited documents changed or were retired, and the text a receipt
pinned is still readable —
including a retired document’s — by the key that made the read.
Limits
Section titled “Limits”| Limit | Value |
|---|---|
| Documents per add request | 100 |
| Documents per snapshot | 2,000 |
| Bytes per snapshot | 50 MB |
| Plan or findings page | 100 |
| An open snapshot expires after | 24 hours |
| A planned snapshot expires | 7 days after it is planned |
What a snapshot does not do
Section titled “What a snapshot does not do”- It does not convert files. Documents are Markdown text.
- It does not read perfectly. A model reads documents into your declared
fields, and it can miss or misread a value. The plan shows the quote behind
every value so a person can check it, and a failed read is
unknown, never a withdrawal. - It compares values, not units. A field compared as
contradictsneeds asingledeclaration; a difference the schema cannot describe — a unit, a currency, a qualifier — is at mostdisagreesorunknown. - It has one producer. A source belongs to one key; two keys cannot report as one source.