
# Permissions & Approval

Two different questions, two different blocks. `permissions` says who may set an [agent](/en/docs/ai-agents) in motion. `approval` says which of its actions stop and wait for a person once it is running.

## Who May Invoke

```yaml
agents:
  - name: support-agent
    role: support
    systemPrompt: You are a courteous support assistant.
    permissions:
      type: agent
      trigger: [admin, member]
```

| Property                  | Description                                                                        |
| ------------------------- | ---------------------------------------------------------------------------------- |
| `permissions.type`        | User-type discriminator. Always `agent`.                                           |
| `permissions.trigger`     | Who may invoke: `all`, `authenticated`, or a role array such as `[admin, member]`. |
| `permissions.emailDomain` | Domain for the synthetic agent address. Defaults to `agents.sovrium.local`.        |

`trigger` governs the invocation surfaces — the [chat panel](/en/docs/ai-chat), the API, an automation. It does **not** govern what the agent may then do: that remains its role plus its [tools allowlist](/en/docs/agent-tools). A `viewer` allowed to trigger an admin-role agent is triggering something more privileged than themselves, so read `trigger` as delegation and set it deliberately.

## Human-in-the-Loop Approval

`approval` inserts a person between the model's decision and its effect.

| Property              | Description                                                                              |
| --------------------- | ---------------------------------------------------------------------------------------- |
| `approval.mode`       | `none` (execute immediately), `all` (everything waits), or `selective`.                  |
| `approval.required`   | Actions needing approval — a subset of `tools.actions`. Required when `mode: selective`. |
| `approval.timeout`    | Seconds before a pending approval expires. Defaults to `3600`.                           |
| `approval.escalation` | `{ after: <seconds>, to: <role> }` — hand an unactioned request to another role.         |

```yaml
approval:
  mode: selective
  required: [record.delete, email.send]
  timeout: 1800
  escalation:
    after: 600
    to: admin
```

Read that as a timeline. The agent decides to send an email; the request goes to a human. Ten minutes later, nobody has acted, so it escalates to `admin`. Twenty minutes after that the timeout hits and the request expires unexecuted.

:::callout
**`after` must be less than `timeout`.** An escalation scheduled at or past the expiry never fires — the request dies before anyone is asked. Leave enough room after the escalation for the escalated-to role to actually respond; `after: 600` with `timeout: 660` is technically valid and practically useless.
:::

## Choosing a Mode

| Mode        | Right when                                                                         |
| ----------- | ---------------------------------------------------------------------------------- |
| `none`      | Every action the agent can take is reversible and low-stakes. A read-only analyst. |
| `selective` | Most work is routine but a few actions leave the building. The usual answer.       |
| `all`       | A new agent you do not yet trust, or one whose whole remit is consequential.       |

`selective` is the default choice in practice, and the discipline is to ask of each action: if the model gets this wrong, can I undo it? `record.update` on an audited table is recoverable. `email.send` is not — the message is gone. Neither is `auth.banUser`, from the banned user's point of view.

Running `all` for a fortnight after deploying a new agent is a cheap way to learn what it actually does before narrowing to `selective`. The approval queue doubles as a log of intentions.

## Approval and Scheduling

Scheduled runs respect approval. An agent with `mode: all` on a nightly cron does not execute at 3 a.m. — it queues a request that sits until someone arrives, and expires if `timeout` passes first.

That combination is usually a mistake, and worth naming: an unattended trigger paired with an attended gate produces an agent that reliably does nothing overnight. Either narrow to `selective` so the routine part proceeds, or accept that the schedule only prepares work for the morning. See [Scheduling & Limits](/en/docs/agent-schedule-limits).

## Related Pages

- [Agents Overview](/en/docs/ai-agents) — the agent these blocks configure.
- [Tools](/en/docs/agent-tools) — the actions `approval.required` selects from.
- [Scheduling & Limits](/en/docs/agent-schedule-limits) — unattended runs.
- [Roles & RBAC](/en/docs/auth-roles-rbac) — the roles named in `trigger` and `escalation`.
- [AI Chat](/en/docs/ai-chat) — one of the surfaces `trigger` governs.
