roasOS
Agent

Runs

Read an agent run: its status, its approvals, its events, its cost, and how to stop it.

One family reads every agent run: GET /v1/agent-runs/{id}. An ask, a skill run, a workflow run, and a scheduled run use it.

Method and pathReturns
GET /v1/agent-runs/{id}The full run
GET /v1/agent-runs/{id}/status{ id, status, updated_at } for the poll loop
GET /v1/agent-runs/{id}/resultThe answer of the agent, when the run is terminal
GET /v1/agent-runs/{id}/eventsThe ordered lifecycle of the run
POST /v1/agent-runs/{id}/cancelStops a run that is not terminal
POST /v1/agent-runs/{id}/gates/{gate_id}/approveApproves one open approval, and starts the work behind it
POST /v1/agent-runs/{id}/gates/{gate_id}/declineRefuses one open approval

A run in a different workspace returns 404 agent_run_not_found. It never returns 403.

The statuses

StatusMeaning
quotedA quote only. Nothing is reserved, and nothing runs.
processingThe run works.
cancelingYou asked the run to stop. The run has not stopped yet.
completedThe run finished. The result is ready.
failedThe run stopped with an error.
canceledYou stopped the run.

The three last values are terminal. A run keeps the same id in each status.

A run in canceling carries a cancel_requested_at timestamp. The run stops at the end of the step that it works on. It then reads canceled. A run that finishes that step first reads completed.

The run envelope

{
  "data": {
    "schema_version": "agent-run@1",
    "kind": "agent_run",
    "id": "3d9c17aa-...",
    "trigger": "skill",
    "skill_id": "7c2a3f10-...",
    "conversation_id": "1f0c8b52-...",
    "status": "processing",
    "max_credits": 25,
    "generation_spend_cap_credits": 40,
    "credits_reserved": 65,
    "pricing_version": "agent-token-pricing@2026-10-04.v3",
    "started_at": "2026-08-21T10:08:02.000Z",
    "pending_approvals": [
      {
        "gate_id": "44b1d0e7-...",
        "summary": "storyboard render x 3",
        "credits": 60,
        "expires_at": "2026-08-21T10:23:02.000Z",
        "approve_url": "https://www.roasos.com/home/1f0c8b52-..."
      }
    ],
    "created_at": "2026-08-21T10:07:44.000Z",
    "updated_at": "2026-08-21T10:09:20.000Z"
  }
}

trigger states what started the run: api, skill, routine, workflow, or chat. A typed door such as POST /v1/workflows/remix writes workflow and names its pack skill in skill_id.

A completed run carries result_ready: true. Read the answer at GET /v1/agent-runs/{id}/result. A quoted run carries credits_max, quote_id and expires_at instead, and it reserves nothing.

Pending approvals

A run opens an approval for each paid action that it cannot approve itself. Refer to Spend caps and approvals.

FieldMeaning
gate_idThe identifier of the approval. Use it on the approve path and on the decline path.
summaryA short description of the work.
creditsThe price of the work. The value is null when the price is not yet known.
itemsOnly on a batch card. One entry for each item on the card, with its index, its summary and its credits. Use index in drop when you approve.
expires_atThe time when the approval stops. The limit is 15 minutes.
approve_urlWhere to say yes. A run in a thread names its card in roasOS. A run with no thread names the approve path of this approval.

An approval that expires does not run and does not charge you.

The list holds at most 20 open approvals. One approval card carries at most eight quotes. A card above that number is one that the run cannot approve by itself, so it waits for a person.

A run that starts from a skill or from a workflow writes no thread, so it has no card in roasOS. Approve that work over REST. Refer to Approve an approval.

The events

curl https://api.roasos.com/v1/agent-runs/3d9c17aa-.../events \
  -H "Authorization: Bearer roasos_sk_..."
{
  "data": [
    { "seq": 0, "event": "accepted", "at": "2026-08-21T10:07:44.000Z" },
    { "seq": 1000, "event": "started", "at": "2026-08-21T10:08:02.000Z" },
    { "seq": 2000, "event": "tool_called", "at": "2026-08-21T10:08:05.000Z" },
    { "seq": 2001, "event": "tool_called", "at": "2026-08-21T10:08:44.000Z" },
    { "seq": 3000, "event": "gate_opened", "at": "2026-08-21T10:09:01.000Z" },
    { "seq": 4000, "event": "gate_auto_approved", "at": "2026-08-21T10:09:01.000Z" },
    { "seq": 5000, "event": "generation_started", "at": "2026-08-21T10:09:02.000Z" },
    { "seq": 6000, "event": "settled", "at": "2026-08-21T10:11:09.000Z" }
  ]
}

