Skip to main content
If your agent already emits OpenTelemetry traces with the gen_ai.* attributes, it can write to Niadra without new calls in the conversation path: point an OTLP exporter at Niadra and every model call becomes the messages of the conversation, with who spoke and when. This is the fastest way to start building memory from an agent you do not want to touch, and a common way to capture a vendor platform that exports traces but has no Niadra integration. Reading still happens through context(), the history tools or MCP. OpenTelemetry covers the write side.

The endpoint

Niadra accepts OTLP over HTTP, with the body as JSON (Content-Type: application/json) or protobuf (application/x-protobuf), gzipped or not:
Authenticate with the source key in Authorization: Bearer, the same key your agent would use for the SDK, with the track scope. The answer is the standard OTLP one: each rejected message counts once in partialSuccess.rejectedSpans, with its position and reason in errorMessage.

The levels

What the spans produce depends on what they carry. With the turns feature on in the space, each turn of the spans also becomes a turn record: A bronze record keeps the digests of what it read, not the values: its mode is hash_only, unless the space’s recording says otherwise for the source. A value the exporter cut, by an attribute length limit or by dropped attributes, marks its call truncated: true.

What Niadra reads from a span

The gen_ai semantic conventions are still changing, and several generations of attributes live side by side in the libraries in use today. Niadra reads them in cascade, so you do not have to pin a library version for us: From a tool call (gen_ai.operation.name equal to execute_tool), Niadra reads gen_ai.tool.name, gen_ai.tool.call.id (or the span id) and, when the instrumentation captures content, the arguments and the result, which become a kind: tool call of the record with the span’s duration. From a model call, the model (gen_ai.response.model, or gen_ai.request.model), gen_ai.usage.input_tokens and gen_ai.usage.output_tokens, the cached-token count when the instrumentation reports one, and the input and output messages. A few things OpenTelemetry has no attribute for, so you set them yourself, on the span or on the resource: A span or span event named niadra.exposure, emitted when a list reaches the person (not when the tool returns it), carries niadra.exposure.id, niadra.exposure.list, niadra.exposure.page, niadra.exposure.offset, niadra.exposure.visible_k and niadra.exposure.items (JSON with pos, ref and shown), and becomes a presented interaction of the customer’s signals. Every span repeats the conversation so far, so each message gets the idempotency key of its position in the conversation: a re-exported history deduplicates instead of duplicating. The spans of one turn may arrive in more than one export: Niadra merges them into the same record (a later export adds the calls and exposures the record lacked and widens its start and end), and an export that adds nothing counts as a duplicate. The SDK may copy niadra.turn.id and niadra.pack.hash from OpenTelemetry baggage to the spans of the turn, so they reach spans your code never touches; baggage travels in the headers of every outgoing call, including calls to third parties, so only those two values may go in it, never a handle, an exposure or any value that names a person.
Span attributes can end up in every backend your collector exports to. Prefer an internal id (app_user_id or enduser.id) over a phone number or an e-mail, and send the Niadra attributes only on the pipeline that goes to Niadra. Personal data never travels in a URL.

Steps

1. Point an exporter at Niadra

Add an OTLP/HTTP exporter next to the one you already have, as JSON or protobuf: the Node.js and Python exporters work as they are, straight to Niadra or through an OpenTelemetry Collector, when you prefer to keep a pipeline for Niadra alone.

2. Set the channel and the customer

Set the Niadra attributes on the span that carries the messages, or once on the resource for a process that serves one channel. Keep gen_ai.conversation.id stable for the whole conversation: it becomes the conversation_id.
Attributes are read from the span itself and from its resource. When your model-client instrumentation writes the messages on a child span, set the Niadra attributes on the resource, or copy them to that span with a span processor.

3. Turn on content capture

Most gen_ai instrumentations leave message content out by default. Niadra needs it to build memory, so enable content capture in the instrumentation you use (for many Python instrumentations, OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true). If you prefer to keep content out of your other backends, run a second pipeline for Niadra only.

4. Confirm the events arrived

A span becomes messages with the same guarantees as a batch: the raw content is stored before the answer and events are ordered by the time they happened. Check the coverage of the source, on the Systems screen of the Console or with GET /v1/sources/coverage, as a person with the integration role or with an admin key: what arrived (received) and what was rejected (rejected), day by day.

When to use the SDK instead

OpenTelemetry records what the model saw and said. The SDK adds what traces do not carry: the context read before the answer and the context_stamp of each agent turn, verification with verify(), links between handles with identify(), agent actions with closes, handoffs and the end of the conversation. Those feed the context use measurement. A common path is to start with OpenTelemetry to build memory from day one, then add context() and the SDK writes to the agents that answer customers.

Next steps

Events and the batch

what an event is and how it is deduplicated.

Quickstart

reading context with the SDK.

Receive OTLP traces

the endpoint reference.

Webhooks from your systems

events from CRM, ERP and help desk.