# Approvals

> Putting a person in the loop: where an agent must ask, and how it is enforced.

An agent asks for a person whenever its output has an effect outside the run. Where it must ask is declared on its card, and enforced by the engine at run time, never by its prompt.

## Declaring approval points

Two fields on the [card](/concepts/agents/#the-agent-card) decide it:

```yaml
- id: publish
  effect: external        # it sends, publishes or writes into one of your systems
  autonomy: 1             # a named person approves each output
```

| `effect`   | `autonomy` | Behaviour                                                                |
| ---------- | ---------- | ------------------------------------------------------------------------ |
| `internal` | (ignored)  | Runs unattended; writes only to the run’s own store                      |
| `external` | `0`        | A draft only. A card that `needs` it is refused when the catalogue loads |
| `external` | `1`        | Waits for a person to approve each output                                |
| `external` | `2`        | Goes ahead within a standing pre-approval; otherwise waits               |

`effect` defaults to `external` and `autonomy` to `1`, so a card that forgets to say is gated rather than free. A pack’s `ceiling` (1 by default, at most 2) can lower a card’s level, never raise it.

## Pre-approvals

A card at level 2 declares the kind of action it performs and the parameters it reports for each one:

```yaml
- id: send-reminder
  effect: external
  autonomy: 2
  action: reminder.send
  action_params: [audience, template]
```

The catalogue names its `approvers`: the only people who may grant a pre-approval. A handler grants one in LightHouse (`preapproval.grant`) for that action, with constraints on the declared parameters, a daily and a total limit, and an expiry. When the agent would wait, the orchestrator looks for a live grant that covers it; if one does, the use is counted and the output approved at once, naming the grant and its approver. If none does, or the store cannot be read, it waits for a person.

## Enforcement at run time, not in the prompt

- The orchestrator decides whether an output waits, from the card. The model is never asked.
- With approvals managed by LightHouse (the default for new deployments), the engine accepts a decision only as a command signed by LightHouse, from a person with a second factor. Any other route is refused and logged.
- A decision carries the digest of the artefact the person saw; if it changed, the decision is refused.
- If LightHouse cannot be reached, nothing is approved.

See [Approvals](/concepts/approvals/) and [Handlers](/concepts/handlers/).

Source: https://docs.atlanticagenticlabs.com/build/approvals/
