> ## 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 Tools

> list_approvals and decide_approval are the human's promotion workflow. The agent can request paper-trading promotion, but only a human can approve it.

The approval tools family is the **human's** workflow, not the agent's. The agent can request that a backtested strategy be promoted to paper trading, but only a human can approve or deny that request. This is the money-path gate — enforced at the code level, not just by policy.

## The approval state machine

Every approval request moves through a simple state machine:

```
pending → approved
        → denied
```

| State      | Description                                                                               |
| ---------- | ----------------------------------------------------------------------------------------- |
| `pending`  | The request has been created (by the agent) and is waiting for a human decision.          |
| `approved` | A human approved the request. The strategy is now promoted to paper trading.              |
| `denied`   | A human denied the request. The strategy is not promoted. The `reason` field records why. |

<Callout type="warn">
  `approved` and `denied` are terminal. An approval cannot be reversed through the tools API. If a strategy was approved by mistake, it must be retired from paper trading through the web UI — a separate, audited action.
</Callout>

## Who can call what

| Tool              | Agent           | Human |
| ----------------- | --------------- | ----- |
| `list_approvals`  | Yes (read-only) | Yes   |
| `decide_approval` | **No**          | Yes   |

The agent can call `list_approvals` to check the status of a request it submitted. The agent **cannot** call `decide_approval` — the MCP server rejects the call with an `insufficient_privilege` error if the caller is an agent-level API key. Only human-level API keys (issued through the web UI) can decide approvals.

This is enforced at the authentication layer, not at the UI layer. There is no way for the agent to promote a strategy to paper trading, period.

## `list_approvals`

List pending approval requests for the tenant. The agent calls this to check whether a promotion request it submitted has been decided. The human calls this (or uses the web UI) to see what's waiting.

### Arguments

| Argument | Type   | Required | Description                                                         |
| -------- | ------ | -------- | ------------------------------------------------------------------- |
| `status` | string | No       | Filter by status: `pending` (default), `approved`, `denied`, `all`. |

### Return value

An array of approval records:

| Field              | Type           | Description                                         |
| ------------------ | -------------- | --------------------------------------------------- |
| `approval_id`      | string (UUID)  | The approval identifier.                            |
| `strategy_id`      | string         | The strategy being promoted.                        |
| `strategy_version` | integer        | The version of the strategy.                        |
| `run_id`           | string         | The backtest run that supports the promotion.       |
| `requested_by`     | string         | `agent` or `human`.                                 |
| `status`           | string         | `pending`, `approved`, or `denied`.                 |
| `reason`           | string \| null | The reason provided with the request.               |
| `decision_reason`  | string \| null | The reason provided with the decision (if decided). |
| `decided_by`       | string \| null | The human who decided (if decided).                 |
| `created_at`       | string         | When the request was created.                       |
| `decided_at`       | string \| null | When the request was decided.                       |

### Example call

```json theme={null}
{ "status": "pending" }
```

### Example response

```json theme={null}
[
  {
    "approval_id": "appr_01HQKX2J3K4M5N6P7R8S9T0V4G",
    "strategy_id": "strat_01HQKX2J3K4M5N6P7R8S9T0V1Y",
    "strategy_version": 1,
    "run_id": "run_01HQKX2J3K4M5N6P7R8S9T0V2A",
    "requested_by": "agent",
    "status": "pending",
    "reason": "NVDA momentum strategy backtested with Sharpe 1.84 and CAGR 46% over 2022-2024. Requesting paper-trading promotion.",
    "decision_reason": null,
    "decided_by": null,
    "created_at": "2025-01-15T14:40:00.000Z",
    "decided_at": null
  }
]
```

## `decide_approval`

Approve or deny a pending approval request. **Human-only.** The MCP server rejects this call if the caller is an agent-level API key.

### Arguments

| Argument      | Type   | Required | Description                                                              |
| ------------- | ------ | -------- | ------------------------------------------------------------------------ |
| `approval_id` | string | Yes      | The approval identifier.                                                 |
| `decision`    | string | Yes      | `approved` or `denied`.                                                  |
| `reason`      | string | No       | A human-readable reason for the decision. Stored on the approval record. |

### Return value

| Field               | Type              | Description                                                     |
| ------------------- | ----------------- | --------------------------------------------------------------- |
| `approval_id`       | string            | The approval identifier.                                        |
| `status`            | string            | The new status (`approved` or `denied`).                        |
| `decided_by`        | string            | The human who decided.                                          |
| `decided_at`        | string (ISO 8601) | When the decision was made.                                     |
| `reason`            | string            | The reason provided.                                            |
| `paper_strategy_id` | string \| null    | If approved, the ID of the new paper-trading strategy instance. |

### Example call

```json theme={null}
{
  "approval_id": "appr_01HQKX2J3K4M5N6P7R8S9T0V4G",
  "decision": "approved",
  "reason": "Metrics look solid. Sharpe 1.84, max DD 28% is within tolerance. Approving for paper trading with a 90-day review window."
}
```

### Example response

```json theme={null}
{
  "approval_id": "appr_01HQKX2J3K4M5N6P7R8S9T0V4G",
  "status": "approved",
  "decided_by": "usr_01HQKX2J3K4M5N6P7R8S9T0V4H",
  "decided_at": "2025-01-15T15:02:33.111Z",
  "reason": "Metrics look solid. Sharpe 1.84, max DD 28% is within tolerance. Approving for paper trading with a 90-day review window.",
  "paper_strategy_id": "paper_01HQKX2J3K4M5N6P7R8S9T0V4I"
}
```

## The promotion workflow

<Steps>
  <Step title="Agent runs a backtest">
    The agent calls `backtest_run` and reviews the stats. If the results look good, it proceeds.
  </Step>

  <Step title="Agent requests promotion">
    The agent submits a promotion request (via the strategy API or by flagging the run). This creates a `pending` approval record.
  </Step>

  <Step title="Human reviews">
    The human opens the web UI, reviews the strategy spec, backtest results, risk attestation, and the agent's reasoning. The human can also re-run the backtest with different parameters to sanity-check.
  </Step>

  <Step title="Human decides">
    The human calls `decide_approval` (or clicks **Approve** / **Deny** in the web UI). If approved, the strategy is promoted to paper trading and begins generating virtual positions against live market data.
  </Step>

  <Step title="Agent checks status">
    The agent calls `list_approvals` to see whether its request was approved or denied, and reports the outcome to the human.
  </Step>
</Steps>

<Callout type="info">
  Paper trading is the last step before live capital. A paper strategy trades on live market data with virtual money. Promoting from paper to live capital is a separate, even more tightly gated workflow that is not exposed through the tools API at all.
</Callout>

<Tip>
  The human should always review the risk attestation on the backtest run before approving. A high Sharpe with a breached `max_weight` constraint is a red flag, not a green light.
</Tip>

## Related

* [Backtest tools](/tools/backtest) — the run that supports a promotion request
* [Strategy tools](/tools/strategy) — the spec being promoted
* [Artifact tools](/tools/artifacts) — the evidence bundle the human reviews
* [Strategy approvals](/strategy/approvals) — the strategy-level view of the promotion workflow
