Approvals

Approvals

An approval is an object, not a dialog box. When a Worker proposes an action it is not trusted to take alone - a deploy, a rollback, opening a pull request - the action does not run. An approval is created and waits for a human. You can list approvals, read one, and approve or deny it over the API, the console, or a Slack button.

Why this is a resource
If approving is a popup, the only thing that can approve is a person looking at a screen. Making it an addressable object means the queue is inspectable, decisions are auditable after the fact, and the outcome of an approved action is something you can read back.

Lifecycle

  • pending - proposed and waiting. The action has not run.
  • deciding - briefly claimed while a decision is carried out. Two concurrent decisions (a Slack click and a console click) cannot both execute the action; the second one sees the row already claimed.
  • approved - the action ran. Read result for what it returned, or error if the tool itself failed. A failed tool is still an approved decision: you said yes, and the attempt is on the record.
  • denied - the action never ran, and any unattended run that was waiting on it ends.

Endpoints

bash
GET    /api/approvals                      # all states, newest first
GET    /api/approvals?status=pending       # just what needs you
GET    /api/approvals?worker_id=<uuid>     # one worker's proposals
GET    /api/approvals/{request_id}         # one approval, any state
POST   /api/approvals/{request_id}/approve # run it
POST   /api/approvals/{request_id}/deny    # discard it

What an approval looks like

json
{
  "request_id": "9f3c1e...",
  "status": "pending",
  "worker_name": "pr-reviewer",
  "worker_role": "deploy_proposer",
  "run_id": "4a1b...",
  "tool": "deploy",
  "args": { "project": "api", "env_vars": "***redacted***" },
  "requested_at": "2026-07-26T09:14:02Z",
  "decided_at": null,
  "result": null,
  "error": null
}

Arguments are shown redacted. The full arguments are stored encrypted and never leave the server - they exist only so the action can actually run once you approve. An approval card is something an agent can influence the contents of, so it is never a place secrets are printed.

Who can approve

Deciding requires a signed-in session. API tokens cannot approve - including, especially, a Worker's own token, since a worker approving its own proposal would defeat the gate entirely. An agent can see that its action is pending (it gets a pending result back, and can poll it) but it can never resolve it.

By default only the worker's owner can approve. A worker set to channel approver policy can also be approved by anyone in its report channel from a Slack button - useful for a team on call, and a deliberate loosening you opt into per worker.

Attribution

An approved action is recorded as the worker's action, not yours. Your decision is recorded separately, on the approval itself. The activity log shows the worker that proposed the action and the policy role it acted under, so "a worker deployed this, and a human allowed it" stays two distinct facts rather than collapsing into one.

Kill switch interaction
Pausing a worker, or killing agents account-wide, blocks approving its queued actions - otherwise the most dangerous residue a misbehaving agent leaves behind (an action already sitting in the queue) would still be firable after you hit the panic button. Denying stays available, so you can always clear the queue.
Reading with an agent? This page is also plain markdown at /docs/approvals.md, and the full docs index lives at /docs/llms.txt.