> ## Documentation Index
> Fetch the complete documentation index at: https://docs.doomberg.me/llms.txt
> Use this file to discover all available pages before exploring further.

# Approval workflow

> How a backtested strategy is promoted to paper trading — agents request, humans decide, and every step is audited.

Agents can research, propose strategies, and run backtests autonomously. But **deploying capital** — even paper capital — requires a human to approve. This is enforced at the code level, not just by policy.

## The core rule

<Callout type="warn">
  An agent can **request** promotion to paper trading (`deploy:paper` scope). Only a **human** can **approve** it (`approvals:decide` requires a human actor). An agent calling `decide_approval` is rejected.
</Callout>

This split is the boundary between the agent's autonomy and the human's authority. The agent does the research; the human flips the switch.

## Promotion flow

<Steps>
  <Step title="Agent requests promotion">
    The agent calls `request_promotion` (MCP) or `POST /api/runs/{id}/promote` with `scope: "paper"`. This creates an approval record in the `pending` state and emits an `approval_requested` trace event.
  </Step>

  <Step title="Human is notified">
    The web UI shows a pending approval badge. The human can review the run, its artifacts, and the strategy spec before deciding.
  </Step>

  <Step title="Human decides">
    The human calls `decide_approval` (MCP) or `POST /api/approvals/{id}/decide` with `decision: "approve"` or `"deny"`. This emits an `approval_decided` trace event.
  </Step>

  <Step title="System acts">
    If approved, the run transitions to the paper-trading lifecycle. If denied, the run stays in its prior state and the denial is recorded.
  </Step>
</Steps>

## Approval state machine

```
pending → approved
        → denied
```

| State      | Meaning                                     |
| ---------- | ------------------------------------------- |
| `pending`  | Request created, awaiting a human decision. |
| `approved` | Human approved. Promotion proceeds.         |
| `denied`   | Human denied. Run is not promoted.          |

An approval is **single-shot**: once decided, it cannot be reopened. A new promotion request creates a new approval record.

## REST endpoints

| Method | Path                         | Description                                      |
| ------ | ---------------------------- | ------------------------------------------------ |
| `GET`  | `/api/approvals`             | List approvals (optional `?run_id=`, `?state=`). |
| `POST` | `/api/approvals/{id}/decide` | Approve or deny. **Human-only.**                 |

See [Approval endpoints](/api/approvals) for full request/response examples.

## MCP tools

| Tool              | Scope              | Actor          | Description                                |
| ----------------- | ------------------ | -------------- | ------------------------------------------ |
| `list_approvals`  | `approvals:read`   | agent or human | List pending/all approvals for the tenant. |
| `decide_approval` | `approvals:decide` | **human only** | Approve or deny a pending request.         |

<Callout type="info">
  The `decide_approval` tool checks `actor_kind`. If the caller's principal is an API key (agent), the call is rejected with `403`. Only Clerk-authenticated human sessions can decide.
</Callout>

## Trace events

Every approval action is traced and streamed over SSE:

| Event type           | Emitted when             | `data` includes                         |
| -------------------- | ------------------------ | --------------------------------------- |
| `approval_requested` | Agent requests promotion | `run_id`, `scope`, `requested_by`       |
| `approval_decided`   | Human approves or denies | `approval_id`, `decision`, `decided_by` |

See [Trace events](/api/runs) for the full event contract.

## Audit trail

Every approval is permanent and auditable:

* The **request** records the agent's principal, the run id, the scope, and the timestamp.
* The **decision** records the human's principal, the decision, and the timestamp.
* Both are written to the shared database with `tenant_id` scoping.
* The trace events are persisted and replayable via [Session replay](/api/sessions).

<Callout>
  An approval cannot be silently forged. The `decided_by` field is stamped from the authenticated human principal — an agent cannot set it.
</Callout>

## Scopes involved

| Scope              | Who has it   | What it allows                      |
| ------------------ | ------------ | ----------------------------------- |
| `deploy:paper`     | agent        | Request promotion to paper trading. |
| `approvals:read`   | agent, human | List and inspect approvals.         |
| `approvals:decide` | human        | Approve or deny a pending request.  |

<Callout type="warn">
  `deploy:live` is **unissuable**. No principal — human or agent — can be granted live deployment. This is intentional. See [Scopes](/api/overview).
</Callout>

## Related

* [Scopes](/api/overview) — the full capability model
* [Approval endpoints](/api/approvals) — REST API reference
* [Run endpoints](/api/runs) — the promote endpoint
* [Trace events](/api/runs) — `approval_requested` and `approval_decided`
