IntegrationsMigrate ingestion to v4

Migrate custom ingestion to Langfuse v4

Use this guide if you send tracing data to Langfuse through a custom integration. It covers both migrations from the legacy /api/public/ingestion endpoint and updates to existing OpenTelemetry (OTEL) pipelines.

Using Python or JavaScript/TypeScript? Use the Langfuse SDK instead of building directly on the OTEL API. The SDK handles Langfuse attributes, propagation, media, filtering, and export for you. Follow the Python v3 → v4 or JS/TS v4 → v5 migration guide. For other languages, use the native OpenTelemetry API for your language and export spans to Langfuse.

Choose your migration path

Current setupMigration path
Custom events sent to /api/public/ingestionReplace legacy ingestion events with OTEL spans
OTEL without x-langfuse-ingestion-version: 4Update the exporter and audit the span format
OTEL with trace context only on the root spanPropagate context to every relevant span
OTEL using langfuse.trace.input or langfuse.trace.outputMove the overall input and output to the root observation
OTEL that re-exports a span to update data already accepted by LangfuseExport one complete span

Adding the x-langfuse-ingestion-version: 4 header selects the v4 ingestion path, but the header alone does not make a legacy or incomplete span format v4-ready. Complete the applicable migration steps below before switching production traffic.

V4-ready ingestion checklist

Your integration is ready for the observation-centric Langfuse v4 data model when it:

  • sends traces via OTLP over HTTP to /api/public/otel/v1/traces;
  • authenticates with Langfuse project keys via Basic Auth and sends x-langfuse-ingestion-version: 4;
  • represents every operation as one complete OTEL span with valid trace ID, span ID, timing, and parent context;
  • puts each operation's input and output on its observation, including the overall request and response on the root observation;
  • copies trace-wide context needed for filtering or aggregation to every relevant span;
  • uses explicit Langfuse metadata attributes for metadata that must be filterable;
  • exports a span once rather than re-ingesting the same ID to update it; and
  • targets observations, rather than legacy trace input/output, for evaluations.

See the OTEL endpoint configuration and complete attribute mapping for the stable transport and schema reference.

Migrate from the legacy ingestion API

The legacy /api/public/ingestion endpoint accepts Langfuse-specific create and update events. The OTEL endpoint instead accepts standard OTLP spans. Use your language's native OpenTelemetry API to create and finish spans, and configure its OTLP/HTTP exporter to send them to Langfuse.

Do not translate each legacy API request into a separate OTEL export. Build the operation locally, set its final attributes, end it, and let the OTEL SDK export the completed span.

Map legacy events to OTEL spans

Legacy ingestion conceptV4-ready OTEL representation
Trace recordOne OTEL trace ID shared by all observations. There is no separately ingested trace entity.
Trace namelangfuse.trace.name, copied to every span where the name must be queryable
Trace user, session, tags, release, version, environmentCorresponding langfuse.* attributes copied to every relevant span
Trace metadatalangfuse.trace.metadata.<key> copied to every relevant span
Trace input and outputlangfuse.observation.input and langfuse.observation.output on the root span
Span, generation, or eventAn OTEL span with langfuse.observation.type set to span, generation, or event
Parent observation IDOTEL parent span context
Observation input and outputlangfuse.observation.input and langfuse.observation.output on that span
Observation metadatalangfuse.observation.metadata.<key>
Model, model parameters, usage, cost, or prompt linkThe matching Langfuse OTEL attributes
Create followed by update eventsOne span assembled in memory and exported after it ends
Score eventThe dedicated Scores SDK or API, not an OTLP trace span

OTEL attributes support scalar values and arrays of scalar values. Serialize structured input, output, model parameters, usage, cost, and metadata values as JSON strings where required by the attribute mapping.

Configure the OTLP exporter

The exact setup depends on the OpenTelemetry SDK for your language. At minimum, configure:

OTEL_EXPORTER_OTLP_ENDPOINT="https://cloud.langfuse.com/api/public/otel"
OTEL_EXPORTER_OTLP_HEADERS="Authorization=Basic ${AUTH_STRING},x-langfuse-ingestion-version=4"

