Skip to content
Agent Graph

Graphs

A graph is one of your account’s live shares: the sessions and agents agent-graph watch-remote streams from a machine, kept on the site while it’s in use, and deleted after a week with no new events.

Retrieve a graph to see what it holds (retention), how big it is, and how its nodes stand (counts), now or as of any retained event.

List graphs
GET/graphs
Retrieve a graph
GET/graphs/{graph_id}
ModelsExpand Collapse
Graph object { id, as_of_event_id, counts, 6 more }

One of your account’s live shares, and how its nodes stand at as_of_event_id.

id: string

Unique identifier for the graph, gph_….

as_of_event_id: string

The event this graph’s counts are as of. Pass it as as_of to read other objects at the same moment.

counts: GraphCounts { agents, blocked, by_state, 3 more }

How many nodes the graph has, by kind and state.

agents: number

Nodes of kind agent.

blocked: number

Unfinished nodes waiting on another node.

by_state: object { canceled, completed, failed, 3 more }

Nodes in each state.

canceled: number
completed: number
failed: number
idle: number
input_required: number
working: number
nodes: number

Every node.

sessions: number

Nodes of kind session.

stale: number

Nodes that are stale: working, but silent too long.

created: number

When the share was made, in Unix milliseconds.

formatint64
last_event: number

When its most recent event happened, in Unix milliseconds.

formatint64
object: "graph"

String representing the object’s type: always graph.

retention: Retention { event_count, first_event_created, first_event_id, 2 more }

The span of history the site holds for this graph now, and so the range as_of can take. A live share keeps between 1,000 and 2,000 events, trimmed from the start as new ones arrive.

event_count: number

How many events are held.

first_event_created: number

When the oldest event happened, in Unix milliseconds.

formatint64
first_event_id: string

The oldest event you can read, or ask as_of. null while the graph has no events.

last_event_created: number

When the newest event happened, in Unix milliseconds.

formatint64
last_event_id: string

The newest event. null while the graph has no events.

source: "watch" or "paste" or "upload"

How the graph was shared. watch is a live share from agent-graph watch-remote; this version of the API returns only those.

One of the following:
"watch"
"paste"
"upload"
url: string

Where to see the graph in the viewer. Only the account that owns it can open it.

formaturi
GraphCounts object { agents, blocked, by_state, 3 more }

How many nodes the graph has, by kind and state.

agents: number

Nodes of kind agent.

blocked: number

Unfinished nodes waiting on another node.

by_state: object { canceled, completed, failed, 3 more }

Nodes in each state.

canceled: number
completed: number
failed: number
idle: number
input_required: number
working: number
nodes: number

Every node.

sessions: number

Nodes of kind session.

stale: number

Nodes that are stale: working, but silent too long.

Retention object { event_count, first_event_created, first_event_id, 2 more }

The span of history the site holds for this graph now, and so the range as_of can take. A live share keeps between 1,000 and 2,000 events, trimmed from the start as new ones arrive.

event_count: number

How many events are held.

first_event_created: number

When the oldest event happened, in Unix milliseconds.

formatint64
first_event_id: string

The oldest event you can read, or ask as_of. null while the graph has no events.

last_event_created: number

When the newest event happened, in Unix milliseconds.

formatint64
last_event_id: string

The newest event. null while the graph has no events.

GraphsNodes

A node is a session (one run of a coding agent) or an agent it started. Nodes form trees:

session        claude-code:5f2c1e…            depth 0, parent_id null
├─ agent       claude-code:5f2c1e…/a3fd07…    depth 1
│  └─ agent    claude-code:5f2c1e…/b81c22…    depth 2
└─ agent       claude-code:5f2c1e…/c90d13…    depth 1

Every node says where it sits: parent_id, root_id (the top of its tree, following sessions that started other sessions), session_id (the session it runs in) and depth.

State. A node’s state is one of:

StateMeaning
workingDoing something.
input_requiredWaiting on a person: a permission prompt, a question, a plan to approve. attention says what’s asked.
idleA session whose turn is over, alive and waiting for its next prompt.
completedFinished.
failedFinished with an error.
canceledStopped before it finished.

