Facts
A fact is a claim about a subject: Acme’s contract is $120k, Bob works at
Y, the handbook says PTO starts on day one. Every fact carries a warrant —
either a source (a record you can go and read) or ground: "assertion"
(you stake the claim; the principal is the key, never a field you send).
Never neither. Never both.
curl -X POST "$API/v1/brains/tenant:acme/statements" \ -H "authorization: Bearer $NICIA_KEY" \ -H "content-type: application/json" \ -H "idempotency-key: stmt-salesforce-arr" \ -d '{ "subject": "acme", "predicate": "contract_value_usd", "value": 120000, "source": "salesforce-renewal" }'{ "data": { "statementIds": ["stmt_91ab01"], "outcome": { "kind": "admitted", "batchId": "batch_44a1e0", "policyVersionId": "pol_nicia_open", "acceptedSnapshot": "snap_6f1e4d6b", "eventIds": ["chg_44a1e0"] }, "statements": [ { "id": "stmt_91ab01", "subject": "acme", "predicate": "contract_value_usd", "value": 120000, "validFrom": "2026-08-14T09:20:00.000Z", "source": "salesforce-renewal", "assertedBy": { "kind": "service", "id": "key_7f3a1c" }, "status": "active" } ] }}source is a record id — the same kebab rule as PATCH /records/{id}. A
document id with colons (hubspot:deal:412) is not a source; write or
address the record first. assertedBy is projected from the change event.
You cannot set it.
The other warrant is a stake:
curl -X POST "$API/v1/brains/tenant:acme/statements" \ -H "authorization: Bearer $NICIA_KEY" \ -H "content-type: application/json" \ -H "idempotency-key: stmt-handbook-pto" \ -d '{ "subject": "acme", "predicate": "pto_starts_on", "value": "day-one", "ground": "assertion" }'Sources disagree
Section titled “Sources disagree”A later fact from the same source supersedes. A different source
disagreeing over an overlapping window does not: both facts stay, and the
next /context names it. You do not call GET /conflicts to find out. That
list is the operator inbox.
curl -X POST "$API/v1/brains/tenant:acme/statements" \ -H "authorization: Bearer $NICIA_KEY" \ -H "content-type: application/json" \ -H "idempotency-key: stmt-netsuite-arr" \ -d '{ "subject": "acme", "predicate": "contract_value_usd", "value": 145000, "source": "netsuite-renewal" }'curl -X POST "$API/v1/brains/tenant:acme/context" \ -H "authorization: Bearer $NICIA_KEY" \ -H "content-type: application/json" \ -d '{ "prompt": "What is the Acme contract value?", "maxTokens": 2000, "format": "items" }'{ "data": { "text": "", "citations": [], "items": [ { "kind": "record", "id": "stmt-salesforce-arr", "fields": { "contract_value_usd": 120000 }, "status": "contested" }, { "kind": "record", "id": "stmt-netsuite-arr", "fields": { "contract_value_usd": 145000 }, "status": "contested" } ], "staged": 0, "warnings": [ { "kind": "contested", "subject": "acme", "predicate": "contract_value_usd", "count": 2, "detail": "Two sources disagree about acme.contract_value_usd." } ], "receipt": "rcp_contested", "truncated": false }}warnings[] kind contested names the subject and predicate. Per-item
status: "contested" marks the facts in this answer. There is no
conflicts[] field on /context.
Resolve
Section titled “Resolve”GET /conflicts is the inbox. POST /conflicts/{id}/resolve keeps one fact
or writes a corrected value. The loser is retained, not deleted. The call
needs review and an Idempotency-Key.
curl "$API/v1/brains/tenant:acme/conflicts?resolved=false" \ -H "authorization: Bearer $NICIA_KEY"{ "data": { "conflicts": [ { "id": "cfl_91ab", "subject": "acme", "predicate": "contract_value_usd", "statements": [ { "id": "stmt_91ab01", "subject": "acme", "predicate": "contract_value_usd", "value": 120000, "validFrom": "2026-08-14T09:20:00.000Z", "source": "salesforce-renewal", "assertedBy": { "kind": "service", "id": "key_7f3a1c" }, "status": "conflicted" }, { "id": "stmt_91ab02", "subject": "acme", "predicate": "contract_value_usd", "value": 145000, "validFrom": "2026-08-14T09:21:00.000Z", "source": "netsuite-renewal", "assertedBy": { "kind": "service", "id": "key_7f3a1c" }, "status": "conflicted" } ], "detectedAt": "2026-08-14T09:21:01.000Z" } ], "receipt": "rcp_conflicts" }}curl -X POST "$API/v1/brains/tenant:acme/conflicts/cfl_91ab/resolve" \ -H "authorization: Bearer $NICIA_KEY" \ -H "content-type: application/json" \ -H "idempotency-key: resolve-acme-arr" \ -d '{ "keep": "stmt_91ab02" }'{ "data": { "outcome": { "kind": "admitted", "batchId": "batch_91ab03", "policyVersionId": "pol_nicia_open", "acceptedSnapshot": "snap_91ab03", "eventIds": ["chg_91ab03"] } }}Or send "value": 145000 instead of keep to supersede both with a
corrected number.