Use the host for your Langfuse region or self-hosted deployment. If your exporter uses signal-specific variables, configure OTEL_EXPORTER_OTLP_TRACES_ENDPOINT with /api/public/otel/v1/traces and set the same headers in OTEL_EXPORTER_OTLP_TRACES_HEADERS. See endpoint configuration and authentication for all regions and protocols.

Make an existing OTEL integration v4-ready

Existing OTEL integrations already use the correct transport, but their span attributes may still depend on the legacy trace model. Audit the following behaviors before adding the v4 header to production traffic.

Propagate trace context to observations

Langfuse v4 queries observations directly. Attributes that exist only on the root span are therefore unavailable when filtering or aggregating its child observations.

Copy these attributes to every span where they must be queryable:

  • langfuse.user.id or user.id
  • langfuse.session.id or session.id
  • langfuse.trace.name
  • langfuse.trace.tags
  • langfuse.trace.metadata.<key>
  • langfuse.version
  • langfuse.release
  • langfuse.environment

The recommended implementation uses OTEL Baggage with a processor that copies selected baggage entries to span attributes. See Propagating Trace Attributes to All Spans for the implementation pattern and its security considerations.

Move trace input and output

Langfuse v4 has no separate trace input or output. A trace is a group of observations correlated by trace ID. Put the overall application's request and response on its root observation:

  • replace langfuse.trace.input with langfuse.observation.input on the root span;
  • replace langfuse.trace.output with langfuse.observation.output on the root span; and
  • keep operation-specific input and output on the corresponding child spans.

The legacy trace input/output attributes remain available only for backward compatibility with trace-level LLM-as-a-judge evaluators. Migrate those evaluators to the observation level before removing the compatibility attributes.

Export complete immutable spans

It is normal to update an active span in memory before ending it. After Langfuse has accepted the span, however, do not export another record with the same span ID to add or change attributes.

Langfuse v4 does not reliably deduplicate repeated records on the read path. Re-ingesting the same ID can create duplicate observations, inflate metrics, and produce inconsistent query results. See How to update traces, observations, and scores.

Make metadata filterable

Unmapped OTEL span attributes appear under metadata.attributes, and resource attributes appear under metadata.resourceAttributes. These catch-all objects are useful for inspection but their nested keys are not directly filterable in Langfuse.

Use langfuse.trace.metadata.<key> for trace-wide dimensions and langfuse.observation.metadata.<key> for operation-specific dimensions. Copy trace-wide metadata to every span on which it must be filterable. See How Metadata Mapping Works.

Preserve a root observation

Export the application root span and preserve OTEL parent context so Langfuse can reconstruct the observation hierarchy. If a collector filters spans, confirm that it does not remove the root while retaining only its children.

For overall application or agent evaluations, target a root observation containing the overall input, output, and any other context required by the evaluator. Observation-level evaluators do not automatically read sibling or child observations.

Validate before cutover

Send a canary trace containing a root span, one generation, and one additional child span. Give it a unique tag or metadata value, then confirm:

  1. All three observations appear in the unified observations table without the legacy ingestion delay.
  2. Their hierarchy, start times, and end times are correct.
  3. The root observation contains the overall input and output.
  4. User, session, tags, environment, version, release, trace name, and required metadata are available on every observation where you need to filter or aggregate them.
  5. The generation contains its model, model parameters, token usage, cost, and prompt link where applicable.
  6. Any observation-level evaluator matches the intended observation and receives all mapped variables.
  7. The Observations API v2 returns the expected observation rows and fields.

Do not dual-send the same logical spans with the same IDs through legacy REST and OTEL into the same project. Validate in a separate project or use distinct canary trace IDs. Otherwise, the v4 data model may contain duplicate observations.

After the canary passes, shift traffic to OTEL and monitor ingestion errors, observation counts, and evaluation execution. Stop sending trace, span, generation, and event payloads to /api/public/ingestion; continue using the dedicated APIs for resources such as scores.


Was this page helpful?

Last edited