Export To Langfuse
Updated 14 hours ago • June 2, 2026
Langfuse ingests Kortyx traces over OpenTelemetry. Kortyx remains backend-neutral: there is no Langfuse-specific Kortyx package and your app owns the exporter setup. Wire @kortyx/otel as usual, then map a small number of Kortyx attributes to Langfuse-native names so the UI renders trace input, output, tags, and optional managed-prompt linkage correctly.
This guide assumes you have already read OpenTelemetry. Everything here is additive on top of that setup.
Install
@kortyx/otel is required regardless of backend — it produces the spans. @langfuse/otel provides the LangfuseSpanProcessor that exports them to Langfuse.
Good to know: Add
@langfuse/tracingonly when your app also needs Langfuse-specific manual observations or context propagation outside Kortyx. It is not required for the Kortyx adapter path below.
Configure the span processor
Replace the OTLP exporter from the OpenTelemetry guide with Langfuse's span processor, or run both side-by-side.
The Langfuse span processor batches spans by default. In short-lived runtimes, flush it before the process exits or the runtime freezes:
Next.js serverless routes
Register your OTel bootstrap from instrumentation.ts so tracing starts before the agent:
In a Vercel or Next.js serverless route, schedule a flush after the response completes:
Good to know: If your Next.js build creates separate instrumentation and route bundles, keep the processor in a shared
globalThis-backed singleton module. That ensures the route flushes the same processor instance registered with your tracer provider.
Translate Kortyx attributes to Langfuse attributes
Kortyx emits attributes under the kortyx.* namespace. Langfuse reads its own langfuse.* keys. Use the mapAttributes hook on the OTel adapter to translate.
The mapping reference:
After the first request, Langfuse should show a trace named agent · <workflow-id> with the Kortyx run span as its root. If captureContent is enabled, the trace card and observations should include their input and output.
Optional: link managed prompts
Tracing works without a prompt store. If a node uses an external managed prompt, pass its identity through useReason({ telemetry: { prompt } }) so Langfuse links that generation to the corresponding managed prompt.
Langfuse expects langfuse.observation.prompt.version to be an integer. If your prompt store uses string versions such as "v1", keep that value in telemetry.prompt.metadata for filtering instead of mapping it as the Langfuse prompt version.
Map tags
Tags work the same way as in the generic OpenTelemetry guide — pass them through telemetry.tags and the mapAttributes hook above translates kortyx.trace.tags to langfuse.trace.tags. Once translated, tags become filter facets on the Langfuse trace list.
Langfuse treats langfuse.trace.tags as trace-level context, even when it sees the attribute on a child observation. Put stable tags such as environment and service on createAgent(...); use per-useReason tags only when a generation should add searchable context to the whole trace.
Score traces from the client
Langfuse scores (thumbs up/down, hallucination flags, deflection rates) are written against a traceId. Kortyx surfaces the active trace id to the client via the trace stream chunk and ChatMsg.traceId — no header capture or wrapper spans needed.
A minimal feedback flow:
Then write the score with the Langfuse client in a server route:
Good to know: Accepting a raw
traceIdfrom the browser lets any authenticated user write scores against any trace id they can guess. If that matters for your app, mint a signed token server-side keyed to{ traceId, userId }while the stream is open (you can wrap the kortyx stream and inject the token as an extrastructured-datachunk after the nativetracechunk), then verify it in the feedback route.
What to read next
- Read OpenTelemetry for the base adapter setup, identity propagation, and prompt metadata.
- Read
useChat(...)for howChatMsg.traceIdflows through the React client.