Chain, chain hop and chain triage
Included from Basic tier
JSON:API types: chain (the index summary), chain_activity (one
hop of a chain), chain_triage (the triage digest). All three verified
in their serializers (endpoints_chains_index.go, endpoints_chains.go,
endpoints_chains_triage.go).
The v2 OpenAPI document declares this resource's schema; the resource-field coverage gate holds this page to it.
A chain is a correlation cascade: every event, change and workflow run
sharing one correlation id. The chain key is coalesce(correlation_id, id) —
a pre-spine row is its own chain root at read time, so no backfill ever
rewrites the large tables.
Endpoints
| Verb | Path | Serves |
|---|---|---|
GET | /api/chains | chain summaries inside a required time window, keyset-paginated on (last_activity_at, correlation_id) descending. 206 with meta.next_after when another page may exist. |
GET | /api/chains/{correlation_id} | The full cascade as chain_activity hops, chronological (created_at, id — UUIDv7 ids break timestamp ties). |
GET | /api/chains/{correlation_id}/triage | One chain_triage resource — the server-side triage digest serving both the GUI Triage panel and opschain chains triage. |
Visibility and redaction
Hops are RBAC-filtered per viewer: a hop whose node path the viewer cannot
read renders as a redacted stub (kind, depth, timestamp only), so the chain's
shape survives without leaking another project's detail. Events with no
event_node_path (system-wide events) render unredacted, matching the events
index. A chain lists in the index only when the viewer can read at least one
member; a summary's node_paths is narrowed to the readable ones.
chain (the index summary)
Resource id = the correlation id. links.self = /api/chains/<id>.
| Attribute | Type | Meaning |
|---|---|---|
correlation_id | string | The chain key (repeated from the id). |
root | object | The chain's first hop (ordered by chain_depth, then objects before events): kind, summary, username (nullable), initiated_by_type (nullable), node_path (nullable), created_at. |
first_activity_at / last_activity_at | string | The activity bounds (ISO8601, 6 fractional digits). |
counts | object | Member tally by kind: changes, workflow_runs, events. |
has_failures | boolean | True when any member event's type matches the failure shapes (api:*:error, api:*:system_error, api:*:pre_execution, error:*). |
node_paths | array of strings | The involved node paths the viewer can read (capped at 10 per row before filtering). |
Index parameters
| Parameter | Meaning |
|---|---|
since | Required (or filter[created_at_gteq]): a relative window <N>m, <N>h or <N>d. Clamped by the chains_max_window_days global setting (default 90 days); exceeding it is a named 422. |
filter[created_at_gteq] | The absolute-window alternative, RFC3339. |
node | An absolute node-path prefix; matches a chain with ANY member on or under it, not just the root. |
origin | Root-origin filter: user, schedule, trigger or api. A root with no initiated_by_type counts as user. |
has_failures | Boolean; keeps only failing chains. |
limit | 1–200, default 50. |
after | The previous page's meta.next_after keyset cursor (<last_activity_at>,<correlation_id>). |
chain_activity (one hop)
Always present:
| Attribute | Meaning |
|---|---|
kind | event, change or workflow_run. |
created_at | Hop timestamp. |
chain_depth | Cause depth within the chain. |
redacted | True when the viewer cannot read the hop's node path; the attributes below are then absent. |
Present only when readable (null where the underlying column is null):
| Attribute | Meaning |
|---|---|
summary | The hop's one-line identity: the event type, or the change/run action path. |
node_path | The authorisation/display path (null for events without one). |
caused_by_event_id | The causing event. |
initiated_by_type / initiated_by_id | The initiator (e.g. workflow_step + its id). |
source_type / source_id | Event hops: the stored polymorphic source. |
username | The acting username (events fall back to the payload's username). |
message | Event hops: the payload's message/error/reason. |
step_name / step_change_id | Event hops sourced from a step: the step's name and owning change. |
author / executed_as | Event hops: the payload's author and run-as identity. |
spawned_by_step / spawned_by_stage | Change/run hops initiated by a workflow step: that step's name and its stage step's name. |
template_version | A template label ("<template name> <version>") where the hop resolves one (a templated change, or a template-version-history event); null otherwise. |
exchange_id | Event hops: the agent exchange the event evidences — the exchange_id of an info:agent:invoked, info:agent:chat or info:agent:adapter_transcript event (the last names the redacted adapter transcript at /api/projects/{project}/agents/{code}/adapter_transcripts/{exchange}); null otherwise. |
Agent work on a chain
A unit of agent work is one chain (the SDK-managed correlation scope,
opschain docs ai-agents): invoking an agent's capability through the
platform proxy stamps a correlation id on the exchange — inherited from
the caller's X-Opschain-Correlation-Id header when it carries one (an
integration relaying an incident's chain, a trigger), else minted for the
invocation — and lands the info:agent:invoked root hop. Every call the
agent then makes through its SDK inside that scope joins the chain
automatically: each governed read (info:agent:request), a filed
recommendation (info:recommendation:filed), the sealed adapter
transcript (info:agent:adapter_transcript), a brokered model call
(info:agent:model_call) and the change or workflow run it creates (the
run row itself, initiated_by_type agent). A human accepting the
recommendation later joins the accepted change to the same chain. The
approvals inbox reads this chain back as the approval context's
chain_summary, so an approver sees the lineage without leaving the
inbox.
links.self points at the member (/api/events/<id>, /api/changes/<id> or
/api/workflow_runs/<id>) on readable hops; redacted hops carry no links.
chain_triage (the digest)
Resource id = the correlation id. Links: self, chain. Query parameter
lines bounds each hop's log tail (1–200, default 40).
| Attribute | Meaning |
|---|---|
failure_class | The chain's headline classification: none when nothing failed, otherwise the first failing hop's class — one of the folded object statuses error, system_error, aborted, cancelled, the whole-chain override pre_execution, event (an orphan error event with no failed parent object), or stalled. |
attributed | {runner, group} — the runner (and its registered group) every failure executed on, set only when all failing hops name exactly one runner; empty object otherwise. |
failing_hops | The failing members, each: id, kind, summary, node_path, failure_class, error (omitted when empty), runner (omitted when empty), log_tail (last N log lines, child steps included), links (self, and log_lines for change/run hops). |
Per-hop failure_class values, verbatim: the folded statuses error,
system_error, aborted, cancelled; event for orphan error events;
pre_execution (never started — infrastructure, git or secrets) for a
pre-execution rejection; and stalled for a non-terminal member with no
dispatch-plane activity anywhere (no queued scheduler job, no live lease, no
wait/approval gate, no live nested run) once the whole chain has been quiet
for 2 minutes — the shape a mid-run server restart leaves behind. Its error
text names the stuck step and the remedy. Hops the viewer cannot read never
appear.
Related pages
opschain docs events— the event members and their vocabulary.opschain docs triggers— chains rooted in a trigger firing.