Skip to main content
The same rules apply to every route of the Niadra API: the data API of your space and the control API. Knowing them once saves you from reading them again on each endpoint.

Address

Every space has a stable address, with its region in the name:
The key carries the region and the space, never the cell that serves them, and the SDKs derive the address from the key. When a space moves between cells, the address stays the same; during the move, the old cell answers 421 and the SDKs retry at once on a new connection. The internal topology is never part of the contract. The control API, for tenants, projects, sources, keys, people, configuration and usage, lives at https://control.api.niadra.com.

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 a Deprecation header on every response of the old version.

Authentication

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 and analytics. 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 an Idempotency-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.
The batch uses the 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.

Errors

Errors answer application/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 429 rate_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.

Caching and ETag

Every deterministic read has an ETag: the context, the history navigation and business objects. 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 of forget. 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. A test in our CI with a canary value makes sure none of this appears in a URL, a log, a span or an error message. Ids that are not personal data, such as a profile id, an item id, a conversation_id or an object id, may appear in paths.

Tracing

Send a W3C traceparent 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.

Speed

No LLM runs in the path of context() or of the navigation.

Starting caps of a space

These are starting values, set in your contract. 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 are snake_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.