Skip to content
Agent Graph

List nodes

GET/graphs/{graph_id}/nodes

Returns a graph’s nodes as of a point in its history, filtered and ordered as you ask.

Common uses:

To listSend
The top-level treesis_root=true
A node’s childrenparent_id=node_…
Everything under a nodeancestor_id=node_…&order=tree
The top three levels under itancestor_id=node_…&max_depth=3&order=tree
Everything waiting on a personstate=input_required
What may have hungstale=true
What’s blocked behind another nodeblocked=true

Filters combine with AND. In tree order (order=tree), each parent comes before its children, and siblings are oldest first, so a page never separates a node from what places it; with a filter, the nodes that match keep that order.

Path ParametersExpand Collapse
graph_id: string
Query ParametersExpand Collapse
agent_type: optional string

Only agents of this type, e.g. Explore.

ancestor_id: optional string

Only nodes under this one, at any depth (not the node itself).

as_of: optional string or number

Answer as of this point in the graph’s history: an event id (evt_…, the state just after that event) or a Unix time in milliseconds (the state after the last event at or before it). Leave it out for now.

Must be within the graph’s retention, else 410 outside_retention_window. To read several pages or objects at one moment, pass the as_of_event_id of the first response.

One of the following:
EventID = string
UnixTimeMs = number
blocked: optional boolean

Only nodes that are (true) or aren’t (false) waiting on another node.

created: optional object { gt, gte, lt, lte }

Only nodes that started in this range.

gt: optional number

After this time.

formatint64
gte: optional number

At or after this time.

formatint64
lt: optional number

Before this time.

formatint64
lte: optional number

At or before this time.

formatint64
ending_before: optional string

A cursor: an object id from the list. Returns the page before it. Pass the first id of the page you have to get the previous one.

expand: optional array of string

Fields to expand on each node, with the data. prefix: expand[]=data.parent, expand[]=data.blocked.on. children and descendants can’t be expanded in a list (use parent_id or ancestor_id instead).

Send one expand[] per path. A path goes at most 4 levels deep; at most 20 paths per request. An expanded field appears beside its _id field, which is always there; one not expanded is absent.

is_root: optional boolean

Only roots (true) or only non-roots (false).

kind: optional NodeKind

Only sessions or only agents.

One of the following:
"session"
"agent"
last_event: optional object { gt, gte, lt, lte }

Only nodes whose last event is in this range.

gt: optional number

After this time.

formatint64
gte: optional number

At or after this time.

formatint64
lt: optional number

Before this time.

formatint64
lte: optional number

At or before this time.

formatint64
limit: optional number

How many objects to return, from 1 to 1,000.

minimum1
maximum1000
max_depth: optional number

With ancestor_id: only nodes at most this many levels below it. 1 is its children.

minimum1
order: optional "newest" or "tree"

newest: most recently started first. tree: depth-first, each parent before its children, siblings oldest first.

One of the following:
"newest"
"tree"
parent_id: optional string

Only nodes directly under this one.

provider: optional string

Only nodes from this agent tool, e.g. claude-code.

root_id: optional string

Only nodes in this tree, the root included.

session_id: optional string

Only this session and the agents in it.

stale: optional boolean

Only nodes that are, or aren’t, stale.

starting_after: optional string

A cursor: an object id from the list. Returns the page after it. Pass the last id of the page you have to get the next one.

state: optional array of NodeState

Only nodes in these states. Send one state[] per state.

One of the following:
"working"
"input_required"
"idle"
"completed"
"failed"
"canceled"
Header ParametersExpand Collapse
"If-None-Match": optional string
ReturnsExpand Collapse
as_of_event_id: string

The event the list is as of. Keep it as as_of for every page.

data: array of Node { id, agent_type, as_of_event_id, 39 more }
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
has_more: boolean

Whether there are more after this page.

object: "list"
url: string

The URL for this list, with every parameter that defined it (including as_of). Add starting_after to get the next page.

List nodes

