Docs/Turns
API reference

Turns

GET /v1/agents/turns/{turnId}, and the turn's trajectory.

View as Markdown

Reads the result of one turn. Every POST /message returns a turnId: the turn the message started, or the running turn it joined. See Structured output for the options that make a turn return typed output.

Authenticate with the same API key as the rest of /v1/agents.

Get a turn

GET /v1/agents/turns/{turnId}

Answers at once. Poll it until status is succeeded or failed; every 2 to 5 seconds is enough.

json
{
  "turnId": "turn_vzjz62w7j7jvOilZ6zBL",
  "sessionId": "cs_02JAB",
  "machineId": "m_7f2a",
  "status": "failed",
  "output": null,
  "reason_code": "output_invalid",
  "raw_answer": "I found two invoices for September: INV-1043 and INV-1051. The totals were not shown on the list page.",
  "usage": { "inputTokens": 48210, "outputTokens": 1630 },
  "recordTrajectory": false,
  "trajectory": {
    "recorded": false,
    "url": "/v1/agents/turns/turn_vzjz62w7j7jvOilZ6zBL/trajectory"
  },
  "evidence": [],
  "output_errors": ["/invoices/0 must have required property 'total'"],
  "template": "Sign in to the {{vendor}} portal and list the invoices dated in {{month}}.",
  "inputs": { "vendor": "Acme", "month": "September 2026" }
}
FieldNotes
turnIdThe turn.
sessionIdThe session the message landed in. null until it is delivered.
machineIdThe computer.
statusrunning, needs_input, finalizing, succeeded or failed. See Status.
outputWith output_schema: an object that matches it. Without: Sai's final answer, a string. null until succeeded.
reason_codeSet when failed: agent_error, machine_unavailable, not_started, and for a structured message output_invalid, criteria_not_met or criteria_unchecked.
raw_answerOnly when failed: Sai's final answer (output_invalid), or a short explanation.
error_codeOnly for a known agent_error: insufficient_credits or free_computer_time_exhausted.
usage{ inputTokens, outputTokens } of Sai's model calls in the turn. null until the turn ends.
recordTrajectoryWhether the turn records its steps.
trajectory{ recorded, url }: whether any step was stored, and where to read them.
evidenceStructured messages only. One entry per check: { id, passed, score?, evidence? }. A string success_criteria gives one entry without id or score.
output_errorsStructured messages only, with output_invalid: why the last attempt did not match the schema.
template, inputsStructured messages only: the message as sent, before {{name}} was filled, and its values.

The structured fields are extra fields on the same object, so code written for plain messages reads a structured turn unchanged.

Trajectory

Requires options.recordTrajectory: true on the message that started the turn.

GET /v1/agents/turns/{turnId}/trajectory
json
{
  "turnId": "turn_vzjz62w7j7jvOilZ6zBL",
  "trajectory": {
    "recorded": true,
    "url": "/v1/agents/turns/turn_vzjz62w7j7jvOilZ6zBL/trajectory"
  },
  "steps": [
    {
      "index": 0,
      "timestamp": "2026-10-10T18:02:11.204Z",
      "title": "Open the Acme portal sign-in page",
      "rationale": "The task starts at the portal, so I open its sign-in page first.",
      "code": "await machine.browser.newtab('https://portal.acme.example/login')",
      "screenshot": "/v1/agents/turns/turn_vzjz62w7j7jvOilZ6zBL/trajectory/screenshots/0"
    },
    {
      "index": 7,
      "final": true,
      "timestamp": "2026-10-10T18:04:52.880Z",
      "screenshot": "/v1/agents/turns/turn_vzjz62w7j7jvOilZ6zBL/trajectory/screenshots/7"
    }
  ]
}
FieldNotes
indexStep number, from 0.
finaltrue on the last entry: the screen when the turn ended.
titleSai's one-line title for the step.
rationaleSai's reason for the step.
codeThe code Sai ran.
errorPresent when the step failed.
screenshotPath of the screen before the step, or null. Fetch it with your key.

steps is empty for a turn without recordTrajectory. At most 500 steps are listed.

Screenshot

GET /v1/agents/turns/{turnId}/trajectory/screenshots/{index}

Returns the JPEG (Content-Type: image/jpeg).

Errors

StatusBody
404{ "error": "turn_not_found", "message": "No turn with this id on your account." }. Also for another account's turn, and for a screenshot that does not exist.

Gotchas

  • finalizing is not done. Keep polling until succeeded or failed.
  • status: succeeded without output_schema or success_criteria only means Sai finished with an answer. Read output.
  • A message sent while Sai was busy (queued: true) joined the running turn, so its turnId is that turn's and so is the result.
  • A later message in the same session never changes a finished turn.