Skip to content
Beta. This surface may change before GA; breaking changes are dated in the changelog.

Quickstart

Agents don’t need memory. They need a Brain. This quickstart gets you a cited answer in three HTTP calls: create a Brain, put something in it, ask it a question. No schema to design, no policy to configure, no ids to write down.

Sign in at app.nicia.ai, open Settings → API keys, and mint an organization key under “Organization keys”. The secret is shown once — copy it now.

Getting this first key is interactive, and nothing on this site shortcuts it. Creating an account is open to anyone, but signing in needs a verified email address, and both endpoints refuse a request that did not come from the product frontend. Budget for one person in a browser, once — every call below is automatable afterwards. See Authentication.

Terminal window
export NICIA_KEY=""
export API="https://api.nicia.ai"

An organization key can do anything inside the organization you minted it from: create Brains, read and write them, and mint narrower keys to hand out later. That is why it is the right key for a quickstart and the wrong key to ship inside a client app. It is bound to that organization at mint time, so every call below sends it alone, in authorization: Bearer — there is no second id to copy or send. See Authentication.

Every response is { "data": … } on success and { "error": { "kind", "message" } } on failure. See Errors.

A Brain is addressed by a handle you choose: a customer id, an employee id, a project slug — anything stable that you already have. The handle is public and appears in every URL. A key is always a secret credential; the two are never the same thing.

Terminal window
curl -X PUT "$API/v1/brains/tenant:acme" \
-H "authorization: Bearer $NICIA_KEY" \
-H "content-type: application/json" \
-d '{ "name": "Acme", "purpose": "Everything we know about Acme." }'
{
"data": {
"brain": {
"id": "brain_3a91c7",
"generation": "6f1e4d6b-9f0c-4a21-8f3e-1f1a2b3c4d5e",
"handle": "tenant:acme",
"name": "Acme",
"purpose": "Everything we know about Acme.",
"schema": "nicia-base",
"mode": "open",
"createdAt": "2026-08-14T09:12:04.221Z",
"updatedAt": "2026-08-14T09:12:04.221Z"
}
}
}

PUT is create-or-update on your handle, so it is safe to call on every request, in every worker, after every crash. The first call returns 201, every identical call after it returns 200, and there is no “does it exist yet” query to race against.

Three defaults worth knowing:

  • schema: "nicia-base" — a deterministic starter vocabulary (people, companies, deals, concepts, notes). You did not have to design anything. nicia-core, the domain-neutral vocabulary, is available by naming it explicitly on the create call — the initial schema is fixed on creation, and later changes go through governed schema evolution; see Schemas).
  • mode: "open" — most writes land immediately; destructive changes still wait for approval. Switch to "reviewed" when you want a human to approve what agents write. See Review.
  • The Brain is addressed by tenant:acme from here on. Nicia’s own id comes back in the response and works in the same slot, but nothing on this site asks for it — every path below takes the handle. Store it only if you call one of the older id-addressed paths. generation beside it is the Brain’s lifecycle fence — opaque, re-minted by a revive or reprovision, and needed only by the generation-scoped writes on the older planes.

Documents and structured records are peer on-ramps. Use whichever you have.

Terminal window
# A document — text, markdown, a transcript. Text, not bytes: convert files first.
curl -X POST "$API/v1/brains/tenant:acme/documents" \
-H "authorization: Bearer $NICIA_KEY" \
-H "content-type: application/json" \
-d '{
"id": "hubspot:deal:412",
"text": "Renewal call 2026-03-14. Acme renewed at $120k for 24 months. Bob Smith (Head of Platform) pushed for SSO before Q3.",
"metadata": { "source": "hubspot", "type": "call-note" }
}'
{
"data": {
"document": { "id": "hubspot:deal:412", "status": "queued" }
}
}

id is yours. Sending the same id again replaces that document, so a nightly sync is a loop of POSTs and nothing else.

If what you have is already structured, send it as a record instead — same Brain, no schema step:

