Idempotency and retries
Network calls fail, and a retry must not create a second of anything. Nicia splits writes into two groups, and you only have to think about one of them.
Addressed writes are naturally idempotent
Section titled “Addressed writes are naturally idempotent”When you name the thing you are writing, the address is the identity. Sending the same request twice is one write, and there is nothing to configure.
| Operation | Why it is safe |
|---|---|
PUT /v1/brains/{handle} |
Your handle is the identity |
PATCH / PUT …/records/{recordId} on a record that EXISTS |
The id is the identity |
POST …/documents with an id |
An upsert on your document id |
POST …/documents without an id |
Content-addressed — Nicia derives sha256:<hash> from the text as the id |
This is why the documents page recommends sending your own
id and the records page recommends deriving slugs from your
own system: it turns a nightly sync into a plain loop with no bookkeeping.
A document with no id gets the same idempotency for free, by a different
route: the id is derived from the text itself, so sending the same content
twice is one document, not two. That trade cuts the other way from everything
else on this page — two documents you meant to be separate collapse into one
if their text happens to be byte-identical. Send your own id when you mean
two.
The one gap the address cannot close is a create whose answer you never received. A retry re-reads the address, finds nothing there — because the first attempt may have failed, or may have staged for review — and writes again. Nicia will not paper over that by hashing your request into a key: two creates you meant separately are byte-identical to a retry, so a derived key would answer the first one’s outcome to the second and drop a write while reporting success. Send a key on a create instead; every record write verb accepts one.
Non-addressed writes take an Idempotency-Key
Section titled “Non-addressed writes take an Idempotency-Key”When the server assigns the identity, a retry cannot be told apart from a deliberate repeat — so you say which it is.
curl -X POST "https://api.nicia.ai/v1/brains/tenant:acme/batch" \ -H "authorization: Bearer $NICIA_KEY" \ -H "content-type: application/json" \ -H "idempotency-key: sync:acme:2026-08-14" \ -d '{ "records": [ … ] }'| Operation | Without a key |
|---|---|
POST …/batch |
At-least-once |
POST …/keys |
At-least-once — may mint two live secrets |
Rules:
- The first request with a given key does the work.
- A retry with the same key and same body returns the same result, including
the original ids. That is how you tell a replay from fresh work: the
changeyou get back carries the first request’s id, not a new one. - The same key with a different body is a
409 conflict. Use a fresh key for a different operation. - A key is scoped to one Brain and the key that sent it, and it is kept for as long as the Brain is. There is no expiry window to race.
Nicia deliberately does not derive a key from a hash of your request body. That would make two intentionally identical operations — two statements of the same measurement, two keys with the same name, a batch you meant to send twice — permanently indistinguishable.
POST …/keys follows the same header, with one difference in what a replay
returns: the key record with a null secret rather than the original result —
the secret is shown exactly once, at the first mint, and Nicia cannot show it
again.
Every write that is not addressed takes the header — there is no idempotencyKey
body field anywhere in the API.
Choosing a key
Section titled “Choosing a key”Use something stable for the logical operation, not something random per attempt:
sync:acme:2026-08-14, app-key:acme, an order id, or a UUID you generate
before the first attempt and reuse across retries.
A genuine second operation deserves a genuine second key. If a nightly job runs twice on purpose, fold the run into the key.
What to retry
Section titled “What to retry”Retry 429 and 5xx with exponential backoff, honouring Retry-After when it
is present. Do not retry other 4xx — they describe a problem with the request
that will answer the same way again. See Errors.
The rule for repeating a write is the same either way: an addressed write is always safe; a non-addressed write is safe when it carries a key.