Address
Every space has a stable address, with its region in the name:wrong_cell to any key or person of the space, instead of serving what it still holds, and the SDKs retry at once on a new connection, which the edge routes to the new cell. The internal topology is never part of the contract: a space on a dedicated cell of the Regulado plan has the same address shape.
The control API, for tenants, projects, sources, keys, people, configuration and usage, lives at https://control.api.niadra.com. The generic webhook also answers at https://mtls.<region>.api.niadra.com, the address that asks for a client certificate in the handshake, for sources authenticated by mtls; see System webhooks.
Versions
The version is in the path:/v1. A new field or a new route does not change the version, so your code must ignore fields it does not know (both SDKs do). Removing a field or changing its meaning is /v2, with 12 months in which both versions answer, a notice by e-mail and, on every response of the old version, Deprecation: @<instant> (RFC 9745), which says since when, and Sunset: <HTTP date> (RFC 8594), which says when it stops, exposed through CORS with the Link of rel="deprecation". Both SDKs log a warning, once per route, when a response carries Deprecation. Today nothing in /v1 has been deprecated, so no response carries those headers. The API versioning policy says what counts as a breaking change and how the notice arrives.
Authentication
The login that confirms the authenticator also returns ten recovery codes, once. When the app is lost, each code works once, in
recovery_code in place of totp, and the answer says how many remain in recovery_codes_left. Niadra keeps only an HMAC-SHA256 of each code. A person generates new codes and changes the app with the password again, and an admin resets the second factor of someone who lost both the app and the codes. New people arrive by invitation, a single-use link to choose a password, or through the company’s SSO, over OIDC or SAML 2.0. A deactivated person loses the control plane at once; a token already issued keeps working on the data API until it expires.
A source key only authenticates with its secret; the key_id in its prefix is public. The scopes are track, act, context, search, identify, admin, analytics, agent_memory, agent_memory:write, replay, coordinate, state:push and agent_state; the last four apply only in a space that turned their feature on. See Spaces and keys.
Governance routes (identity, privacy, audit, measurement, review, triggers) accept either a person with one of the route roles or a source key with the admin scope. The Console roles are admin, security, integration, review, analysis and vendor; admin passes every role check, and a vendor person only sees the sources named in their role binding. Review verdicts are the exception: only a person with the review role records them.
Idempotency
Routes that change identity or erase data take anIdempotency-Key header, and on them it is required: every identity write (create or retract an assertion, merge, unmerge, block a handle, create or end a link, accept or dismiss a suggestion), POST /v1/forget and cutting a source in the cell. Niadra keeps the key for 24 hours with a hash of the body:
- the same key with the same body returns the first answer again;
- the same key with a different body returns 409
conflict.
idempotency_key of each item instead: the provider message id, or a UUIDv7 minted by the SDK. A repeated key is counted in duplicates and never stored twice, even when a webhook is resent days later.
The writes of the agent features require it too: declaring, claiming, reserving an effect, creating and accepting a handoff (coordination); promoting turns, creating a scenario and reporting a run (turn records); creating a legal hold, a review request and releasing content. Turn recording and state pushes use, instead of the header, each record’s turn_id and each item’s version: the same turn sent twice is one turn, and a field moves only under a greater version.
Errors
Route errors answerapplication/problem+json (RFC 9457) with type, title, status, detail, code and request_id. The batch answers 200 when every item was accepted, and 207 with one error per rejected item otherwise. The full catalog is in Errors.
Pagination
Lists paginate by an opaque cursor, in a stable order.next_cursor is null on the last page. Never build a cursor yourself: its content may change without notice.
Rate limits
Over the limit, the answer is 429rate_limited with Retry-After in seconds. Limits apply per key, and writes (/v1/batch, /v1/ingest, /v1/otel, /v1/media, /v1/feedback) are counted apart from reads, so a flood of writes never cuts reads first. A 503 unavailable also carries Retry-After.
A space created a few seconds ago, or one being restored, answers 503 unavailable with Retry-After: 5 while its database is prepared, for up to about a minute. The SDKs send the events of their queue again on their own, keeping them queued until it works; a context or history read is not repeated and comes back empty. A client of the raw API should honour Retry-After and repeat the request.
Caching and ETag
The context has an ETag. The same request with the same state gives the same bytes.
The SDKs send the ETag they hold on every context refresh.
Personal data stays out of URLs
Phone numbers, e-mail addresses, documents, handles, search queries and any free text travel only in request bodies, never in paths or query strings. URLs end up in load balancer logs, firewall logs and tracing spans, outside the encryption and outside the reach offorget. That is why context, search and timeline by handle are POST, and why profile search takes its query in the body. The GET forms of context and timeline take a profile id instead.
Ids that are not personal data, such as a profile id, an item id or an object id, may appear in paths. A conversation_id may be the customer’s phone number, e-mail address or WhatsApp id, so every read that takes one has a POST form, with the id in the body:
The Console and the SDKs use the
POST forms of the reads they make. The GET forms stay in /v1; if you use one of them, send an opaque conversation id from your system.
Tracing
Send a W3Ctraceparent header and Niadra returns it on the response. Send X-Request-Id to choose the request id, or let Niadra mint one; every response carries it in the X-Request-Id header and in the request_id of an error. Quote it when you contact support.
Sizes
A batch body over 2.5 MB, or a batch with more than 500 items, answers 422
invalid_input. An item over 1 MB is rejected on its own, with the item error too_large, and the rest of the batch goes in.
Memory never cuts
The sizes above are a request’s: a larger request is refused with an error, never half taken. What goes in, memory stores and learns whole, and never deletes on its own:- A long turn, e-mail or document is read in parts, in order and with overlap, never cut at its start, its end or at N characters. A file is read whole, in windows of 50 pages, and a text over 100 thousand characters becomes several turns, each with its part (events).
- Summaries, resolutions, values, open items, instructions, agent notes and links are stored whole, with no cap on how many.
- A batch that fails every attempt is kept and can be tried again.
- What leaves memory leaves through an erasure the data subject asks for and through the retention the space configures, and both reach everything, kept batches included.
left_out in the notes block, omitted in the constraints block, open_objects_more in a handoff package, partial on a derived field, “mais N objetos deste cliente fora desta lista” and “mais N registros (consulte o histórico)” on the context’s lines (in the space’s language), “(continua no histórico)” on a long quoted text, and a cursor on the lists that come in pages. The rest is in the history’s search and open.
Speed
No LLM runs in the path of
context() or of the navigation. The times measured in the region are on the benchmark.
Starting caps of a space
These are the caps of every space today. Ask us when a use case needs more.
Names in the contract
Every name in the API, the SDKs, the MCP tools, the events and the webhooks is English ASCII. Fields aresnake_case (conversation_id, occurred_at); times are ISO 8601 with a time zone; enums use fixed lowercase values, such as message, system_event and action for kind, or customer, ai_agent, human_agent and system for speaker. Verification levels are V0 to V4 and no_customer. Within /v1 no value ever changes meaning.
Next steps
Errors
every code, and what to do with it.
Events and the batch
idempotency and per-item errors in practice.
Spaces and keys
scopes, sources and the key format.
The Niadra API
every route, generated from the OpenAPI.