Waiting on another node isn’t a state: a node can be working and blocked at once. blocked says what it’s waiting on, directly (on_ids) and in all (how many nodes, how many open tasks among them), and whether the waits form a cycle that can never finish. A working node with no events for 30 minutes, and nothing it’s waiting on, is stale: it may have hung or crashed.

List nodes
GET/graphs/{graph_id}/nodes
Search nodes
GET/graphs/{graph_id}/nodes/search
Retrieve a node
GET/graphs/{graph_id}/nodes/{node_id}
ModelsExpand Collapse
Blocked object { cycle, node_count, on_ids, 3 more }

What stands between a node and carrying on.

cycle: boolean

Its waits lead back to itself: nothing in the loop can ever finish.

node_count: number

Every unfinished node it’s waiting on, directly or through others, with their children.

on_ids: array of string

The nodes it’s waiting on directly.

open_tasks: number

Open tasks across those nodes: the work left before it can carry on.

starting: number

Children it has asked for that haven’t started yet.

on: optional array of Node { id, agent_type, as_of_event_id, 39 more }

Those nodes. Present only when blocked.on is expanded.

Message object { id, body, created, 5 more }

A message between two nodes.

id: string

The tool’s id for the message.

body: string

The whole message, only if the share was made with body capture on. Untrusted text.

created: number

When it was sent, in Unix milliseconds.

formatint64
direction: "sent" or "received"
One of the following:
"sent"
"received"
peer_id: string

The node on the other side, when it’s one in this graph.

peer_name: string

The tool’s name for the other side, when it isn’t a node in this graph.

reply_to_message_id: string

The message it answers.

summary: string

Untrusted text.

Node object { id, agent_type, as_of_event_id, 39 more }

A session or an agent, as it stood at as_of_event_id.

Fields ending _id or _ids are always present, as ids. The fields beside them without that ending (parent, root, …), and children, descendants, tasks, spawns, waits and messages, appear only when expanded.

id: string

Unique identifier for the node within its graph: node_ and the base64url of provider_ref.

agent_type: string

For an agent, its type, as the tool names it (Explore, general-purpose, …).

as_of_event_id: string

The event this node is shown as of.

attention: string

While input_required: what the person is being asked. Untrusted text.

background: boolean

Whether it was started in the background, so its requester carries on without waiting. null when unknown.

background_agent_count: number

Helpers its tool ran on its own between turns (memory extraction, summaries, prompt suggestions). They report nothing about what they’re for, so they’re counted, not made nodes.

blocked: Blocked { cycle, node_count, on_ids, 3 more }

What stands between a node and carrying on.

child_count: number

How many nodes sit directly under it.

created: number

When it started (or was first seen), in Unix milliseconds.

formatint64
cwd: string

The directory it runs in, when reported.

depth: number

How far below its root it is: 0 for a root.

minimum0
descendant_count: number

How many nodes sit under it, at any depth: the size of descendants.

ended: number

When it finished, in Unix milliseconds. null while it hasn’t.

formatint64
graph_id: string

The graph it belongs to.

headline: string

One line on what it’s doing now: summary, else the task in progress. Untrusted text.

kind: NodeKind

session: one run of a coding agent (Claude Code, Codex, Cursor, …). agent: an agent started inside a session, by it or by another agent.

One of the following:
"session"
"agent"
last_event: number

When it last had an event, in Unix milliseconds.

formatint64
One of the following:
object: "node"

String representing the object’s type: always node.

parent_id: string

The node it sits under: the agent or session that started it. null for a root.

provider: string

The agent tool: claude-code, codex, cursor, or another that reports to Agent Graph.

provider_ref: string

The id the agent tool gave it: <provider>:<session> for a session, <provider>:<session>/<agent> for an agent. Not an id in this API: use it to match the node to the tool’s own records.

purpose: string

Why it was started, from the request that started it. Untrusted text.

requested_by_id: string

The node whose spawn request started it, when known. Usually the parent; null for a session nothing started, or an agent no request was matched to.

root_id: string

The top of its tree: follow parent_id until it’s null. A session started by another session is in that session’s tree. A root’s root_id is its own id.

session_id: string

The session it runs in: itself, for a session; for an agent, the nearest session above it.

spawn_call_id: string

