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

> List and decide promotion approvals — agents request, humans decide, every step is audited.

Approvals gate the money path. An agent requests promotion to paper trading; a human approves or denies. See [Approval workflow](/strategy/approvals) for the full flow.

## List approvals

```http theme={null}
GET /api/approvals?run_id=run_abc&state=pending
Authorization: Bearer <token>
```

| Query param | Type   | Description                                             |
| ----------- | ------ | ------------------------------------------------------- |
| `run_id`    | string | Optional. Filter to approvals for a specific run.       |
| `state`     | string | Optional. Filter by `pending`, `approved`, or `denied`. |

### Response

```json theme={null}
{
  "approvals": [
    {
      "id": "apr_def",
      "run_id": "run_abc",
      "scope": "paper",
      "state": "pending",
      "requested_by": { "actor_kind": "api_key", "key_id": "kid_123" },
      "decided_by": null,
      "tenant_id": "tnt_abc",
      "created_at": "2025-01-15T12:00:00Z",
      "decided_at": null
    }
  ]
}
```

Requires the `approvals:read` scope.

## Decide an approval

```http theme={null}
POST /api/approvals/{id}/decide
Authorization: Bearer <clerk-jwt>
Content-Type: application/json
```

```json theme={null}
{ "decision": "approve" }
```

| Field      | Type | Required | Description          |
| ---------- | ---- | -------- | -------------------- |
| `decision` | enum | yes      | `approve` or `deny`. |

<Callout type="warn">
  Deciding an approval is **human-only**. The endpoint requires a Clerk session JWT; an API key (agent) is rejected with `403`. The `approvals:decide` scope is only ever granted to human principals.
</Callout>

### Response (approved)

```json theme={null}
{
  "id": "apr_def",
  "run_id": "run_abc",
  "scope": "paper",
  "state": "approved",
  "requested_by": { "actor_kind": "api_key", "key_id": "kid_123" },
  "decided_by": { "actor_kind": "human", "user_id": "user_josh" },
  "decided_at": "2025-01-15T12:05:00Z"
}
```

### Response (denied)

```json theme={null}
{
  "id": "apr_def",
  "run_id": "run_abc",
  "scope": "paper",
  "state": "denied",
  "decided_by": { "actor_kind": "human", "user_id": "user_josh" },
  "decided_at": "2025-01-15T12:05:00Z"
}
```

## Trace events

Deciding an approval emits an `approval_decided` trace event, which is streamed over SSE and persisted in the audit trail. See [Trace events](/api/runs).

## Errors

\| Status | When |
\|---|---|---|
\| `403` | Caller is an API key (agent), not a human session. |
\| `404` | Approval does not exist or belongs to another tenant. |
\| `409` | Approval is not in the `pending` state (already decided). |

## Related

* [Approval workflow](/strategy/approvals) — the full flow and state machine
* [Run endpoints](/api/runs) — the promote endpoint that creates approvals
* [Scopes](/api/overview) — `approvals:read` and `approvals:decide`
* [Trace events](/api/runs) — `approval_requested` and `approval_decided`
