Skip to main content

REST API: Observability

GET /v1/observability/{thread_id} reconstructs a run trace for a thread from the events its runs emitted: a span tree, a flat event list, aggregated token usage, and call counts. It is what the playground's trace view renders.

Base path: /v1/observability

Permission: graph:read.


GET /v1/observability/{thread_id}

Path parameters:

ParameterTypeDescription
thread_idstringThread whose runs to inspect

Query parameters:

ParameterTypeDefaultDescription
run_idstringlatest runReturn this specific run instead of the most recent one

Response:

{
"success": true,
"data": {
"thread_id": "my-thread-1",
"run_count": 2,
"run_ids": ["run-a", "run-b"],
"run": {
"run_id": "run-b",
"thread_id": "my-thread-1",
"status": "done",
"started_at": 1775000000.0,
"finished_at": 1775000004.2,
"duration_ms": 4200.0,
"spans": [
{
"id": "root",
"name": "graph",
"kind": "root",
"parent": null,
"start_ms": 0.0,
"duration_ms": 4200.0
},
{
"id": "s1",
"name": "node: MAIN",
"kind": "node",
"parent": "root",
"start_ms": 5.0,
"duration_ms": 1800.0
},
{
"id": "s2",
"name": "llm.generate",
"kind": "llm",
"parent": "s1",
"start_ms": 10.0,
"duration_ms": 0.0,
"model": "gpt-4o-mini",
"input_tokens": 812,
"output_tokens": 143
},
{
"id": "s3",
"name": "tool: get_weather",
"kind": "tool",
"parent": "s1",
"start_ms": 1750.0,
"duration_ms": 0.0
}
],
"events": [
{
"id": "e1",
"type": "message",
"node": "MAIN",
"offset_ms": 1710.0,
"summary": "llm.generate · 955 tokens"
}
],
"usage": {
"prompt_tokens": 812,
"completion_tokens": 143,
"reasoning_tokens": 0,
"total_tokens": 955
},
"llm_calls": 1,
"tool_calls": 1,
"iterations": 2
}
}
}

Top level

FieldTypeDescription
thread_idstringThe thread that was queried
run_countintegerNumber of runs recorded for the thread
run_idsstring arrayEvery recorded run id, so a client can offer a run picker
runobject or nullThe requested (or latest) run. null when the thread has no recorded runs.

Run

FieldTypeDescription
run_idstringRun identifier
thread_idstringOwning thread
statusstringrunning, done, error, or stopped
started_atnumber or nullUnix timestamp (seconds, fractional)
finished_atnumber or nullUnix timestamp (seconds, fractional)
duration_msnumberWall-clock run duration in milliseconds
spansarrayThe span tree, see below
eventsarrayFlat event list, see below
usageobjectAggregated token usage
llm_callsintegerNumber of records that carried token usage
tool_callsintegerNumber of tool calls observed in the records
iterationsintegerNumber of distinct nodes the run touched

Span

Spans form a tree rooted at a single root span covering the whole run. Node spans hang off the root; LLM and tool spans hang off the node span they occurred under, falling back to root when the node is unknown.

FieldTypeDescription
idstringSpan id, stable within the run. The root span is always root; the rest are s1, s2, and so on in emission order.
namestringgraph for the root, node: <node> for node spans, llm.generate for LLM spans, tool: <tool_name> for tool spans
kindstringroot, node, llm, or tool
parentstring or nullParent span id. null for the root span.
start_msnumberOffset from the run start, in milliseconds
duration_msnumberSpan duration in milliseconds. LLM and tool spans are point-in-time markers and report 0.0; only root and node spans carry a real duration.
modelstring or nullModel name. Present on llm spans.
input_tokensinteger or nullPrompt tokens. Present on llm spans.
output_tokensinteger or nullCompletion tokens. Present on llm spans.

Event

Events are returned newest first, so a UI can render the list without reversing it. Ids are assigned in chronological order (e0 is the oldest), which means the ids count down through the array.

FieldTypeDescription
idstringEvent id, e<index> in chronological order
typestringmessage, updates, state, error, or result
nodestringNode the event is attributed to. Empty when not node-scoped.
offset_msnumberOffset from the run start, in milliseconds
summarystringOne-line summary, for example tool_call get_weather, llm.generate · 955 tokens, error, or delta

Token usage

FieldType
prompt_tokensinteger
completion_tokensinteger
reasoning_tokensinteger
total_tokensinteger

Availability

Traces come from an in-process telemetry store that records chunks as runs execute. It is bound only outside production: when MODE=production the store is not created and this endpoint returns an empty payload (run_count: 0, run: null) rather than an error.

The store is in-memory and not durable. It is a development and playground aid, not a production observability system. For production tracing, configure OpenTelemetry (OTEL_ENABLED) or the observability block in agentflow.json.

Both POST /v1/graph/invoke and POST /v1/graph/stream record runs. Invoke has no chunk stream, so its trace is reconstructed from the final messages, which is enough for usage and cost but produces a coarser span tree than a streamed run.


See also