OpenTelemetry
Updated 14 hours ago • June 2, 2026
Kortyx can emit server-side OpenTelemetry spans for chat runs, workflow nodes, useReason(...), and provider calls.
Use this when you want to inspect:
- which session and user triggered a run
- which workflow and node handled the request
- which provider/model was called
- token usage and finish reasons
- prompt identity and version metadata
- interrupts, retries, and failures
Kortyx does not configure your collector or tracing backend. Your app owns the OpenTelemetry SDK/exporter setup.
Install
Configure OpenTelemetry on the server
Initialize OpenTelemetry before creating the agent.
Good to know: Keep OpenTelemetry setup in server bootstrap code. React clients should pass stable identifiers such as
sessionId, while provider calls and token usage stay on the server. If your tracing endpoint requires headers, setOTEL_EXPORTER_OTLP_TRACES_HEADERSin the environment instead of adding header parsing to the example.
Wire Kortyx tracing
Pass the OpenTelemetry adapter when creating the agent.
See Capture inputs and outputs before enabling raw prompt or completion capture.
Call the agent normally and pass request context from trusted server code.
The trace contains a run span, node spans, useReason spans, and generation spans. Generation spans include standard gen_ai.* attributes for provider/model metadata and token usage. MCP tool loops add useReason.tool-step.* and useReason.tool-call.* events plus kortyx.tool.* count attributes on the useReason span.
Pass identity from React
React clients can pass request context through the route transport. For authenticated apps, treat frontend context as request hints and set the trusted userId and tenantId on the server from your auth/session layer.
On the route, parse the chat body, read the authenticated user, and forward the merged context to the agent.
The accountId from the React client is sent in body.context, merged into the server context, and recorded as kortyx.trace.metadata.accountId. The resulting spans also include session.id, gen_ai.conversation.id, user.id, and kortyx.tenant.id when those values are present.
If you want accountId as a first-class attribute in your tracing backend, map it in the adapter:
Good to know: If your app only has an anonymous browser identifier, pass it as app context such as
anonymousIdand map it explicitly increateOpenTelemetryTraceAdapter({ mapAttributes }). UseuserIdfor authenticated server-derived identities.
Attach prompt metadata
If your node loads prompts from an external prompt store, pass prompt identity through useReason({ telemetry }).
Kortyx records generic prompt attributes such as gen_ai.prompt.name, gen_ai.prompt.version, and kortyx.prompt.*.
Customize span attributes
Use mapper hooks when your OpenTelemetry pipeline expects additional attributes.
Good to know: Prompt text alone is not enough to link a generation to a managed prompt. Pass prompt name, version, type, and any prompt-store metadata in
telemetry.prompt.
Capture inputs and outputs
Raw prompts and model outputs can contain sensitive data. Kortyx leaves content capture off by default.
Enable it only when your app has an approved retention and redaction policy.
Attach tags to traces
Pass tags in telemetry to label runs with searchable strings. They are emitted as the OpenTelemetry attribute kortyx.trace.tags on the relevant span and most backends expose them as filter facets.
App-wide tags applied to every kortyx.run span:
Per-call tags applied to the matching useReason span:
If your tracing backend expects a different attribute name (for example, Langfuse looks for langfuse.trace.tags), translate kortyx.trace.tags in mapAttributes. See Export To Langfuse for the exact wiring.
Read trace ids from the client
When @kortyx/otel is wired on the agent, the orchestrator emits a trace stream chunk as soon as the kortyx.run span opens:
@kortyx/react reads it off the wire and stamps every assistant ChatMsg produced during that turn with traceId, spanId, and runId. You do not need to capture response headers or open your own wrapper spans — the values are first-class fields on the message.
The trace fields are undefined until a trace chunk arrives, which happens when an OTel trace adapter is configured on the agent. Without @kortyx/otel (or another adapter that supports getActiveContext) wired into createAgent({ telemetry: { trace } }), no trace chunk is emitted and msg.traceId stays undefined.
Restored-from-storage messages keep their trace ids — createBrowserChatStorage serializes them with the rest of the ChatMsg.
What to read next
- Read Export To Langfuse to use Langfuse as your tracing backend with the right attribute names.
- Read Runtime Context to pass request metadata into nodes.
- Read Runtime Persistence if your traced flows use interrupts and resume.