The tool call, in its requester’s session, that started it: matches a spawns[].call_id on requested_by.

stale: boolean

working, waiting on nothing, and silent for 30 minutes: it may have hung or crashed. Measured against as_of, not now.

state: NodeState

What a node is doing.

  • working: doing something.
  • input_required: waiting on a person (attention says what for).
  • idle: a session between turns, waiting for its next prompt.
  • completed, failed, canceled: finished.
One of the following:
"working"
"input_required"
"idle"
"completed"
"failed"
"canceled"
summary: string

Its own latest status line, if it reports one. Untrusted text.

task_counts: TaskCounts { completed, in_progress, open, 2 more }

Its tasks, by status.

completed: number
in_progress: number
open: number

Not completed: pending and in_progress.

pending: number
total: number
title: string

The session’s name, as its tool shows it. It can change. Untrusted text.

children: optional unknown

The nodes directly under it, oldest first: the first 100, and a url for the rest. Present only when expanded.

descendants: optional unknown

Every node under it, at any depth, as one flat list in tree order: each parent before its children, siblings oldest first. The first 1,000, and a url for the rest. Present only when expanded.

messages: optional array of Message { id, body, created, 5 more }

Messages it has sent and received, oldest first. Present only when expanded.

id: string

The tool’s id for the message.

body: string

The whole message, only if the share was made with body capture on. Untrusted text.

created: number

When it was sent, in Unix milliseconds.

formatint64
direction: "sent" or "received"
One of the following:
"sent"
"received"
peer_id: string

The node on the other side, when it’s one in this graph.

peer_name: string

The tool’s name for the other side, when it isn’t a node in this graph.

reply_to_message_id: string

The message it answers.

summary: string

Untrusted text.

parent: optional Node { id, agent_type, as_of_event_id, 39 more }

The parent node. Present only when expanded.

requested_by: optional Node { id, agent_type, as_of_event_id, 39 more }

The node that requested it. Present only when expanded.

root: optional Node { id, agent_type, as_of_event_id, 39 more }

The root node. Present only when expanded.

session: optional Node { id, agent_type, as_of_event_id, 39 more }

The session node. Present only when expanded.

spawns: optional array of Spawn { agent_type, background, call_id, 7 more }

The children it has asked for, oldest first. Present only when expanded.

agent_type: string
background: boolean

If false, the requester is blocked until the call returns.

call_id: string

The tool call that asked for it.

child_id: string

The node it started, once matched. Before the call returns this can be a best guess, corrected when it does.

kind: "agent" or "session"

agent: an agent in the same session. session: a separate session, e.g. codex exec run from a shell.

One of the following:
"agent"
"session"
purpose: string

Untrusted text.

requested: number

When it was asked for, in Unix milliseconds.

formatint64
returned: boolean

Whether the call has returned.

run: boolean

Whether it’s through agent-graph run, when known.

title: string

For a session, the name it was to be given.

tasks: optional array of Task { id, active_text, status, text }

Its task list, in the tool’s order. Present only when expanded.

id: string

The tool’s id for the task, unique within the node.

active_text: string

How the tool words it while it’s in progress (Running the tests). Untrusted text.

status: "pending" or "in_progress" or "completed"
One of the following:
"pending"
"in_progress"
"completed"
text: string

What the task is. Untrusted text.

waits: optional array of Wait { id, ended, kind, 4 more }

What it has waited on and is waiting on, oldest first. Present only when expanded.

id: string

The wait’s id, unique within the node.

ended: number

When it stopped, in Unix milliseconds.

formatint64
kind: "spawn" or "explicit"

spawn: waiting for a child it asked for, until the call returns. explicit: any other wait the tool reported.

One of the following:
"spawn"
"explicit"
on_id: string

The node waited on. null while a requested child hasn’t started.

open: boolean

Whether it’s still waiting.

reason: string

Untrusted text.

started: number

When it started waiting, in Unix milliseconds.

formatint64
NodeKind = "session" or "agent"

session: one run of a coding agent (Claude Code, Codex, Cursor, …). agent: an agent started inside a session, by it or by another agent.

One of the following:
"session"
"agent"
NodeState = "working" or "input_required" or "idle" or 3 more