The list holds nine names: accepted, started, tool_called, gate_opened, gate_auto_approved, generation_started, settled, failed, and cancelled.

tool_called, gate_opened, and generation_started occur many times in one run. Each name keeps its own band of numbers, and each occurrence takes the next number in that band. seq is therefore an address inside a band, and not a clock.

roasOS orders the list by at first, and by seq inside one timestamp. Read at for the time of a step.

The list takes no cursor and no limit. It gives the first 500 events of the run.

A run that is older than the timeline shows the steps that its own times can prove.

What a run costs

roasOS reserves the ceiling of the run at the confirm step. It settles the run when the run stops. It refunds the part that the run does not use. A terminal run therefore carries a usage object.

{
  "model": "anthropic/claude-sonnet-5",
  "pricing_version": "agent-token-pricing@2026-10-04.v3",
  "input_tokens": 18422,
  "cached_input_tokens": 12800,
  "output_tokens": 2140,
  "steps": 6,
  "tool_calls": 4,
  "credits_reserved": 65,
  "credits_charged": 2,
  "credits_refunded": 63,
  "generation_credits_charged": 40
}
FieldMeaning
modelThe model that did the thinking of the run.
pricing_versionThe rate table that made the charge.
input_tokensThe tokens that the run sent to the model.
cached_input_tokensThe part of input_tokens that came from the cache.
output_tokensThe tokens that the model wrote.
stepsThe number of steps in the run.
tool_callsThe number of tools that the run called.
credits_reservedThe ceiling that the confirm step reserved.
credits_chargedThe credits for the tokens of the run.
credits_refundedThe part of the reservation that roasOS gave back.
generation_credits_chargedThe credits that the paid work inside the run used.

Each generation inside a run quotes and reserves on its own. The run cap sits above those generations. It never replaces them.

roasOS never estimates tokens to charge you. If the token record of a run is not available, roasOS charges 0 credits and refunds the reservation in full. The usage object then shows zero tokens.

A failed run

A failed run gives the reason in error, and it refunds the reservation. Use error.code in your conditions. Do not parse error.message.

{
  "data": {
    "schema_version": "agent-run@1",
    "kind": "agent_run",
    "id": "3d9c17aa-...",
    "status": "failed",
    "credits_reserved": 65,
    "completed_at": "2026-08-21T10:09:55.000Z",
    "error": { "code": "run_failed", "message": "The run stopped before it answered" },
    "usage": { "…": "…" }
  }
}

A run row carries one of three codes.

CodeCause
run_failedThe turn stopped before it answered
run_start_refusedroasOS could not start the turn. Nothing ran.
run_timed_outThe run passed its time limit, and roasOS stopped it

Each of the three refunds the reservation in full. A refusal that occurs before the 202 answers on the call itself. It makes no run, and it therefore has no code here. Refer to Errors at the door.

A run that stops in the middle keeps the generations that it already started. Those jobs have their own lifecycle. Read them at GET /v1/jobs/{id}.

Approve an approval

POST /v1/agent-runs/{id}/gates/{gate_id}/approve releases one open approval and starts the work behind it. approve_url on the approval already holds this path when the run has no thread.

curl -X POST https://api.roasos.com/v1/agent-runs/6f31c07d-.../gates/44b1d0e7-.../approve \
  -H "Authorization: Bearer roasos_sk_..." \
  -H "Idempotency-Key: remix-glow-serum-001-approve" \
  -H "Content-Type: application/json" \
  -d '{ "generation_spend_cap_credits": 120, "note": "Ship both variants." }'
{
  "data": {
    "gate_id": "44b1d0e7-...",
    "run_id": "6f31c07d-...",
    "status": "approved",
    "decided_at": "2026-08-21T11:12:40.000Z",
    "follow_up_run_id": "a71e5b28-..."
  }
}
FieldMeaning
statusapproved, already_decided, or expired
decided_atThe time of the decision. It is null when the approval expired.
follow_up_run_idThe run that the approval started. It is null when the approval started no work.

The body is optional, and it carries four fields. note records why, in 400 characters or fewer. max_credits and generation_spend_cap_credits bound the follow-up run, exactly as they bound the first run. drop removes items from a batch card. Refer to Remove items from a batch.

