trace_id. The SDK creates it
before upload, stamps it on every TelemetryEvent you produce in the same trace context, and
stores it on the Trajectory. After upload, the backend returns its own trajectory_id, which
the SDK can stamp onto the buffered events for you.
The Mental Model
| ID | Owner | When known | Purpose |
|---|---|---|---|
trace_id | SDK / caller | Before upload | Correlates telemetry events and trajectories for the same application trace. |
trajectory_id | Backend | After upload | Stable backend identifier for the persisted trajectory row. |
session_id | SDK / caller | Before upload | Broader grouping across traces; defaults to trace_id in TraceContext. |
conversation_id | Caller / source system | Before upload | Source-system conversation identifier — not a correlation key. |
trace_id for the things you want correlated together. Usually that means one
trace_id per trajectory. If a single product trace intentionally produces multiple trajectory
records, reusing the same trace_id links them and their telemetry into one application trace.
You should rarely need to think about
trajectory_id directly. The SDK fetches it from the
upload response and stamps it onto matching events for you — see Upload Both Together.Recommended Workflow
Start withtj.start_trace() when instrumenting a product or agent run. The returned
TraceContext owns the trace_id and a buffer of TelemetryEvent objects.
trajectory.trace_id and every event in trace.events share the same trace_id.
Upload Both Together
tj.upload_trace() is the simplest path when you have a trajectory and its buffered telemetry
events at the same time. It uploads the trajectory first, reads the returned trajectory_id,
stamps matching events that share the same trace_id, then pushes telemetry — atomically.
| Key | Type | Description |
|---|---|---|
upload | dict | Raw response from tj.upload() (counts, errors, per-trajectory items). |
push | PushResult | Counts of pushed / skipped events plus any error messages. |
events | list[TelemetryEvent] | The events as actually sent, after trajectory_id stamping. |
unstamped_events | list[TelemetryEvent] | Events that were pushed without a trajectory_id because no match was found. |
Atomic guarantee
If any input trajectory’s upload comes back withstatus="error" (or status="skipped" with a
non-null error — the hash-conflict case), and there are unstamped events tied to that
trace_id, upload_trace raises RuntimeError and pushes no events. Partners who want
best-effort pushes should call upload and push_events separately.
Upload Events Before Trajectory
Events can be buffered locally before the trajectory exists. Onceupload returns the backend
trajectory_id, stamp it onto the buffered events before pushing.
Upload Trajectory Before Events
If the trajectory is uploaded first, read the returnedtrajectory_id and pass it when
creating later events.
Bring Your Own Trace ID
start_trace() accepts an explicit trace_id when you want to use an ID that already exists
in your system (a request ID, an OpenTelemetry trace ID, your own UUID, etc.):
Related
Telemetry Events
What
TelemetryEvent is, how to construct one, and how push_events works.Bring Your Own Data
Build trajectories from CSV / JSONL / OpenAI / Anthropic / Vercel formats.
API Reference
Full signatures for
start_trace, upload_trace, push_events, and friends.Core Concepts
Trajectories, Steps, Turns, and Messages.