What a node is doing.

  • working: doing something.
  • input_required: waiting on a person (attention says what for).
  • idle: a session between turns, waiting for its next prompt.
  • completed, failed, canceled: finished.
One of the following:
"working"
"input_required"
"idle"
"completed"
"failed"
"canceled"
Spawn object { agent_type, background, call_id, 7 more }

A child a node asked for.

agent_type: string
background: boolean

If false, the requester is blocked until the call returns.

call_id: string

The tool call that asked for it.

child_id: string

The node it started, once matched. Before the call returns this can be a best guess, corrected when it does.

kind: "agent" or "session"

agent: an agent in the same session. session: a separate session, e.g. codex exec run from a shell.

One of the following:
"agent"
"session"
purpose: string

Untrusted text.

requested: number

When it was asked for, in Unix milliseconds.

formatint64
returned: boolean

Whether the call has returned.

run: boolean

Whether it’s through agent-graph run, when known.

title: string

For a session, the name it was to be given.

Task object { id, active_text, status, text }

One item on a node’s task list.

id: string

The tool’s id for the task, unique within the node.

active_text: string

How the tool words it while it’s in progress (Running the tests). Untrusted text.

status: "pending" or "in_progress" or "completed"
One of the following:
"pending"
"in_progress"
"completed"
text: string

What the task is. Untrusted text.

TaskCounts object { completed, in_progress, open, 2 more }

Its tasks, by status.

completed: number
in_progress: number
open: number

Not completed: pending and in_progress.

pending: number
total: number
Wait object { id, ended, kind, 4 more }

A time a node waited on another.

id: string

The wait’s id, unique within the node.

ended: number

When it stopped, in Unix milliseconds.

formatint64
kind: "spawn" or "explicit"

spawn: waiting for a child it asked for, until the call returns. explicit: any other wait the tool reported.

One of the following:
"spawn"
"explicit"
on_id: string

The node waited on. null while a requested child hasn’t started.

open: boolean

Whether it’s still waiting.

reason: string

Untrusted text.

started: number

When it started waiting, in Unix milliseconds.

formatint64

GraphsEvents

An event is one thing that happened in a graph. Events are immutable and ordered by id, which sorts by time.

Each event has the payload its agent tool reported, and changes: every node the event changed, and for each, the fields that changed, from what to what. A node’s derived fields count too: an agent being spawned changes its parent’s child_count, and every ancestor’s descendant_count.

List events starting_after the last one you saw to follow a graph cheaply, or with order=asc from any point to replay its history.

Event types

TypepayloadWhen
session.startedcwd?, source? (startup, resume, …), title?, link_method?A session starts or resumes.
session.endedreason?A session exits.
agent.spawnedagent_type?, purpose?, background?An agent starts.
agent.finishedstatus (completed, failed, canceled), summary?An agent stops.
statusstate, summary?, title?A node’s state or status line changes.
tasks.updateditems: [{id, text, active_text?, status}]A to-do tool sent its whole list, which replaces the node’s.
task.upsertedid, text?, active_text?, status?One task was created or changed.
task.deletedidA task was removed.
spawn.requestedcall_id, kind (agent, session), agent_type?, purpose?, backgroundA node asked for a child to start. Unless background, it’s blocked until the call returns.
spawn.returnedcall_id, child?, outcome?That call returned.
wait.startedwait_id, on (a provider node id), reason?A node blocked on another for some other reason.
wait.endedwait_id, outcome?That block cleared.
message.sentmessage_id, to, reply_to?, summary?, body?One node sent another a message.
activitytool, label?A tool call, when the share records them.

Ids inside payload are the provider’s own (as in provider_ref), exactly as the agent tool reported them; use node_id and changes for API ids. More types may be added: ignore those you don’t recognise.

List events
GET/graphs/{graph_id}/events
Retrieve an event
GET/graphs/{graph_id}/events/{event_id}
ModelsExpand Collapse
Event object { id, changes, created, 7 more }

One thing that happened in a graph, and every change it made to its nodes.

id: string

Unique identifier for the event, evt_…. Sorts by time.

changes: array of NodeChange { created, fields, node_id }

Every node this event changed, starting with node_id. Empty when it changed nothing (a repeated status, say).

