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" }
202 { "status": "queued", "run_id": "…" }Follow it
GET /v1/runs/{run_id} → status, specialists, activity, any pending approval
POST /v1/runs/{run_id}/guidance → steer it mid-flight
POST /v1/runs/{run_id}/cancel → stop itPoll 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 six 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.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
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.