Smart Agent Teams

Runs

One execution of an agent, from the queue to a finished transcript with tokens and cost.

A run is one execution of an agent. The API records runs but never executes them: assigning a task, waking an agent or triggering a routine puts a run in the queue, and a runner such as sat runner claims it, executes it with Claude Code or Codex, streams the transcript back and reports tokens and cost. Runs are how work happens and how money is spent, so every budget, cost report and "live runs" view is built from them.

How it works

What queues a run

triggerCreated byFirst transcript entry
assignmentCreating a task with an assignee, or reassigning a task to a different agent. Only when the agent is available (not paused, pending_approval or terminated) and the task is not backlog, done or cancelled.Assigned NF-12
manualWake on an agent's page, sat agents wake, or POST /agents/{agent_id}/wake. Agent heartbeats from the scheduler also use this endpoint, so they are manual runs too.The reason you passed, for example heartbeat; otherwise Queued (manual)
routineTriggering a routine, by Run now, sat routines trigger or the scheduler. The routine first creates a task, and the run is linked to both the task and the routine.Queued (routine)

A heartbeat wake from sat scheduler start sends {"reason": "heartbeat"}, so you can tell it apart from a person's wake only by that first system transcript entry. There is no separate heartbeat trigger value.

Queue dedupe. Before creating a run, the API looks for a run that is still queued for the same agent with the same task_id and the same routine_id. If one exists, that run is returned instead of a new one. The trigger is not compared: a manual wake on NF-12 while an assignment run for NF-12 is still queued returns the assignment run. Once a run is claimed it no longer blocks new runs.

Lifecycle

  • A run never goes back to queued. A PATCH with status: queued returns 400 INVALID_STATE.
  • A finished run (succeeded, failed or cancelled) rejects every further update with 400 RUN_FINISHED, including cancel.
  • When a run starts, its agent becomes running. When it finishes, the agent goes back to idle (or to error after a failure), unless the agent has another run still running.

Claim, lease, heartbeat and expiry

Runners take work with POST /runs/claim, which atomically picks the oldest queued run whose agent is not dormant and not already running something, marks it running, stamps runner_id, started_at and heartbeat_at, and returns everything needed to execute it. One agent runs at most one claimed run at a time.

The claim is a lease. The runner keeps it alive by calling POST /runs/{run_id}/heartbeat; any PATCH /runs/{run_id} progress report also refreshes heartbeat_at. A running run whose last heartbeat (or start, if it never sent one) is older than RUN_LEASE_SECONDS (default 300) is expired: it becomes failed with an error such as "Runner my-mac:4242 stopped reporting (lease expired)". Expiry runs at the start of every claim, on POST /runs/expire-stale, and from sat scheduler start every 60 seconds.

A runner that sends a runner_id different from the one that claimed the run gets 409 RUN_LEASE_LOST and must stop. The full protocol, including what the runner does on cancel and lease loss, is in How runs work.

Transcript

The transcript is an ordered list of entries. Runners append with append_transcript on PATCH; the API adds system entries when the run is queued and claimed.

Prop

Type

GET /runs leaves transcripts out; fetch one run to get its transcript.

Tokens and cost

Runners report tokens_in, tokens_out and cost_cents (integer cents). Each PATCH replaces the previous values, so the last report wins. sat runner reports what the adapter measured: Claude Code reports its own cost, while Codex runs cost 0 unless the runner sets SAT_CODEX_PRICE_IN and SAT_CODEX_PRICE_OUT. See Budgets and costs.

Finishing enforces the agent's budget

Every way a run can finish (a runner's final report, cancel, lease expiry, agent termination) checks the agent's monthly budget. If the agent's spend this month has reached a non-zero monthly_budget_cents, the agent is set to paused and a budget_override approval is opened (one per agent at a time). A paused agent's queued runs stay queued and are not claimed until it is resumed.

Cancel

POST /runs/{run_id}/cancel works on queued and running runs, whichever runner holds them. It is the same as a PATCH with status: cancelled and no runner_id. A runner executing the run learns about it from its next heartbeat or report (409 RUN_LEASE_LOST or 400 RUN_FINISHED) and stops. Cancelling also enforces the agent's budget.

Fields

RunOut, returned by the list and inside other responses. GET /runs/{run_id} returns RunDetail, which adds transcript.

Prop

Type

Statuses

StatusMeaningChanged by
queuedWaiting for a runner.Created by an assignment, wake or routine trigger.
runningA runner holds the lease and is executing it.POST /runs/claim, or a PATCH with status: running (for runners that do not use claims; not recommended).
succeededThe runner reported success.The runner's final PATCH.
failedThe runner reported failure, or the lease expired.The runner's final PATCH, or lease expiry.
cancelledStopped by a person or by terminating the agent.POST /runs/{run_id}/cancel, PATCH with status: cancelled, or POST /agents/{agent_id}/terminate (error "Agent terminated").

Use it

Runs appear in several places: Live runs on the dashboard (queued and running), the Runs tab on an agent's page (last 50), the Recent failed runs section of the inbox, and the activity log.

Select a run to open its detail page at /c/{companyId}/runs/{runId}. It shows:

  • The agent, the trigger (for example "Grace · assignment run") and a link to the task, such as NF-3 Implement rich-text editor core.
  • Started, Duration, Tokens in / out and Cost.
  • The runner's summary, and the error in a red box when there is one.
  • The Transcript, with time and role for each entry. While the run is queued or running the page shows a live dot and "Waiting for the runner to report…", and refreshes as the runner reports.

Cancel run appears while the run is queued or running.

To queue a run by hand, open the agent and select Wake. It is hidden for paused, pending_approval and terminated agents.

Permissions

Any member of the company can list, read, wake, claim, report on and cancel runs. Non-members get 404 NOT_FOUND. There is no separate runner role: a runner acts with the credentials of the person who started it, usually an API key.

Events

EventWhenExtra fields
run.createdA run was queued by an assignment, wake or routine trigger. Also sent when dedupe returned an existing queued run.agent_id
run.updatedClaimed, reported on (every PATCH), cancelled, expired, or cancelled by termination.agent_id, status
agent.updatedThe agent started or finished a run, or was paused for budget.
approval.createdFinishing the run paused the agent and opened a budget_override approval. Sent without an id.

Heartbeats publish no events. See Live updates.

Limits and known gaps

  • The API does not execute runs. Without a runner (sat runner start or your own), runs stay queued forever.
  • Dedupe compares agent, task and routine only, not the trigger.
  • GET /runs returns at most 500 rows and has no paging cursor. The CLI resolves id prefixes against the 500 most recent runs.
  • Cost is whatever the runner reports. SAT does not price tokens itself.
  • Spend is counted by created_at, so a run's cost counts toward the month it was queued in, even if it finished in the next month.
  • The transcript is stored as one JSON array on the run, with no size limit or truncation on the server.
  • Run history is kept indefinitely; there is no retention or purge.

On this page