created: boolean

Whether the event created it. Its fields then go from null.

fields: map[FieldChange { from, to } ]

Each node field that changed, by name, from what to what. Derived fields count (child_count, descendant_count, blocked, headline, task_counts). stale doesn’t, as it changes with time rather than events, and nor do expandable fields.

from: unknown

The value before the event.

to: unknown

The value after it.

node_id: string
created: number

When it happened, in Unix milliseconds.

formatint64
graph_id: string
node_id: string

The node it’s about.

object: "event"

String representing the object’s type: always event.

payload: map[unknown]

What the agent tool reported, by type: see event types. Ids in it are the provider’s own. Text in it is untrusted.

source: EventSource { adapter, provider, provider_version }

What reported the event.

adapter: string

The Agent Graph adapter that recorded it, and its version, when it says.

provider: string
provider_version: string

The agent tool’s version, when it says.

type: "session.started" or "session.ended" or "agent.spawned" or 11 more

What happened. See event types. More may be added: ignore those you don’t recognise.

One of the following:
"session.started"
"session.ended"
"agent.spawned"
"agent.finished"
"status"
"tasks.updated"
"task.upserted"
"task.deleted"
"spawn.requested"
"spawn.returned"
"wait.started"
"wait.ended"
"message.sent"
"activity"
node: optional Node { id, agent_type, as_of_event_id, 39 more }

That node, as it stood just after this event. Present only when expanded.

id: string

Unique identifier for the node within its graph: node_ and the base64url of provider_ref.

agent_type: string

For an agent, its type, as the tool names it (Explore, general-purpose, …).

as_of_event_id: string

The event this node is shown as of.

attention: string

While input_required: what the person is being asked. Untrusted text.

background: boolean

Whether it was started in the background, so its requester carries on without waiting. null when unknown.

background_agent_count: number

Helpers its tool ran on its own between turns (memory extraction, summaries, prompt suggestions). They report nothing about what they’re for, so they’re counted, not made nodes.

blocked: Blocked { cycle, node_count, on_ids, 3 more }

What stands between a node and carrying on.

child_count: number

How many nodes sit directly under it.

created: number

When it started (or was first seen), in Unix milliseconds.

formatint64
cwd: string

The directory it runs in, when reported.

depth: number

How far below its root it is: 0 for a root.

minimum0
descendant_count: number

How many nodes sit under it, at any depth: the size of descendants.

ended: number

When it finished, in Unix milliseconds. null while it hasn’t.

formatint64
graph_id: string

The graph it belongs to.

headline: string

One line on what it’s doing now: summary, else the task in progress. Untrusted text.

kind: NodeKind

session: one run of a coding agent (Claude Code, Codex, Cursor, …). agent: an agent started inside a session, by it or by another agent.

One of the following:
"session"
"agent"
last_event: number

When it last had an event, in Unix milliseconds.

formatint64
One of the following:
object: "node"

String representing the object’s type: always node.

parent_id: string

The node it sits under: the agent or session that started it. null for a root.

provider: string

The agent tool: claude-code, codex, cursor, or another that reports to Agent Graph.

provider_ref: string

The id the agent tool gave it: <provider>:<session> for a session, <provider>:<session>/<agent> for an agent. Not an id in this API: use it to match the node to the tool’s own records.

purpose: string

Why it was started, from the request that started it. Untrusted text.

requested_by_id: string

The node whose spawn request started it, when known. Usually the parent; null for a session nothing started, or an agent no request was matched to.

root_id: string

The top of its tree: follow parent_id until it’s null. A session started by another session is in that session’s tree. A root’s root_id is its own id.

session_id: string

The session it runs in: itself, for a session; for an agent, the nearest session above it.

spawn_call_id: string

The tool call, in its requester’s session, that started it: matches a spawns[].call_id on requested_by.

stale: boolean

working, waiting on nothing, and silent for 30 minutes: it may have hung or crashed. Measured against as_of, not now.

state: NodeState

What a node is doing.

  • working: doing something.
  • input_required: waiting on a person (attention says what for).
  • idle: a session between turns, waiting for its next prompt.
  • completed, failed, canceled: finished.
