---
title: "Cancel an agent run"
method: POST
path: "/agent/runs/{run}/cancel"
tags: ["Agents"]
---

# Cancel an agent run

`POST /agent/runs/{run}/cancel`

## Request

- Base URL: `https://app.mandala.computer/api/v1`
- URL: `https://app.mandala.computer/api/v1/agent/runs/{run}/cancel`
- Auth: HTTP bearer

Stops a running run where it is: the model call in flight is abandoned and no further action is taken. What it already did to the desktop stands, and so does what it cost. The run answers as `cancelled`. Cancelling a run that already ended answers it as it is, unchanged. A run this key cannot see is a `404`.

Requires the `member` role or higher.

## Path parameters

- `run` string, required

## Response `200`

The run.

- AgentRun
  - `id` string, required: `run_` and twenty-four hex characters.
  - `computer_id` string, required
  - `thread_id` string, nullable, required: The thread the run is kept in, once it is: `null` until a new thread has something in it (a run that has not acted yet, or failed before it did), and when the thread was deleted. Continue it with `thread_id` on the next run.
  - `status` 'running' | 'completed' | 'failed' | 'cancelled' | 'interrupted', required: `completed`: the loop ended on its own, and `result.stop` says how — only `end_turn` means the task was finished. `failed`: see `error`. `cancelled`: stopped by a cancel. `interrupted`: the control plane restarted while it was going (`error.status` 503), or the run stopped without its ending being recorded (`error.status` 500); it was not resumed.
  - `prompt` string, required: The prompt, at most 32,000 characters of it.
  - `model` string, required: The model the run was started with.
  - `max_steps` integer, required
  - `steps` integer, required: How many actions it has taken so far.
  - `cost_usd` number: Funded runs: the charge so far, in USD.
  - `result` AgentResult
    - `cost_usd` number: Cumulative AI credit charge in USD, present on funded runs only.
    - `steps` integer, required: How many actions the loop took — a count; the actions themselves are `steps_taken`.
    - `stop` string, required: Why the loop ended: `end_turn` (the model finished), `max_steps` (it reached `max_steps`), `refusal` (the model declined), `pause_turn` (the model kept pausing its turn), `rate_limited` (your rate budget ran out mid-run), `credits_exhausted` (funded credits unavailable), `max_cost` (the next reservation would exceed max_cost_usd), or another stop reason the model itself gave, such as `max_tokens`. OpenAI also reports `incomplete` for an unfinished response and `safety_check` when action approval is needed. Only `end_turn` means the task was finished; the others are a run that stopped, and the work it did to the desktop stands.
    - `text` string, required: The model’s closing text — its answer, or why it could not get there.
    - `usage` AgentUsage, required
      - `input_tokens` integer, required: Input tokens, the cache reads and writes below included.
      - `output_tokens` integer, required
      - `cache_read_tokens` integer, required
      - `cache_write_tokens` integer, required: Provider-reported cache writes; zero only when absent. OpenAI reads input_tokens_details.cache_write_tokens.
    - `thread_id` string: The thread this exchange was kept in; send it as `thread_id` to continue. Absent with `store: false`.
    - `steps_taken` AgentStep[]: Every action, in order. On the `stream: false` body only; the stream’s `done` event carries the other fields, and each action arrived before it as a `step` event.
      - `cost_usd` number: Cumulative funded run charge in USD. Multiple tools in one reply repeat the same total; do not sum these fields.
      - `credits_remaining_usd` number: Available account AI credits after this model call; may be negative after an overrun. Omitted if the post-debit balance lookup is unavailable.
      - `n` integer, required: Its position in the run, from 1.
      - `tool` string, required: The tool the model reached for, e.g. `computer` or `bash`.
      - `action` string: The provider-native action within it, e.g. `left_click`, `zoom` or OpenAI `click`. Absent for `bash`.
      - `detail` string: What the platform did with it, in one line.
      - `error` string: Present when an action failed or was refused. On an Anthropic run the model is told and the loop goes on. On an OpenAI run a failed batch or `bash` command ends the run as a failure, after the actions the desktop acknowledged; input is not replayed.
  - `error` object
    - `error` string, required: What went wrong, in a sentence.
    - `status` integer, required: The status the agent route would have answered it with.
    - `reason` string: The one-word classification, where there is one — see `Error`.
    - `top_up_url` string: Where to add credits, when the run failed for want of them.
  - `steps_taken` AgentStep[], required: Every action so far, in order. Empty in a list.
    - `cost_usd` number: Cumulative funded run charge in USD. Multiple tools in one reply repeat the same total; do not sum these fields.
    - `credits_remaining_usd` number: Available account AI credits after this model call; may be negative after an overrun. Omitted if the post-debit balance lookup is unavailable.
    - `n` integer, required: Its position in the run, from 1.
    - `tool` string, required: The tool the model reached for, e.g. `computer` or `bash`.
    - `action` string: The provider-native action within it, e.g. `left_click`, `zoom` or OpenAI `click`. Absent for `bash`.
    - `detail` string: What the platform did with it, in one line.
    - `error` string: Present when an action failed or was refused. On an Anthropic run the model is told and the loop goes on. On an OpenAI run a failed batch or `bash` command ends the run as a failure, after the actions the desktop acknowledged; input is not replayed.
  - `created_at` string, required
  - `updated_at` string, required: When it last took a step or changed status.
  - `ended_at` string, nullable, required: When it ended; `null` while running.

