# Runs

Source: https://amolfi.com/docs#api--runs

A run is the second shape: you hand Amolfi a goal instead of a tool call. Amo triages it, delegates to the specialists that own the work, and follows it against your real workspace — the same work you would watch happen in chat.

### Start one

Starting a run returns **202**, never 200. A run is asynchronous by nature, and a run id must never read as finished work.

```
POST /v1/runs
{
  "goal": "draft the october email from what actually shipped",
  "working_style": "autonomous",
  "effort": "normal"
}

202 { "status": "queued", "run_id": "…", "working_style": "autonomous", "effort": "normal" }
```

### Choose how involved to be

- `collaborative` — **Ask**. Amo does the work and pauses only when your judgment would materially improve the result.
- `autonomous` — **Auto**. The default. Amo assumes you may be away, makes reasonable judgment calls, and continues as durable background work.

Working style changes collaboration, not authority. It never changes a tool’s permission, risk class, cost ceiling, or approval requirement. Omit it and the server uses `autonomous`; unknown write values are refused rather than silently mapped.

### Choose the reasoning effort

- `low` — **Fast**. Speed first.
- `normal` — **Normal**. The default for everyday work.
- `high` — **High**. The kernel routes the run through the heavy reasoning model.
- `ultrawork` — **Ultrawork**. The kernel uses its separately configurable Ultrawork route.

Effort is durable run state, not terminal decoration. Start, list, status, and receipts carry it, and each execution iteration uses its model route. Effort never changes permission or approval authority.

### Find durable work

```
GET /v1/runs   → up to 25 recent goal-directed runs, current work first
```

Run history lives on the server, not in one terminal process. The CLI stores only one non-secret selected-run pointer per API origin and workspace so another terminal can use `amolfi runs status` or `amolfi runs watch` without inventing local history.

### Follow it

```
GET  /v1/runs/{run_id}           → status, style, effort, specialists, activity, input or approval
POST /v1/runs/{run_id}/guidance  → steer it, or answer a needs_input checkpoint
POST /v1/runs/{run_id}/cancel    → stop it
```

Poll the run rather than holding a connection open. Each snapshot carries the run’s status, the specialists working on it, and the recent activity lines.

### The status vocabulary

A run reports one of seven statuses. This is a deliberately narrow public vocabulary — the internal lifecycle is richer, and it stays internal so it can change without breaking you.

- `queued` — accepted, not started.
- `working` — in progress.
- `needs_input` — parked on one work question. Send guidance to answer it and wake the same run.
- `pending_approval` — stopped, waiting on a person. Not an error.
- `completed` · `failed` · `cancelled` — terminal.

Activity lines are also a projection, not raw internals: they read as `amo:…`, `specialist:…`, `tool:…`, `approval:…`, or `run:…`. A tool line can carry the hostname it called. Render that as plain text and never as a link — it is influenced by whatever the tool was pointed at.

### When a run needs an owner

`needs_input` and `pending_approval` are intentionally different. Guidance may answer a work question. It may never settle an approval.

A run that reaches something outward — sending, spending, signing, publishing — does not execute it and does not fail. It files an approval card and waits. The run surfaces as `pending_approval` with the decision’s id, and a workspace owner approves or declines in the app.

> Machines propose. Owners approve. A run is a goal, not an authorisation.

There is deliberately **no** approve endpoint. Guidance is guidance — it maps to steering a run that is already yours to steer — and adding an approve verb would be the bypass this whole design exists to prevent, wearing a different noun. The scope vocabulary makes it unrepresentable: `propose` is the ceiling.

### What bounds a run

A run’s reach is bounded by the token’s scopes, not by the goal’s ambition. A goal that needs a capability the token lacks stops with a 403 rather than returning a partial result and calling it done. Running a goal needs `runs.runs.write`; polling one needs `runs.runs.read`.

Subscribe to the `run.*` webhooks if you would rather be told than poll — `run.waiting_approval` is the moment your systems learn a human decision is needed.

[Webhooks](https://amolfi.com/docs/api/webhooks)[Approvals & governance](https://amolfi.com/docs/amo/approvals-and-governance)

```
curl https://api.amolfi.com/v1/runs \
  -H "Authorization: Bearer $AMOLFI_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $REQUEST_ID" \
  --data '{"goal":"Draft the October email from what shipped","working_style":"autonomous","effort":"normal"}'
```

202 Accepted · documented response

```
{
  "status": "queued",
  "run_id": "…",
  "working_style": "autonomous",
  "effort": "normal"
}
```

Set REQUEST\_ID to a unique value for a new run. Reuse it only when retrying the same request. Poll the returned run ID for progress.
