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
requests, httpx, langfuse and langsmith when they are installed.
Set up
- FastAPI / Starlette
- Django
- Read in: the journey id is taken from the request (the
X-Journey-Idheader or the SDK’s cookie) and kept in a Python context variable for the rest of that request. - Passed along: outgoing
requestsorhttpxcalls 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 asorders-service,*.internalor Kubernetes*.svcnames); add your public services withinstrument(propagate_to=['https://agents.acme.com']). Third parties such as LLM vendors never receive it. - Stamped on traces: every Langfuse trace and every LangSmith run created during the request gets
metadata.journey_id, plus ajourney:<id>tag.
Non-HTTP entry points
Celery needs nothing more: a task queued during a request carries the id, and its traces are stamped. Callinstrument() 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:
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. Settracing_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:
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 keyjourney_id in:
- Langfuse: the trace’s metadata, not only a span’s
- LangSmith: the root run’s metadata
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.