roasOS composes the text of the follow-up run from the approved card. The caller never writes it. A run with no thread has no history, so the card is the only true record of what was approved.

The follow-up run carries trigger: "api", and it keeps the skill_id and the conversation_id of the run that opened the card.

Remove items from a batch

A batch card holds two to eight items of the same tool. Each item has its own price. The items list on the approval shows them. You can remove some items before you approve the card. This is the same as when you clear an item on the card in roasOS.

Put the index of each item that must not run in drop:

curl -X POST https://api.roasos.com/v1/agent-runs/6f31c07d-.../gates/44b1d0e7-.../approve \
  -H "Authorization: Bearer roasos_sk_..." \
  -H "Idempotency-Key: statics-glow-serum-001-approve" \
  -H "Content-Type: application/json" \
  -d '{ "drop": [1, 3] }'
  • The items in drop do not run, and roasOS does not charge for them.
  • roasOS checks the per-request ceiling of the key against the items that you keep.
  • drop must keep one item or more. To refuse all of the card, decline it.
  • An index that the card does not have answers 400 gate_drop_invalid.
  • A retry of an approval keeps the drop list of the first approval. A new list on the retry has no effect.

Only a card with a render behind it is approvable. A card that asks a question answers 409 agent_gate_not_approvable. A card that asks for more headroom is one such card. Its answer is a higher cap on a new run, and never an approval. Decline it, or start the run again with a higher generation_spend_cap_credits.

The approval spends on your key. It quotes and confirms the follow-up run in the one call, so the body states no quote_only value and no quote_id.

RuleAnswer above the limit
Idempotency-Key is required400 idempotency_key_required
The price on the card stays inside max_credits_per_request on the key403 request_credit_limit_exceeded
The follow-up run stays inside the daily budget and the monthly budget429, with a Retry-After header
Your workspace has the credits402 insufficient_credits

An approval that another caller already decided answers already_decided, and one that ran out of time answers expired. Neither one charges you. An approval that does not belong to this run answers 404 agent_gate_not_found.

Poll follow_up_run_id as you poll any other run. Each render it starts is an ordinary job. Refer to Jobs.

Approve is a REST operation only. It is not an MCP tool, and it will not become one. A connected model may decline an approval with roasos_agent_decline_approval. A connected model can never approve its own spend. Refer to Approving spend is never a tool.

Decline an approval

POST /v1/agent-runs/{id}/gates/{gate_id}/decline refuses the work. Nothing starts, and roasOS charges nothing. The body takes an optional note. Refer to Decline an approval.

Cancel a run

curl -X POST https://api.roasos.com/v1/agent-runs/3d9c17aa-.../cancel \
  -H "Authorization: Bearer roasos_sk_..."

The answer is the run envelope. A run that stopped at once reads canceled. A run that still works on a step reads canceling with a cancel_requested_at timestamp. Poll the run until it becomes terminal. roasOS refunds the part of the reservation that the run did not use.

A generation that the run already started keeps its own lifecycle. Stop that job at POST /v1/jobs/{id}/cancel.

A run that already finished returns 409 run_not_cancelable.

Cancel is a REST operation only. It is not an MCP tool. A connected model must not stop a run that a person started. (Job cancel is different: roasos_cancel_job cancels only a job that the same connection started.)

Agent runs send no webhook

An agent run has no webhook_url field. Poll GET /v1/agent-runs/{id}/status.

A generation that a run starts is an ordinary job. That job obeys the usual rules of Jobs, and it appears in GET /v1/jobs.

Errors

StatusCodeCause
400validation_errorThe run id or the approval id is not a UUID
400idempotency_key_requiredThe Idempotency-Key header is absent on an approve
400spend_cap_exceeds_policyThe caps on the approve body are above the per-request maximum of the key
400gate_drop_invaliddrop names an item that the card does not have, or it names all of the items
402insufficient_creditsThe balance does not cover the approved work
403request_credit_limit_exceededThe approved amount is above the per-request ceiling of the key
404agent_run_not_foundroasOS cannot find the run in your workspace
404agent_gate_not_foundThe approval does not belong to this run
409run_not_completed/result before the run is terminal
409run_not_cancelableThe run already finished
409agent_gate_not_approvableThe approval card asks a question, and no render is behind it
429daily_credit_budget_exceeded, monthly_credit_budget_exceededThe budget of the key has no space
503agent_run_unavailableThe run projection is not available at this time

On this page