One of the following:
"working"
"input_required"
"idle"
"completed"
"failed"
"canceled"
summary: string

Its own latest status line, if it reports one. Untrusted text.

task_counts: TaskCounts { completed, in_progress, open, 2 more }

Its tasks, by status.

completed: number
in_progress: number
open: number

Not completed: pending and in_progress.

pending: number
total: number
title: string

The session’s name, as its tool shows it. It can change. Untrusted text.

children: optional unknown

The nodes directly under it, oldest first: the first 100, and a url for the rest. Present only when expanded.

descendants: optional unknown

Every node under it, at any depth, as one flat list in tree order: each parent before its children, siblings oldest first. The first 1,000, and a url for the rest. Present only when expanded.

messages: optional array of Message { id, body, created, 5 more }

Messages it has sent and received, oldest first. Present only when expanded.

id: string

The tool’s id for the message.

body: string

The whole message, only if the share was made with body capture on. Untrusted text.

created: number

When it was sent, in Unix milliseconds.

formatint64
direction: "sent" or "received"
One of the following:
"sent"
"received"
peer_id: string

The node on the other side, when it’s one in this graph.

peer_name: string

The tool’s name for the other side, when it isn’t a node in this graph.

reply_to_message_id: string

The message it answers.

summary: string

Untrusted text.

parent: optional Node { id, agent_type, as_of_event_id, 39 more }

The parent node. Present only when expanded.

requested_by: optional Node { id, agent_type, as_of_event_id, 39 more }

The node that requested it. Present only when expanded.

root: optional Node { id, agent_type, as_of_event_id, 39 more }

The root node. Present only when expanded.

session: optional Node { id, agent_type, as_of_event_id, 39 more }

The session node. Present only when expanded.

spawns: optional array of Spawn { agent_type, background, call_id, 7 more }

The children it has asked for, oldest first. Present only when expanded.

agent_type: string
background: boolean

If false, the requester is blocked until the call returns.

call_id: string

The tool call that asked for it.

child_id: string

The node it started, once matched. Before the call returns this can be a best guess, corrected when it does.

kind: "agent" or "session"

agent: an agent in the same session. session: a separate session, e.g. codex exec run from a shell.

One of the following:
"agent"
"session"
purpose: string

Untrusted text.

requested: number

When it was asked for, in Unix milliseconds.

formatint64
returned: boolean

Whether the call has returned.

run: boolean

Whether it’s through agent-graph run, when known.

title: string

For a session, the name it was to be given.

tasks: optional array of Task { id, active_text, status, text }

Its task list, in the tool’s order. Present only when expanded.

id: string

The tool’s id for the task, unique within the node.

active_text: string

How the tool words it while it’s in progress (Running the tests). Untrusted text.

status: "pending" or "in_progress" or "completed"
One of the following:
"pending"
"in_progress"
"completed"
text: string

What the task is. Untrusted text.

waits: optional array of Wait { id, ended, kind, 4 more }

What it has waited on and is waiting on, oldest first. Present only when expanded.

id: string

The wait’s id, unique within the node.

ended: number

When it stopped, in Unix milliseconds.

formatint64
kind: "spawn" or "explicit"

spawn: waiting for a child it asked for, until the call returns. explicit: any other wait the tool reported.

One of the following:
"spawn"
"explicit"
on_id: string

The node waited on. null while a requested child hasn’t started.

open: boolean

Whether it’s still waiting.

reason: string

Untrusted text.

started: number

When it started waiting, in Unix milliseconds.

formatint64
EventSource object { adapter, provider, provider_version }

What reported the event.

adapter: string

The Agent Graph adapter that recorded it, and its version, when it says.

provider: string
provider_version: string

The agent tool’s version, when it says.

FieldChange object { from, to }
from: unknown

The value before the event.

to: unknown

The value after it.

NodeChange object { created, fields, node_id }

How one event changed one node.

created: boolean

Whether the event created it. Its fields then go from null.

fields: map[FieldChange { from, to } ]

Each node field that changed, by name, from what to what. Derived fields count (child_count, descendant_count, blocked, headline, task_counts). stale doesn’t, as it changes with time rather than events, and nor do expandable fields.

from: unknown

The value before the event.

to: unknown

The value after it.

node_id: string