Agents APISessions

Events

Follow what an agent does as it works, and pick up exactly where you left off.

sessions.events(id) yields everything that happens in a session, in order, for as long as the session is alive. It reconnects by itself if the connection drops, and ends when the session is deleted. Break out of the loop once you have what you need.

What you receive

ItemHas seqWhat it is
session.statusnoThe session's status. It comes first, and again whenever the status changes, with status, error and any pending_approvals.
approval.requestednoThe agent is waiting for your permission. See Approvals.
eventyesSomething the agent did: { seq, type, data, time }. Events are stored and numbered from 1.
JSON
{ "type": "session.status", "status": "running", "error": null, "pending_approvals": [], … }
{ "seq": 7, "type": "tool.called", "data": { "tool": "bash", "input": { "command": "python3 --version" }, … }, "time": "2026-09-28T07:18:16Z" }
{ "seq": 12, "type": "text.ended", "data": { "text": "The installed Python version is 3.11.2." }, "time": "…" }
{ "seq": 13, "type": "run.completed", "data": { … }, "time": "…" }
Only answer requests without a seq
An approval.requested item without a seq is a live request. Check "seq" in event before you answer, as every example here does.

Follow a session

Show progress, count tokens, keep your place
let answer = ""
let outputTokens = 0
let position = 0 // the last event you handled; store it to resume later

for await (const event of sessions.events(session.id)) {
  if (event.type === "session.status" && "status" in event) {
    console.log("status:", event.status)
    continue
  }
  if (!("seq" in event)) continue // an approval or question: see Approvals and questions
  position = event.seq

  if (event.type === "tool.called") console.log("tool:", event.data?.tool, JSON.stringify(event.data?.input))
  if (event.type === "step.ended") outputTokens += Number((event.data?.tokens as { output?: number } | undefined)?.output ?? 0)
  if (event.type === "text.ended") answer = String(event.data?.text)
  if (event.type === "run.failed") console.error("run failed:", event.data?.error)
  if (event.type === "run.completed" || event.type === "run.failed") break
}

console.log(answer, `(${outputTokens} output tokens)`)
status: provisioning
status: idle
status: running
tool: bash {"command":"python3 --version","description":"Check Python version"}
The installed Python version is 3.11.2. (91 output tokens)

Resume without gaps

Every event has a seq. Pass the last one you handled as after, and the stream continues with the next event: nothing missed and nothing repeated, whether after a dropped connection, a restart of your own service, or days later. session.last_seq is always the newest number.

Continue from where you stopped
await sessions.message(session.id, "And which version of Node.js?")

// Only events after `position`: nothing missed, nothing repeated.
for await (const event of sessions.events(session.id, { after: position })) {
  if ("seq" in event) position = event.seq
  if (event.type === "text.ended") console.log(event.data?.text)
  if (event.type === "run.completed" || event.type === "run.failed") break
}

Stopping the stream

  • Break out of the loop. This closes the connection.
  • TypeScript: pass signal from an AbortController to stop it from elsewhere.
  • Python: pass stop, a threading.Event (asyncio.Event with the async client). It may take a few seconds to stop.

Event types

typedataMeaning
run.startedThe agent picked up a message.
run.completedThe run finished, or was cancelled.
run.failederrorThe run stopped on an error. The session is still usable.
text.endedtextA complete block of the agent's reply. Text arrives whole; there are no partial deltas.
tool.calledtool, inputThe agent called a tool, e.g. bash with { command }.
tool.success · tool.failedcontent · errorA tool call finished, or failed. After a failure the agent usually carries on.
step.endedtokens, finishOne model call ended. tokens has input, output, reasoning and cache counts.
prompt.admittedprompt, deliveryA message you sent entered the conversation (steer or queue).
step.started · text.startedA model call, or a reply block, began.
tool.input.started · tool.input.endednameThe model is writing a tool call.
reasoning.started · reasoning.endedtextThe model's reasoning, for models that expose it.
compaction.started · compaction.endedtextEarlier conversation was summarized to fit the context window.
retriedattempt, errorA model call was retried after a transient error.

New event types can appear over time. Ignore any you don't recognise.