curl https://agentgraph.chofter.com/api/v1/graphs/$GRAPH_ID/nodes \
    -H "Authorization: Bearer $AGENT_GRAPH_API_KEY"
{
  "object": "list",
  "url": "/api/v1/graphs/gph_k3P9xQ2mWt7a/nodes?parent_id=node_Y2xhdWRlLWNvZGU6NWYyYzFlOWEtOGQxYi00YjZlLTlhNTEtMGY2ZjJjN2QzZTEw&as_of=evt_01M4G3G4E8Q9R2M5X4T6W8Y0B1",
  "as_of_event_id": "evt_01M4G3G4E8Q9R2M5X4T6W8Y0B1",
  "has_more": true,
  "data": [
    {
      "id": "node_Y2xhdWRlLWNvZGU6NWYyYzFlOWEtOGQxYi00YjZlLTlhNTEtMGY2ZjJjN2QzZTEwL2EzZmQwN2MyYjE5ZTRkNjE",
      "object": "node",
      "as_of_event_id": "evt_01M4G3G4E8Q9R2M5X4T6W8Y0B1",
      "graph_id": "gph_k3P9xQ2mWt7a",
      "provider_ref": "claude-code:5f2c1e9a-8d1b-4b6e-9a51-0f6f2c7d3e10/a3fd07c2b19e4d61",
      "provider": "claude-code",
      "kind": "agent",
      "parent_id": "node_Y2xhdWRlLWNvZGU6NWYyYzFlOWEtOGQxYi00YjZlLTlhNTEtMGY2ZjJjN2QzZTEw",
      "root_id": "node_Y2xhdWRlLWNvZGU6NWYyYzFlOWEtOGQxYi00YjZlLTlhNTEtMGY2ZjJjN2QzZTEw",
      "session_id": "node_Y2xhdWRlLWNvZGU6NWYyYzFlOWEtOGQxYi00YjZlLTlhNTEtMGY2ZjJjN2QzZTEw",
      "requested_by_id": "node_Y2xhdWRlLWNvZGU6NWYyYzFlOWEtOGQxYi00YjZlLTlhNTEtMGY2ZjJjN2QzZTEw",
      "depth": 1,
      "state": "working",
      "stale": false,
      "attention": null,
      "title": null,
      "summary": "Reading the auth middleware",
      "headline": "Reading the auth middleware",
      "purpose": "Find every caller of the old session store",
      "agent_type": "Explore",
      "background": false,
      "link_method": null,
      "spawn_call_id": "toolu_01HxV9qT3mJb7d2KcL5nRw8E",
      "cwd": "/Users/dev/work/api",
      "created": 1791540843221,
      "ended": null,
      "last_event": 1791541903812,
      "child_count": 1,
      "descendant_count": 1,
      "background_agent_count": 0,
      "task_counts": {
        "total": 4,
        "pending": 1,
        "in_progress": 1,
        "completed": 2,
        "open": 2
      },
      "blocked": {
        "on_ids": [
          "node_Y2xhdWRlLWNvZGU6NWYyYzFlOWEtOGQxYi00YjZlLTlhNTEtMGY2ZjJjN2QzZTEwL2I4MWMyMmUwOWY3YTNjNTU"
        ],
        "starting": 0,
        "node_count": 1,
        "open_tasks": 5,
        "cycle": false
      }
    }
  ]
}
{
  "error": {
    "type": "invalid_request_error",
    "code": "parameter_invalid",
    "message": "Invalid limit: must be between 1 and 1000.",
    "param": "limit",
    "doc_url": "https://agentgraph.chofter.com/docs/reference#errors"
  }
}
{
  "error": {
    "type": "invalid_request_error",
    "code": "expand_too_deep",
    "message": "The path children.children.children.children.children goes 5 levels deep; at most 4 are allowed.",
    "param": "expand",
    "doc_url": "https://agentgraph.chofter.com/docs/reference#errors"
  }
}
{
  "error": {
    "type": "authentication_error",
    "code": "api_key_invalid",
    "message": "Invalid API key: ag_sk_live_****Xa9T.",
    "param": null,
    "doc_url": "https://agentgraph.chofter.com/docs/reference#errors"
  }
}
{
  "error": {
    "type": "permission_error",
    "code": "api_key_restricted",
    "message": "This restricted key can't read gph_k3P9xQ2mWt7a.",
    "param": null,
    "doc_url": "https://agentgraph.chofter.com/docs/reference#errors"
  }
}
{
  "error": {
    "type": "invalid_request_error",
    "code": "resource_missing",
    "message": "No such node: node_Y2xhdWRlLWNvZGU6NWYyYzFlOWEtOGQxYi00YjZlLTlhNTEtMGY2ZjJjN2QzZTEwL2I4MWMyMmUwOWY3YTNjNTU.",
    "param": "node_id",
    "doc_url": "https://agentgraph.chofter.com/docs/reference#errors"
  }
}
{
  "error": {
    "type": "invalid_request_error",
    "code": "node_not_yet_created",
    "message": "The node node_Y2xhdWRlLWNvZGU6NWYyYzFlOWEtOGQxYi00YjZlLTlhNTEtMGY2ZjJjN2QzZTEwL2EzZmQwN2MyYjE5ZTRkNjE is created by evt_01M4G2FMPN7KD3N1PZ0QX8RM4A, after as_of.",
    "param": "as_of",
    "doc_url": "https://agentgraph.chofter.com/docs/reference#errors",
    "first_event_id": "evt_01M4G2FMPN7KD3N1PZ0QX8RM4A"
  }
}
{
  "error": {
    "type": "invalid_request_error",
    "code": "outside_retention_window",
    "message": "This graph holds events from evt_01M4FXYWR82B8XK0V5QJ7M1N3R to evt_01M4G3G4E8Q9R2M5X4T6W8Y0B1.",
    "param": "as_of",
    "doc_url": "https://agentgraph.chofter.com/docs/reference#errors",
    "first_event_id": "evt_01M4FXYWR82B8XK0V5QJ7M1N3R",
    "last_event_id": "evt_01M4G3G4E8Q9R2M5X4T6W8Y0B1"
  }
}
{
  "error": {
    "type": "rate_limit_error",
    "code": "rate_limit",
    "message": "Too many requests. Try again in 1 second.",
    "param": null,
    "doc_url": "https://agentgraph.chofter.com/docs/reference#errors"
  }
}
{
  "error": {
    "type": "api_error",
    "code": "api_error",
    "message": "Something went wrong. Try again, and include the Request-Id if it keeps happening.",
    "param": null,
    "doc_url": "https://agentgraph.chofter.com/docs/reference#errors"
  }
}
Returns Examples
{
  "object": "list",
  "url": "/api/v1/graphs/gph_k3P9xQ2mWt7a/nodes?parent_id=node_Y2xhdWRlLWNvZGU6NWYyYzFlOWEtOGQxYi00YjZlLTlhNTEtMGY2ZjJjN2QzZTEw&as_of=evt_01M4G3G4E8Q9R2M5X4T6W8Y0B1",
  "as_of_event_id": "evt_01M4G3G4E8Q9R2M5X4T6W8Y0B1",
  "has_more": true,
  "data": [
    {
      "id": "node_Y2xhdWRlLWNvZGU6NWYyYzFlOWEtOGQxYi00YjZlLTlhNTEtMGY2ZjJjN2QzZTEwL2EzZmQwN2MyYjE5ZTRkNjE",
      "object": "node",
      "as_of_event_id": "evt_01M4G3G4E8Q9R2M5X4T6W8Y0B1",
      "graph_id": "gph_k3P9xQ2mWt7a",
      "provider_ref": "claude-code:5f2c1e9a-8d1b-4b6e-9a51-0f6f2c7d3e10/a3fd07c2b19e4d61",
      "provider": "claude-code",
      "kind": "agent",
      "parent_id": "node_Y2xhdWRlLWNvZGU6NWYyYzFlOWEtOGQxYi00YjZlLTlhNTEtMGY2ZjJjN2QzZTEw",
      "root_id": "node_Y2xhdWRlLWNvZGU6NWYyYzFlOWEtOGQxYi00YjZlLTlhNTEtMGY2ZjJjN2QzZTEw",
      "session_id": "node_Y2xhdWRlLWNvZGU6NWYyYzFlOWEtOGQxYi00YjZlLTlhNTEtMGY2ZjJjN2QzZTEw",
      "requested_by_id": "node_Y2xhdWRlLWNvZGU6NWYyYzFlOWEtOGQxYi00YjZlLTlhNTEtMGY2ZjJjN2QzZTEw",
      "depth": 1,
      "state": "working",
      "stale": false,
      "attention": null,
      "title": null,
      "summary": "Reading the auth middleware",
      "headline": "Reading the auth middleware",
      "purpose": "Find every caller of the old session store",
      "agent_type": "Explore",
      "background": false,
      "link_method": null,
      "spawn_call_id": "toolu_01HxV9qT3mJb7d2KcL5nRw8E",
      "cwd": "/Users/dev/work/api",
      "created": 1791540843221,
      "ended": null,
      "last_event": 1791541903812,
      "child_count": 1,
      "descendant_count": 1,
      "background_agent_count": 0,
      "task_counts": {
        "total": 4,
        "pending": 1,
        "in_progress": 1,
        "completed": 2,
        "open": 2
      },
      "blocked": {
        "on_ids": [
          "node_Y2xhdWRlLWNvZGU6NWYyYzFlOWEtOGQxYi00YjZlLTlhNTEtMGY2ZjJjN2QzZTEwL2I4MWMyMmUwOWY3YTNjNTU"
        ],
        "starting": 0,
        "node_count": 1,
        "open_tasks": 5,
        "cycle": false
      }
    }
  ]
}
{
  "error": {
    "type": "invalid_request_error",
    "code": "parameter_invalid",
    "message": "Invalid limit: must be between 1 and 1000.",
    "param": "limit",
    "doc_url": "https://agentgraph.chofter.com/docs/reference#errors"
  }
}
{
  "error": {
    "type": "invalid_request_error",
    "code": "expand_too_deep",
    "message": "The path children.children.children.children.children goes 5 levels deep; at most 4 are allowed.",
    "param": "expand",
    "doc_url": "https://agentgraph.chofter.com/docs/reference#errors"
  }
}
{
  "error": {
    "type": "authentication_error",
    "code": "api_key_invalid",
    "message": "Invalid API key: ag_sk_live_****Xa9T.",
    "param": null,
    "doc_url": "https://agentgraph.chofter.com/docs/reference#errors"
  }
}
{
  "error": {
    "type": "permission_error",
    "code": "api_key_restricted",
    "message": "This restricted key can't read gph_k3P9xQ2mWt7a.",
    "param": null,
    "doc_url": "https://agentgraph.chofter.com/docs/reference#errors"
  }
}
{
  "error": {
    "type": "invalid_request_error",
    "code": "resource_missing",
    "message": "No such node: node_Y2xhdWRlLWNvZGU6NWYyYzFlOWEtOGQxYi00YjZlLTlhNTEtMGY2ZjJjN2QzZTEwL2I4MWMyMmUwOWY3YTNjNTU.",
    "param": "node_id",
    "doc_url": "https://agentgraph.chofter.com/docs/reference#errors"
  }
}
{
  "error": {
    "type": "invalid_request_error",
    "code": "node_not_yet_created",
    "message": "The node node_Y2xhdWRlLWNvZGU6NWYyYzFlOWEtOGQxYi00YjZlLTlhNTEtMGY2ZjJjN2QzZTEwL2EzZmQwN2MyYjE5ZTRkNjE is created by evt_01M4G2FMPN7KD3N1PZ0QX8RM4A, after as_of.",
    "param": "as_of",
    "doc_url": "https://agentgraph.chofter.com/docs/reference#errors",
    "first_event_id": "evt_01M4G2FMPN7KD3N1PZ0QX8RM4A"
  }
}
{
  "error": {
    "type": "invalid_request_error",
    "code": "outside_retention_window",
    "message": "This graph holds events from evt_01M4FXYWR82B8XK0V5QJ7M1N3R to evt_01M4G3G4E8Q9R2M5X4T6W8Y0B1.",
    "param": "as_of",
    "doc_url": "https://agentgraph.chofter.com/docs/reference#errors",
    "first_event_id": "evt_01M4FXYWR82B8XK0V5QJ7M1N3R",
    "last_event_id": "evt_01M4G3G4E8Q9R2M5X4T6W8Y0B1"
  }
}
{
  "error": {
    "type": "rate_limit_error",
    "code": "rate_limit",
    "message": "Too many requests. Try again in 1 second.",
    "param": null,
    "doc_url": "https://agentgraph.chofter.com/docs/reference#errors"
  }
}
{
  "error": {
    "type": "api_error",
    "code": "api_error",
    "message": "Something went wrong. Try again, and include the Request-Id if it keeps happening.",
    "param": null,
    "doc_url": "https://agentgraph.chofter.com/docs/reference#errors"
  }
}