Terminal window
curl -X PATCH "$API/v1/brains/tenant:acme/records/bob-smith" \
-H "authorization: Bearer $NICIA_KEY" \
-H "content-type: application/json" \
-d '{ "fields": { "name": "Bob Smith", "role": "Head of Platform" } }'

See Documents and Records.

Terminal window
curl -X POST "$API/v1/brains/tenant:acme/context" \
-H "authorization: Bearer $NICIA_KEY" \
-H "content-type: application/json" \
-d '{ "prompt": "What should I know before the renewal call?", "maxTokens": 2000 }'
{
"data": {
"text": "Renewal call 2026-03-14. Acme renewed at $120k for 24 months. Bob Smith (Head of Platform) pushed for SSO before Q3.\n\nbob-smith\nname: Bob Smith\nrole: Head of Platform",
"citations": [
{
"source": "hubspot:deal:412",
"slug": "brain-evidence-0dd3e2449851d9ba41924c42bd7a61375a8b78050bfe2ca7192eb918d626236a",
"quote": "Renewal call 2026-03-14. Acme renewed at $120k for 24 months. Bob Smith (Head of Platform) pushed for SSO before Q3.",
"recordedAt": "2026-08-14T09:13:10.882Z"
},
{
"slug": "bob-smith",
"quote": "bob-smith\nname: Bob Smith\nrole: Head of Platform",
"recordedAt": "2026-08-14T09:13:44.117Z"
}
],
"staged": 0,
"warnings": [],
"receipt": "rcp_019ffefe",
"truncated": false
}
}

Paste text straight into your prompt. Show citations to your user. Keep receipt in your logs — it is how you reconstruct months later exactly what the model was shown. See Receipts.

text is one block per source, blank-line separated: a document contributes its prose — plus a name line and its frontmatter keys, when it carries any — and a record its label, its kind, and its values. A record with no label is headed by its id, which is why Bob’s block starts bob-smith. Each block is the matching citations[].quote, verbatim — so text is never more than the sum of what you can show a user, and never less. See what text is.

staged is the count of changes waiting for review. It is usually 0 on an open Brain, whose writes are admitted as they arrive; a non-zero value tells you an empty answer means “waiting”, not “nothing there”. See Context.

That is the cited read. The next ten minutes are the loop that makes it defensible: correct what was wrong, see the receipt go stale, name who was told, and notice when two sources disagree — on /context, not on a second endpoint.

5. Correct, see who was told, notice disagreement

Section titled “5. Correct, see who was told, notice disagreement”

The receipt from step 4 is rcp_019ffefe. Rewrite the document — same id, new text — and the next read is current. The old receipt is not.

Terminal window
curl -X POST "$API/v1/brains/tenant:acme/documents" \
-H "authorization: Bearer $NICIA_KEY" \
-H "content-type: application/json" \
-d '{
"id": "hubspot:deal:412",
"text": "Amendment signed 2026-08-01. Acme renewed at $145k for 24 months. Bob Smith (Head of Platform) pushed for SSO before Q3."
}'
Terminal window
curl "$API/v1/brains/tenant:acme/receipts/rcp_019ffefe/now" \
-H "authorization: Bearer $NICIA_KEY"
{
"data": {
"changed": [
{
"source": "hubspot:deal:412",
"shownVersion": 2,
"currentVersion": 3,
"status": "superseded"
}
],
"unchanged": 1,
"deleted": 0
}
}

$120k was right when it was said. It is wrong now. eventId is the change that was superseded — the original write, named in History — not the correction:

Terminal window
curl "$API/v1/brains/tenant:acme/corrections/chg_44a1e0/affected-reads" \
-H "authorization: Bearer $NICIA_KEY"
{
"data": {
"observedConsumers": [
{
"connectionId": "conn_7f3a1c",
"consumerId": "user:4471",
"receiptId": "rcp_019ffefe",
"servedAt": "2026-08-14T11:02:44.000Z"
}
],
"candidateRuns": [],
"truncated": false
}
}

That is who was told: every receipted read of the superseded content. observedConsumers is receipt-backed and never claimed exhaustive; candidateRuns is the weaker “the Brain was available” list and is never unioned in. The knowledge.corrected webhook carries the same observedConsumers list. See Receipts.

