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.

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",
"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, and you can change it later — including to the domain-neutral nicia-core vocabulary by naming it explicitly. See Schemas.
  • mode: "open" — writes land immediately. 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.

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

Terminal window
# A document — text, markdown, a transcript, a PDF
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 whole product. 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 query written once works against every one of them. nicia-core is the neutral vocabulary — people, organizations, topics, events, notes — which is enough for a query to mean the same thing everywhere without anyone authoring an ontology first:

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.

  • Context — token budgets, citations, and receipts.
  • Schemas — when to leave the default alone and when not to.
  • Review — turning on approval, and settling the queue.