## Other responses

- `401`: Platform authentication required or refused: reason is missing, invalid, or revoked, with WWW-Authenticate: Bearer (invalid_token for invalid/revoked).
- `403`: The credential is recognized but its holder lacks the required role or membership, or the holder/account is suspended, or the holder has not verified their email address. An ordinary denial for too low a role may carry no `reason`. The email one carries none either, and its message begins `Verify your email before`: it answers a create, a clone of a computer or a snapshot, a new API key and a new SSH key, and is cleared by verifying the address in the dashboard. `reason: "revoked"` marks authority that is gone: a suspended account or a lost membership (refused as the request arrives, or if it happens while the request is in flight), or a role that was lowered while the request was in flight. The message identifies the refusal; replacing a valid key alone does not restore authority, and sending the same call again does not help.
- `404`: No such resource in your scope — a computer, snapshot, secret, webhook, SSH key, API key, workspace, operation and so on — or no such endpoint; a confirmed missing guest file is no such file in the guest.
- `405`: This public path exists but does not accept that method. Allow lists only its public methods, with HEAD for GET and metadata OPTIONS.
- `429`: You are asking faster than your plan allows. `Retry-After` says how many seconds to wait, and every metered response — anything past authentication and the re-check that your credential’s authority still holds — carries `RateLimit-Remaining`, so you do not have to be refused to find out. A `401`, a `403` from that re-check, an `OPTIONS` answer and an unexpected `500` carry none. The budget is spent in proportion to what a call costs rather than per request. What a call costs, in budget units, by kind (each operation’s exact figure is its `x-rate-cost` in the OpenAPI document): 1 to read one thing; 5 for a listing that spans the fleet, and 5 for a call into a running guest such as a screenshot or an exec; 10 for a power change and 10 to move file bytes; 25 for `GET /usage`, `GET /credits`, creating a public URL, and creating, rotating or testing a webhook; 50 to capture a snapshot; 100 to create, clone or restore a computer, and 100 to start an agent run, whose actions are then charged as they happen. The account-wide budget, as what an idle caller may spend at once and what refills each minute: No plan 1200 at once and 1200 a minute; Solo 4800 at once and 4800 a minute; Studio 12000 at once and 12000 a minute; Swarm 21600 at once and 21600 a minute. A single API key gets half of either. A `401` for a missing or unusable credential costs nothing: it is refused before anything is metered. A few refusals at the door cost one unit: a `403` because the role is too low or the route refuses an API key confined to a workspace, a `404` for a path that matches nothing, a `405`, and an `Idempotency-Key` answered from its stored result or still in progress. Everything else is charged in full when it is admitted, before it is carried out, so a refusal after that has spent the whole cost: a `400`, `402` or `409` from the route, a `403` its handler sends (on most routes, a suspended account included), and a model provider’s `401` on an agent run. The budget belongs to the account and is shared across everything on it, and any single API key may spend at most half of it. `RateLimit-Limit` is whichever of those two is binding on the request that carried it — usually the per-key half, and the account-wide figure when other keys have already spent more of it than this one has. All three `RateLimit-*` headers describe the same one, so `limit` minus `remaining` is what has been spent against it. A few `429`s are not about this budget, say so in their message, and can carry no `Retry-After`: a model provider’s rate limit relayed on an agent run. The cap on forks of one snapshot waiting for their secrets sends its own bounded expiry-based `Retry-After` with `reason: "contention"` (see `POST /snapshots/{id}/clone`).
- `500`: The application could not finish this: an unexpected exception, a build that broke, or a storage fault. Application refusals carry the request correlation ID; unexpected internal details are not returned.

---

[API](https://skmtc.dev/mandala/apis/mandala-computer-api.md) · [All operations](https://skmtc.dev/mandala/apis/mandala-computer-api/llms.txt) · [OpenAPI document](https://skmtc.dev/mandala/apis/mandala-computer-api/revisions/2afbb87fa703?raw)