A second source can disagree without anyone correcting anything. Write two warranted facts about the same subject and predicate — each with its own source — and /context names the disagreement inline. There is no conflicts[] field; the signal is warnings[] kind contested plus per-item status.

source on a statement is a record id — kebab, same rule as every other record — not a document id with colons. Create the source records first:

Terminal window
curl -X PATCH "$API/v1/brains/tenant:acme/records/hubspot-deal-412" \
-H "authorization: Bearer $NICIA_KEY" \
-H "content-type: application/json" \
-d '{ "fields": { "name": "HubSpot deal 412" } }'
curl -X PATCH "$API/v1/brains/tenant:acme/records/netsuite-renewal" \
-H "authorization: Bearer $NICIA_KEY" \
-H "content-type: application/json" \
-d '{ "fields": { "name": "NetSuite renewal record" } }'
curl -X PATCH "$API/v1/brains/tenant:acme/records/acme" \
-H "authorization: Bearer $NICIA_KEY" \
-H "content-type: application/json" \
-d '{ "fields": { "name": "Acme" } }'
Terminal window
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": "hubspot-deal-412"
}'
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 Acme's contract value?", "maxTokens": 2000, "format": "items" }'
{
"data": {
"text": "",
"citations": [],
"items": [
{
"kind": "record",
"id": "acme",
"fields": { "contract_value_usd": 120000 },
"status": "contested"
}
],
"staged": 0,
"warnings": [
{
"kind": "contested",
"subject": "acme",
"predicate": "contract_value_usd",
"count": 2
}
],
"receipt": "rcp_contested",
"truncated": false
}
}

ground: "assertion" is the other warrant: you stake the claim; the principal comes from the key, never from the body. See Facts.

Everything below is the same three calls in different proportions.

Keep one Brain and give each tool its own key, so revocation and attribution stay precise:

Terminal window
# A read-only key for your web app
curl -X POST "$API/v1/brains/tenant:acme/keys" \
-H "authorization: Bearer $NICIA_KEY" \
-H "content-type: application/json" \
-d '{ "name": "web app", "capabilities": ["read"] }'
{
"data": {
"key": {
"id": "key_7f3a1c",
"name": "web app",
"capabilities": ["read"],
"status": "active",
"createdAt": "2026-08-14T09:15:41Z"
},
"secret": "nbk_9f2c…"
}
}

secret is shown once. See Handing out keys.

One Brain per project or account, all on the same schema so a slug (or /context prompt) written once means the same thing against every one of them. nicia-core is the neutral vocabulary — people, organizations, topics, events, notes — which is enough for shared kinds and returned fields without anyone authoring an ontology first. Non-slug /query filters still wait on a property index:

Terminal window
curl -X PUT "$API/v1/brains/project:atlas" \
-H "authorization: Bearer $NICIA_KEY" \
-H "content-type: application/json" \
-d '{ "name": "Atlas", "schema": "nicia-core", "mode": "reviewed" }'

mode: "reviewed" is what turns “my agents write to my notes” into “my agents propose, I accept.” Your review queue is the diff.

// Provisioning is a pure function of your own tenant id.
async function brainFor(tenantId: string): Promise<string> {
const handle = `tenant:${tenantId}`;
await fetch(`${API}/v1/brains/${encodeURIComponent(handle)}`, {
method: "PUT",
headers: {
authorization: `Bearer ${process.env.NICIA_KEY ?? ""}`,
"content-type": "application/json",
},
body: JSON.stringify({ name: tenantId }),
});
return handle; // your handle is the address — nothing to store, nothing to map
}

Because your handle is the address and PUT is idempotent, there is no id-mapping table, no create/get branch, and no duplicate Brain to reconcile. See One Brain per customer.

  • Facts — warranted claims, sources that disagree, resolve.
  • Context — token budgets, citations, and receipts.
  • Receipts — reconstructing a served read, and who was told.
  • Schemas — when to leave the default alone and when not to.
  • Review — turning on approval, and settling the queue.