Contents
Moving from Langfuse to Moda is three changes to how your app sends traces, and one import for the history you already have.
The changes depend on how your app talks to Langfuse today. If your spans come from OpenTelemetry instrumentation, you change where they are exported. If you call Langfuse's own SDK, you swap it for the Moda SDK. Either way you carry your session and user IDs over, so each agent run stays one trace under the right user. Then you import your Langfuse history, so the traces from before the switch sit in Moda next to the ones from after it.
What moves, and what stays in Langfuse
Everything about your traces moves:
- New traces. Spans go to Moda's OTLP endpoint, or through the Moda SDK.
- Sessions. Your Langfuse session IDs become Moda trace IDs, so a multi-turn conversation is still one trace.
- Users. Your Langfuse user IDs become Moda user IDs, so problems are still ranked by the people they affect.
- History. The Langfuse importer brings over past traces with their observations, users, and scores.
Some things are Langfuse features with no Moda equivalent, and they stay where they are:
- Prompts served at runtime. Langfuse serves versioned prompts to your app and caches them in its SDK. Moda versions prompt files from your repository and does not sit in the request path, so there is nothing to fetch.
- Datasets and experiments. Moda builds eval sets from your production traces instead of curated datasets. Experiment runs and their dataset items are not imported.
- Evaluator configuration. Moda's detections run on every trace without an evaluator to write or a sampling rate to set, so there is no rule to port.
Step 1: Point your OpenTelemetry exporter at Moda
If your spans come from OpenTelemetry GenAI instrumentation, OpenLLMetry, or the Vercel AI SDK's telemetry, Moda reads them as they are. Change the exporter's endpoint and its authorization header:
export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT="https://moda-ingest.modas.workers.dev/v1/traces"
export OTEL_EXPORTER_OTLP_TRACES_HEADERS="Authorization=Bearer YOUR_MODA_API_KEY"
export OTEL_EXPORTER_OTLP_TRACES_COMPRESSION="none"
The last line matters. Moda ingest accepts uncompressed request bodies, and the OpenTelemetry spec leaves the default compression to each SDK, so set it to none explicitly. If you export through an OpenTelemetry Collector, set compression: none on the Moda exporter, as in step 3.
For direct export, set moda.user_id on each span to the same user ID you sent to Langfuse. Changing the endpoint does not translate langfuse.user.id or span-level user.id, so the user ID needs an explicit mapping. If you use a Collector, step 3 copies it for you.
Step 2: Replace Langfuse SDK tracing with the Moda SDK
Langfuse's own SDK, including the observe decorator and the Langfuse OpenAI wrapper, writes model input and output to langfuse.observation.input and langfuse.observation.output. Moda does not read those attributes, so repointing that exporter is not enough.
Remove those wrappers and initialize the Moda SDK once at startup. It instruments the OpenAI and Anthropic clients, including streamed responses and tool calls, without changes to your provider code:
import os
import moda
moda.init(api_key=os.environ["MODA_API_KEY"])
moda.conversation_id = session_id
moda.user_id = user_id
In Node.js, await Moda.init(process.env.MODA_API_KEY!) before you create your provider clients, import withContext from moda-ai and wrap each request in withContext(sessionId, userId, async () => { /* your existing agent code */ }). This keeps IDs isolated across concurrent requests. Reserve the process-wide Moda.conversationId and Moda.userId setters for scripts handling one session at a time. If you would rather not do it by hand, moda init has your coding agent add the SDK for you.
Step 3: Carry your session and user IDs over
Moda groups spans into one trace by moda.conversation_id, or gen_ai.conversation.id, across OpenTelemetry traces, and attributes them to a user by moda.user_id. It does not read Langfuse's session or user keys. With the Moda SDK, set the conversation ID to the value you used as the Langfuse session ID and the user ID to the Langfuse user ID, as in step 2.
If your spans pass through a Collector, you can copy the Langfuse keys without touching application code:
processors:
attributes/moda-session:
actions:
- key: moda.conversation_id
from_attribute: langfuse.session.id
action: insert
- key: moda.user_id
from_attribute: langfuse.user.id
action: insert
- key: moda.user_id
from_attribute: user.id
action: insert
exporters:
otlphttp/moda:
traces_endpoint: https://moda-ingest.modas.workers.dev/v1/traces
headers:
Authorization: Bearer ${env:MODA_API_KEY}
compression: none
Add attributes/moda-session to your traces pipeline and replace the Langfuse exporter with otlphttp/moda. The copies work span by span: a session stays one trace only when every span carries the session ID, and a span is attributed to a user only when it carries the user ID. The insert action keeps any moda.user_id a span already has. The user.id rule copies Langfuse's generic user key; drop it if other services send user.id through the same Collector with a broader meaning. Langfuse also accepts the generic session.id key; copy that one too only if no other service sends session.id through the same Collector with a broader meaning, such as a browser session.
Step 4: Import your Langfuse history
Moda supports migrating your Langfuse history. In Moda, open Settings, then Integrations, then Import traces, and choose Langfuse:
- Pick your Langfuse Cloud region (US, EU, Japan, or HIPAA), or enter the URL of your self-hosted Langfuse.
- Paste a Langfuse public key and secret key from the project's settings. Moda stores them encrypted for the import and deletes them when it completes or is cancelled.
- Choose the project and a date range of up to 30 days. Run another import for each earlier window.
- Start the import. It runs in the background, so you can leave the page, and an owner or admin can pause, resume, or cancel it.
Each imported trace keeps its shape:
- Traces and observations. Every observation becomes a span under its parent, with its input, output, model, token usage, and timing. Generations, tool calls, and the other spans keep their type.
- Sessions. Traces that share a Langfuse session ID become one Moda trace, the same way step 3 groups your new traffic. A trace with no session ID becomes a Moda trace of its own.
- Users. The Langfuse user ID becomes the Moda user ID, so imported and new traces from the same person land under one user.
- Errors. Observations at the error level arrive as failed spans with their status message.
- Scores, tags, and metadata. Langfuse scores, whether from evaluators, annotations, or the API, are kept with the trace or observation they belong to, along with tags, release, environment, and metadata.
A few limits are worth knowing before you start. The importer copies what exists in Langfuse when it runs; it is not a sync, so traces created during the import may need a second run, and deletions in Langfuse are not carried over. Importing the same window twice does not duplicate anything. Langfuse's own retention and API rate limits still apply to what can be read.
What you get on day one
Once traces flow, Moda's built-in detections run on every new trace as it arrives: user frustration, tool failures, laziness, and hallucinations checked against the tool results the agent actually received. Failures are grouped into problems ranked by how many users they affect, each linked to its traces.
When you change a prompt or a model, replay it against the traces that failed before it ships. A simulated user plays each conversation, every tool call is simulated from the tool calls recorded in your traces, and a judge scores your current configuration and the change side by side. There are no tool mocks to write and no real tool is called.
The full comparison, including the two places where Langfuse still leads, is on Moda vs Langfuse.
Frequently asked questions
Do I have to re-instrument my app?
Not if it already emits OpenTelemetry GenAI, OpenLLMetry, or Vercel AI SDK spans. Change the exporter's endpoint, API key, and compression. Calls traced with Langfuse's own SDK move to the Moda SDK, which instruments the OpenAI and Anthropic clients with one init call.
Does the Langfuse importer bring my prompts and datasets?
No. It imports traces with their observations, sessions, users, and scores. Prompts, datasets, experiments, and evaluator configuration stay in Langfuse.
Can I run Langfuse and Moda at the same time while I switch?
Yes. A Collector can export the same spans to both, so you can compare them on live traffic before you turn the Langfuse exporter off.
Does the importer work with self-hosted Langfuse?
Yes, as long as Moda can reach your instance's URL over HTTPS. Langfuse Cloud works in all four regions.
