Correlation by Trace ID
Axiom uses the trace ID as its correlation ID. Every unit of work (event processing, a task, a workflow run, a scheduled job run or a report) is identified by its trace ID. Every record produced by that work stores the trace ID, so given one trace ID you can find everything that belongs to that unit of work. See Tracing for the trace model itself.
Tables That Carry trace_id
| Table | Notes |
|---|---|
task |
Trace the task runs in |
report |
Trace of the report generation |
workflow_run |
Trace of the workflow run |
scheduled_job_run |
Trace of the scheduled job run |
routing_outcome |
Trace of the event that was routed |
activity_log |
Added in migration V66 |
ai_usage |
Added in migration V66 |
All columns are nullable UUIDs with no foreign key. Trace data has a bounded lifetime: when TraceCleanup
deletes a trace, it clears trace_id on activity_log, ai_usage, task, scheduled_job_run and report.
Finding All Records for a Trace
GET /traces/{id}returns the trace tree (nodes, tool calls).GET /activity?filterTraceId={id}returns the activity rows for the trace.GET /usage/ai?filterTraceId={id}returns the AI usage rows for the trace.- Tasks, workflow runs, scheduled job runs and reports expose a
traceIdfield.
For both filters, a malformed filterTraceId returns 400 Bad Request.
In the UI, the Activity log and AI usage pages show a Trace column that links to /logs/traces/:traceId,
and a Trace ID filter. Both pages accept a ?traceId= query parameter as a deep link, for example
/logs/activity?traceId=....
The Manager Decisions page links each evaluation to its event and its trace. Expanding a manager-evaluated
row shows the decisions recorded in the trace (with their reasoning) and the tasks each decision created.
The Manager's manager-evaluated / manager-error activity row and its ai_usage row carry both the event
ID and the trace ID, so the AI cost of an evaluation can be found from either.
Agent Calls: Joining the Caller's Trace
When an agent calls Axiom APIs through the Axiom MCP server, sdk-server.js sends two headers on every
request when the matching environment variables are set:
| Header | Source variable |
|---|---|
X-Axiom-Trace-Id |
AXIOM_TRACE_ID |
X-Axiom-Parent-Node-Id |
AXIOM_PARENT_NODE_ID |
CallerTraceFilter validates the headers in this order:
- The trace ID is a valid UUID.
- The trace exists.
- The trace status is
in-progress. - The parent node belongs to that trace. If the parent node header is missing, the trace's root node is used.
If any check fails, the headers are removed and ignored, and a debug log is written. A request never fails because of these headers.
Only POST /projects/{id}/tasks acts on a valid caller trace: the new task joins the caller's trace and its
task node is added under the caller's parent node, instead of a new user-action trace being created. The
task's createdBy stays user. Other endpoints ignore the caller trace.
Accepted Risk
An agent can keep its trace open by creating tasks that join it, because TaskTraceFinalizer keeps a trace
open while any task node is in progress. This is accepted: agent-created tasks are deliberate work that
belongs to the trace.
Known Limitations
- Any in-progress trace can be joined. There is no check that the caller belongs to the same project.
- AI editor and assistant sessions (
ActionTypeAiService,ReportAiService,ScriptAiService,ToolAiService,AssistantSessionManager) are not traced. Their AI usage rows have no trace ID. - Tasks that join a report, scheduled-job or workflow trace never complete it; the owning service does. If the owner finishes first, its trace is marked complete while the joined task may still be running. The task's own node still completes when the task finishes.
- Out of scope for now: MDC log correlation (#428), run and report ID columns (#424, #425), and the lineage view (#430).