Skip to main content
Telemetry events and trajectories are linked by a caller-owned 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

Use one 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.
Start with tj.start_trace() when instrumenting a product or agent run. The returned TraceContext owns the trace_id and a buffer of TelemetryEvent objects.
At this point trajectory.trace_id and every event in trace.events share the same trace_id.

Upload Both Together

trajectory.lib.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 ingests telemetry.
The return value is a TraceUploadResult with three fields:

Failure behavior

upload_trace serializes every event before uploading trajectories because it needs each event’s trace_id for correlation. If any event cannot be represented as JSON, the call raises TypeError and sends neither trajectories nor events. If any trajectory fails, upload_trace raises PartialUploadError and ingests no events. If an event cannot be correlated because a successful upload returned no trajectory ID, it raises TraceCorrelationError and ingests no events. The same error is raised if the upload response omits an item for any submitted trajectory, regardless of whether matching events were supplied. Call upload_trajectories and ingest_events separately when best-effort ingestion is desired. Events that are JSON-serializable but fail telemetry field validation are different: trajectories remain uploaded, valid events are ingested, and invalid events appear in result.telemetry.failures.

Upload Events Before Trajectory

Events can be buffered locally before the trajectory exists. Once upload_trajectories returns the backend trajectory_id, stamp it onto the buffered events before ingestion.

Upload Trajectory Before Events

If the trajectory is uploaded first, read the returned trajectory_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.):
If you omit it, the SDK generates a UUID4 hex string.

Telemetry Events

What TelemetryEvent is, how to construct one, and how ingest_events works.

Bring Your Own Data

Build trajectories from CSV / JSONL / OpenAI / Anthropic / Vercel formats.

API Reference

Full signatures for start_trace, upload_trace, ingest_events, and friends.

Core Concepts

Trajectories, Steps, Turns, and Messages.