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

# HITL Approvals

> Human-in-the-loop approval workflow for AI agent requests

When a `require_approval` policy matches, the MeshAI proxy blocks the request and queues it for human review. An authorized user must approve or deny each request before the agent can proceed.

## How It Works

```
Agent Request → MeshAI Proxy → Policy Engine
                                    ↓
                        require_approval matched?
                           ↓              ↓
                          Yes             No
                           ↓              ↓
                    Return 403      Forward to LLM
                    + queue item
                           ↓
                   Dashboard Queue
                           ↓
                  Approve / Deny
                           ↓
                  Cache decision in Redis (TTL)
                           ↓
                  Agent retries → Approved → Forward to LLM
```

<Steps>
  <Step title="Request blocked">
    The proxy evaluates policies and finds a `require_approval` match. The request is blocked with a `403` status and an approval item is created.
  </Step>

  <Step title="Queued for review">
    The approval item appears in the MeshAI dashboard under **Governance → Approvals**. It includes the agent, the policy that triggered the block, and the request context.
  </Step>

  <Step title="Human decides">
    An authorized user reviews the item and submits a decision of `approved` or `denied`, identifying themselves as the reviewer. They can add a reason for the decision.
  </Step>

  <Step title="Decision cached">
    Approved decisions are cached in Redis so the agent does not need re-approval on every request. The approval request itself carries an expiry (default TTL: 24 hours).
  </Step>

  <Step title="Agent retries">
    The agent retries the request. The proxy checks the Redis cache, finds the approval, and forwards the request to the LLM provider.
  </Step>
</Steps>

## The 403 Response

When a request requires approval, the agent receives:

```json theme={null}
{
  "error": "approval_required",
  "approval_policy_id": 42
}
```

## Approval Statuses

| Status     | Meaning                                            |
| ---------- | -------------------------------------------------- |
| `pending`  | Queued, waiting for a human decision               |
| `approved` | Approved - agent can retry                         |
| `denied`   | Denied - agent request will continue to be blocked |
| `expired`  | TTL elapsed without a decision                     |

## List Pending Approvals

<CodeGroup>
  ```bash curl theme={null}
  curl https://api.meshai.dev/api/v1/approvals?status=pending \
    -H "Authorization: Bearer msh_YOUR_API_KEY"
  ```

  ```python SDK theme={null}
  from meshai import MeshAI

  client = MeshAI(api_key="msh_...")
  pending = client.list_approvals(status="pending")
  ```
</CodeGroup>

<ResponseExample>
  ```json Response theme={null}
  {
    "success": true,
    "data": [
      {
        "id": 17,
        "policy_id": 42,
        "agent_id": "01ARZ3NDEKTSV4RRFFQ69G5FAV",
        "team_id": null,
        "request_context": { "provider": "openai", "model": "gpt-4o" },
        "status": "pending",
        "expires_at": "2026-03-18T10:30:00Z",
        "resolved_at": null,
        "created_at": "2026-03-17T10:30:00Z"
      }
    ],
    "meta": { "total": 1, "page": 1, "limit": 50 }
  }
  ```
</ResponseExample>

## Approve or Deny

<CodeGroup>
  ```bash curl (approve) theme={null}
  curl -X POST https://api.meshai.dev/api/v1/approvals/17/decide \
    -H "Authorization: Bearer msh_YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "reviewer_id": "user@company.com",
      "decision": "approved",
      "reason": "Verified agent configuration is correct"
    }'
  ```

  ```bash curl (deny) theme={null}
  curl -X POST https://api.meshai.dev/api/v1/approvals/17/decide \
    -H "Authorization: Bearer msh_YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "reviewer_id": "user@company.com",
      "decision": "denied",
      "reason": "Agent not authorized for production use yet"
    }'
  ```

  ```python SDK theme={null}
  # Approve
  client.decide_approval(17, reviewer_id="user@company.com", decision="approved", reason="Verified")

  # Deny
  client.decide_approval(17, reviewer_id="user@company.com", decision="denied", reason="Not authorized")
  ```
</CodeGroup>

`reviewer_id` is required (1-100 characters) and `decision` must be `approved` or `denied`; `reason` is optional.

## Cache TTL

When an approval is granted, the decision is cached in Redis so the agent does not need re-approval on every subsequent matching request. The approval request's expiry defaults to **24 hours** from creation - there is currently no per-policy field to override this default.
