Traces

Receive OTLP traces and map GenAI spans into runs, steps and tool calls.

The traces endpoint receives OTLP/HTTP exports from server side instrumentation and turns GenAI spans into runs. It accepts an API key on https://backend.assistant-api.com; Aui-Workspace-Id is optional on this route, unlike the other API key routes, and the general host and credential rules are in Conventions.

Export traces

POST /v1/traces
curl
curl https://backend.assistant-api.com/v1/traces \
  -H "Authorization: Bearer $ASSISTANT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "resourceSpans": [] }'
Field or headerTypeRequiredRules
AuthorizationheaderyesAn API key. A JWT is refused.
Aui-Workspace-IdheadernoDefaults to __OTLP__ on this route.
Aui-User-IdheadernoDefaults to __SYSTEM__.
Content-Typeheaderyesapplication/json, application/x-protobuf or application/protobuf.
Content-Encodingheadernogzip, deflate or identity.
Content-LengthheadernoA declared body larger than 4,194,304 bytes is refused. The streamed body is also counted against the same cap.
Request bodyOTLP exportyesAt most 4,194,304 bytes, or 4 MB.
Request
{
  "resourceSpans": []
}
Response
{}
Response with partialSuccess
{
  "partialSuccess": {
    "rejectedSpans": 1,
    "errorMessage": "Trace 0123456789abcdef0123456789abcdef exceeds the 500 span limit"
  }
}

When all spans are accepted, the response is {}. When spans are rejected, partialSuccess contains rejectedSpans and errorMessage; the message joins the per trace errors, or uses <n> spans were rejected when there is no per trace error.

StatusBodyWhen
200{}Every span was accepted.
200{ "partialSuccess": { "rejectedSpans": n, "errorMessage": "..." } }One or more spans were rejected while the export was processed.
400{ "error": "<parser message>" }The OTLP body is malformed. The parser message is returned in error.
401{ "error": "..." }Authorization is missing.
403{ "error": "This endpoint requires an API key" }The caller supplied a JWT instead of an API key.
413{ "error": "Export exceeds the 4 MB limit" }The declared or streamed body is over 4 MB.
415{ "error": "Send application/json or application/x-protobuf" }Content-Type is not supported.
415{ "error": "Unsupported content encoding <enc>" }Content-Encoding is not gzip, deflate or identity.

Span mapping

The receiver recognizes the six operation names chat, text_completion, embeddings, execute_tool, invoke_agent and create_agent, read from gen_ai.operation.name or, when that attribute is absent, from the first word of the span name. The first recognized GenAI span without a GenAI ancestor starts the trace's run, later roots of the same trace join it, and every recognized GenAI descendant belongs to that run. A span linked through non GenAI spans is reparented to its nearest GenAI ancestor. Spans without a recognized operation are rejected and counted in partialSuccess.

OperationStored result
chatA generation span. When its GenAI parent is a tool span, it is that tool's sampling call.
text_completionA generation span.
embeddingsA generation span.
execute_toolA tool call span.
invoke_agentA root starts a run, and the root itself is not stored as a span. Its agent name, identity and remaining attributes land on the run.
create_agentA root starts a run, but the span is not stored as a span.

Stored generation spans become steps, except for generation spans under tool spans, which are sampling spans. The first generation step is initial; later generation steps are tool-result when a tool ran between the previous and current generation, otherwise continue. Tool spans without a generation parent are attached to the generation that finished latest before the tool began.

Receiver attributes

The receiver reads these span and resource attributes. Unmapped span attributes remain in the stored span attributes.

AttributeUsed for
gen_ai.operation.nameOperation selection.
gen_ai.provider.name, gen_ai.systemProvider selection, with gen_ai.system as the legacy form.
gen_ai.request.model, gen_ai.response.modelModel selection, with the response model preferred.
gen_ai.usage.input_tokens, gen_ai.usage.output_tokens, gen_ai.usage.cache_read.input_tokens, gen_ai.usage.reasoning.output_tokensInput, output, cached input and reasoning usage.
gen_ai.response.finish_reasonsSpan and run outcome.
gen_ai.response.time_to_first_chunkFirst token timing.
gen_ai.tool.name, gen_ai.tool.call.id, gen_ai.tool.call.arguments, gen_ai.tool.call.resultTool name, call id, arguments and result.
gen_ai.input.messages, gen_ai.output.messagesGeneration input and output.
gen_ai.conversation.idThread lookup inside the project. If it does not resolve, the id is preserved in the run attributes.
gen_ai.agent.nameAgent name on the run.
error.typeError type and error code.
user.id, enduser.idThe run creator, in that order.
service.nameRun service name from resource attributes.
service.versionRun release from resource attributes.
deployment.environment.name, deployment.environmentRun environment, with the former preferred.

Limits and identifiers

Each trace keeps at most 500 spans. Extra spans are rejected and counted in partialSuccess with Trace <id> exceeds the 500 span limit. Run attributes and span attributes are each capped at 16,384 bytes. When a cap is exceeded, the receiver drops the largest entries first and adds truncated: true.

The run id is deterministic: run_<sha256(projectId+traceId+rootSpanId) first 24 hex>. A span's primary key is <traceId><spanId>, and spans upsert on (project_id, trace_id, span_id), replacing every span column with the exported value. An existing run with the same (project_id, trace_id) is locked and updated, and the daily run rollup removes the previous values before applying the new ones.

Re export and client reports

Re exporting a trace keeps the client owned source, created_by, created_at, thread_id, assistant_id, message_id, tags, environment, release and first_token_ms, as well as aborted and disconnected outcomes. Existing attributes whose key starts with client. always win. A client report with the same trace id merges into the existing run under these rules; the full field mapping is in Traces.

The route inserts runs and spans in a transaction, records the thread run on the first insert that has a thread, recomputes span_index, assigns parents and step types, and updates the daily rollup. A transaction failure for one run rejects that run's spans and adds its message to partialSuccess; other runs in the export continue. Re exporting is idempotent for the deterministic run and span keys, and spans are indexed by start time, then relative start time and id.