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

# Human Approval

> Add a human decision gate before an action can execute.

## Why it matters

For high-impact actions (payments, account changes, deletions), `approve` prevents automatic execution until a human approves.

## Create a gated action

```bash theme={null}
curl -X POST http://localhost:3000/v1/actions \
  -H "Authorization: Bearer <API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://api.example.com/refunds",
    "body": { "refund_id": "rf_123", "amount": 2500 },
    "approve": true
  }'
```

Initial status is `awaiting_approval`.

## Four approval paths

<AccordionGroup>
  <Accordion title="1) Magic-link email">
    Pass `notify: { email: "approver@example.com" }` when creating the action. SafeFetch sends an email with approve and reject buttons. Clicking approve or reject takes no additional setup — it resolves the action and redirects to the receipt page.

    ```bash theme={null}
    curl -X POST http://localhost:3000/v1/actions \
      -H "Authorization: Bearer <API_KEY>" \
      -H "Content-Type: application/json" \
      -d '{
        "url": "https://api.example.com/refunds",
        "body": { "refund_id": "rf_123", "amount": 2500 },
        "approve": true,
        "notify": { "email": "approver@example.com" }
      }'
    ```

    Requires `RESEND_API_KEY` to be set in your environment. If unset, the action is still created but no email is sent.
  </Accordion>

  <Accordion title="2) In-conversation via MCP">
    Agent calls `approve` tool after explicit human confirmation.
  </Accordion>

  <Accordion title="3) Callback notification path">
    Your app receives action events via `callback`, prompts a reviewer, then calls approve/cancel via API.
  </Accordion>

  <Accordion title="4) Direct API approval call">
    Backend or operator console calls `POST /v1/actions/{id}/approve`.
  </Accordion>
</AccordionGroup>

## Incompatibility with `sync: true`

`approve: true` cannot be combined with `sync: true`. Synchronous mode blocks the request until the action completes — that's incompatible with waiting for a human decision. Attempting both returns a `400` with error code `invalid_sync`.

```bash theme={null}
# This returns 400
curl -X POST http://localhost:3000/v1/actions \
  -H "Authorization: Bearer <API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{ "url": "...", "approve": true, "sync": true }'
# → { "error": "invalid_sync", "message": "Cannot use sync with approve: true — sync cannot wait for human approval" }
```

## Rejecting an action

Reject by cancelling while in `awaiting_approval`:

```bash theme={null}
curl -X POST http://localhost:3000/v1/actions/<act_id>/cancel \
  -H "Authorization: Bearer <API_KEY>"
```

## End-to-end conversation example

<Frame>
  ```text theme={null}
  User: Send a $5,000 vendor payout to ACME.
  Agent: This action requires approval. Should I send it for review?
  User: Yes.
  Agent: Action created: act_abc123 (awaiting_approval).
  Reviewer: Approved.
  Agent (MCP -> approve): Action act_abc123 approved and now pending delivery.
  Agent: Payout dispatch has started. I'll report completion status.
  ```
</Frame>
