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:
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 theturns 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
Thegen_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.
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. Keepgen_ai.conversation.id stable for the whole conversation: it becomes the conversation_id.
3. Turn on content capture
Mostgen_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 withGET /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 thecontext_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.

