# Requests and policy

A request asks a person something: an approval (`ask_approval`) or an open question (`ask_human`). The policy decides which agent actions need one.

## Requests

A request goes to the project's active channels, email or Telegram, or to the ones named in `approvers`. The person answers from the message or from a one-time link.

| Status | Meaning |
|---|---|
| `pending` | Not answered yet |
| `approved` | Approved as asked |
| `denied` | Refused |
| `modified` | The person picked one of your `options`, in `decision` |
| `needs_context` | The person wants more information; any note is in `comment` |
| `answered` | An `ask_human` question was answered |
| `expired` | No answer within `expires_in` (default `24h`): treat it as not approved |

The call waits up to `wait_seconds` (default 45, max 120) for the answer. After that it returns `pending`, and the answer arrives later in three ways:

- `request_status({request_id})`, any time;
- a `request.resolved` event in the inbox, with `request_id`, `status`, `decision`, `comment` and `state_ref`, unless the request has a `callback_url`;
- a signed POST of that event to `callback_url` instead, if the request has one (create the signing secret first with `POST /v1/request-callback-secret`).

Put the id of your saved task state in `state_ref`: whoever handles the `request.resolved` event can resume the work with `state_load`. At most 50 requests can be pending in a project at once.

## Channels

A channel is an email address or a Telegram chat. The owner's email is the first channel. `channel_add` adds another one; it needs the person to confirm it (the email link, or Start in the Telegram bot) and then the approval of the project's active channels. An agent cannot add a channel that answers its own requests.

## Policy

The policy is a list of rules, checked from the top; the first rule whose conditions all hold decides.

```json
{
  "rules": [
    { "id": "no-big-refunds", "match": { "action": "refund", "amount_gt": 500 }, "effect": "deny" },
    { "match": { "action": "refund", "amount_lte": 50 }, "effect": "auto_approve_notify" },
    { "match": { "tool": "email_send", "to_domain_not_in": ["acme.example"] }, "effect": "require_approval" }
  ]
}
```

Conditions, all optional: `tool`, `action`, `risk`, `agent`, `platform` (a value or a list), `to_domain_in`, `to_domain_not_in` (lists of domains), `amount_lte`, `amount_gt`. A condition on a value the call does not carry never holds.

| Effect | What happens |
|---|---|
| `allow` | The action goes ahead |
| `deny` | `email_send` and `file_url` fail with `POLICY_DENIED`; an `ask_approval` comes back `denied` |
| `require_approval` | A person decides; `approvers` picks the channels |
| `auto_approve` | Approved without asking anyone |
| `auto_approve_notify` | Approved, and the channels are told; `approvers` picks who is told |

The policy applies to `ask_approval`, `email_send` and `file_url`. With no matching rule, `ask_approval` goes to people (so does `allow`), and `email_send` and `file_url` go ahead. `ask_human` always goes to people.

`policy_get` reads the policy. `policy_set` proposes a new one: every active channel sees the whole document, and the policy changes only when a person approves. No rule can approve a policy change, so an agent cannot widen its own permissions. Up to 100 rules and 16 KB.
