Skip to main content
DataFramer puts an AI trace into a user’s journey when the trace carries the same journey id as the user’s events. The browser SDK creates that id and sends it to your backend with each request. This page is the second step: your backend writes the id onto every Langfuse trace or LangSmith run it creates. dataframer-journey, a small Python library, does that for you, so you never pass the id around by hand. Skip this page if your linking rule links by a value your traces already carry, or by user and time gap.

Install

It has no dependencies of its own. It hooks into requests, httpx, langfuse and langsmith when they are installed.

Set up

From then on, three things happen on every request:
  1. Read in: the journey id is taken from the request (the X-Journey-Id header or the SDK’s cookie) and kept in a Python context variable for the rest of that request.
  2. Passed along: outgoing requests or httpx calls to your own services carry the id forward, so a chain of services stays connected. By default only private addresses get it (localhost, private IPs, and internal names such as orders-service, *.internal or Kubernetes *.svc names); add your public services with instrument(propagate_to=['https://agents.acme.com']). Third parties such as LLM vendors never receive it.
  3. Stamped on traces: every Langfuse trace and every LangSmith run created during the request gets metadata.journey_id, plus a journey:<id> tag.

Non-HTTP entry points

Celery needs nothing more: a task queued during a request carries the id, and its traces are stamped. Call instrument() in the worker process too; on Django, AppConfig.ready() already runs there. Other queue consumers, scheduled jobs, raw ThreadPoolExecutor or run_in_executor work and WebSocket handlers do not pass through the middleware, so hand the id over from your payload:
Langfuse SDK 3.0 to 3.8 is not stamped. Those releases have no way to attach attributes to every trace, so the library logs a warning and skips them; upgrade to 3.9.0 or newer. Langfuse v2, v4 and v5 and every LangSmith release are stamped. If you cannot upgrade, call get_journey_id() and stamp the trace yourself, as in Without this library.

Use PostHog’s session id

If PostHog runs in your frontend, its session id can serve as the journey id, with no DataFramer code in the browser. Set tracing_headers: ['api.acme.com'] (your API’s hostname) in posthog.init, so PostHog sends X-POSTHOG-SESSION-ID on requests to your API, then read that header:
Your traces then carry the PostHog session id. Import your PostHog events, then add properties.$session_id as a path on events to the built-in Journey id field. Your traces already carry it at metadata.journey_id, so the default linking rule links them. If your API is on another origin, its CORS settings must allow PostHog’s three headers, X-POSTHOG-SESSION-ID, X-POSTHOG-DISTINCT-ID and X-POSTHOG-WINDOW-ID. PostHog adds them only once that part of its library has loaded, so the first requests on a page may carry none.

Without this library

For a backend in another language, or a tracing setup this library does not stamp, write the id onto the trace yourself. DataFramer needs one thing: the journey id, stored under the key journey_id in:
  • Langfuse: the trace’s metadata, not only a span’s
  • LangSmith: the root run’s metadata
The id is either your own, the one you pass to startJourney() in the browser, or the value of the request’s X-Journey-Id header. The browser SDK sends that header only to the URLs in propagateHeaderTo, so set it even when your backend shares the page’s origin, for example propagateHeaderTo: ['/api']. Work that leaves the request, such as a queue job or a call to another service, needs the id passed along with it.

CORS

If your frontend and backend are on different origins, the browser SDK must send the id as a header; see Getting the journey id to your own backend. This library then allows that header in your CORS preflights by itself, for the origins your CORS settings already allow; it never allows a new origin.

Next steps

Send Events from Backend

Send events that happen only on your server, such as a completed order.