# Policy

The rules the user signs, how Enruta enforces them twice, and the fields that set the autonomy level and what a request must say and prove.

Status: Live · Updated: 2026-09-14
Source: https://enruta.ai/docs/policy

A policy is a signed, versioned object written in the terms of the user’s own approval rules. Every decision references the exact version it was made under.

## What a policy expresses

| Rule | Field | Example | Enforced by |
|---|---|---|---|
| Payee allowlist and verification state | `payees[]{domain, name, verified, category, mcc[]}` | `reamandrule.com`, verified | Enruta; merchant lock on a single-use card |
| Per-payment cap | `limits.per_payment` | ≤ $200 | Enruta; card amount = mandate amount |
| Cumulative budget | `limits.budget{amount, window}` | ≤ $1,000 per calendar month | Enruta, reserved atomically at decision time |
| Time window | `window{hours, expiry_seconds}` | Business hours, expiry 15 minutes | Enruta; card expiry |
| Category | `categories[]` and MCC | Office supplies (MCC 5943, 5111) | Enruta; rail MCC controls where offered |
| Approval threshold | `approval{above, approvers[]}` | > $150 needs the person who approves | Enruta step-up; nothing is minted before the approval |
| Duplicate and idempotency checks | `duplicate_window` | Same payee, amount and reference inside 24 h | Enruta |
| Sanctions screening | `screening` | Payee against current lists | Enruta |
| Which agents | `agents[]` | `agt_procurement_02` | Enruta (`selectPolicy`) |

## Fields added in 0.2 and 0.3

All optional. A policy signed before they existed keeps its hash and its meaning.

| Field | Values | What it does |
|---|---|---|
| `mode` | `specified` · `bounded` · `bounded_stepup` (default) | Sets the [autonomy level](https://enruta.ai/docs/autonomy-levels): a locked target, autonomy inside the boundary, or autonomy inside the boundary with a step-up beyond it |
| `refill_policy` | `never` (default) · `on_void_only` · `on_settled_refund` · `on_any_reversal` | Whether a [reversal](https://enruta.ai/docs/reversal) gives the amount back to the mandate account and the budget |
| `deviation{tolerance, action}` | `"0.05"`; `pause_and_notify` · `auto_cancel` | Price deviation the agent may accept without asking; the card limit is opened at `amount × (1 + tolerance)` |
| `finality_rules{irreversible_per_payment}` | decimal string or `null` | A lower per-payment cap for rails whose payments cannot be reversed |
| `retry_policy{max_attempts, window_seconds, same_amount_only}` | `max_attempts` ≥ 1, the first attempt included; `window_seconds`; boolean | At most `max_attempts` attempts at one payment (the same agent, payee and purpose, after a deny, a rail decline or a hand-off) within `window_seconds`; the attempt beyond them, and with `same_amount_only` a retry for another amount, is denied with `retry_limit_reached`. Signed into the mandate ([retrying](https://enruta.ai/docs/decision#retrying)) |
| `visibility_scope{merchant[], issuer_only[]}` | `policy` · `mode` · `refill_policy` · `finality_class` · `principal_login_id` · `retry_policy` · `deviation` · `per_payment_cap` · `budget` · `recurrence` | Which of the mandate’s constraints the merchant sees. The others leave the mandate the merchant receives and are committed to by `hidden_constraints_digest`, their values kept in the record bundle ([mandate](https://enruta.ai/docs/mandate#retry-policy-and-what-the-merchant-sees)) |
| `shadow` | boolean | [Shadow mode](https://enruta.ai/docs/shadow-mode): decisions that would step up or deny come back as `observe`, sealed and reported, never enforced |
| `require_consent` | boolean (default false) | A request without an active [consent record](https://enruta.ai/docs/consent-and-kya) for the agent is denied with `consent_revoked` |
| `reversal{window_days, step_up_above}` | 120 days; decimal string or `null` | How long after settlement a refund may be requested, and above which amount a person signs the refund |
| `intent` | `{ required_slots, clarify, rules, model }` | What a request must say, the rules on its slots, and whether a model judges what rules cannot ([intent and clarification](https://enruta.ai/docs/intent-and-clarification)) |
| `identity_requirements[]` | `{ above, rails, require, on_fail }` | Which [identity anchors](https://enruta.ai/docs/identity-anchors) a payment needs, by amount and rail |
| `payee_identity` | `{ require_verified_above, on_fail }` | Whether the merchant must be verified before paying ([trust model](https://enruta.ai/docs/trust-model)) |

## Retries and what the merchant sees

A retry is a new request by the same agent, to the same payee, for the same purpose (compared without case or extra spaces) after one that failed inside the window: it was denied, the rail declined it, or it was handed off. A request still waiting for a person, and a payment that went through, are not failed attempts. `visibility_scope` changes no decision: it decides what the merchant’s copy of the mandate says. The amount, currency, payee, checkout and expiry are not constraints; the merchant always sees them.

```json
"retry_policy": { "max_attempts": 3, "window_seconds": 86400, "same_amount_only": true },
"visibility_scope": { "merchant": ["retry_policy"], "issuer_only": ["per_payment_cap", "budget", "policy"] }

// three attempts at one payment a day, the first included, never for another amount;
// the merchant sees the amount, the payee, the checkout and the retry rule, not the ceilings or which policy decided
```

When a policy declares no `visibility_scope`, the merchant sees `policy`, `mode`, `refill_policy`, `finality_class`, `principal_login_id` and `retry_policy`, and `deviation`, `per_payment_cap`, `budget` and a recurrence’s caps are not signed into the mandate at all. A name in neither list keeps that default; no name may be in both.

## Atomic budgets

Budgets are reserved when a decision is made and released or consumed when the rail posts. Ten concurrent agents cannot each pass the same $100 check, because the first reservation lowers the budget the others see. A completed reversal with a refill effect gives the amount back to the budget, never below zero.

## Enforced twice

Enruta decides first, then compiles the decision into the control the rail itself offers: a single-use card for the exact amount at the named merchant, a spend limit scoped to one purpose, an approval bound to one bill. If Enruta is wrong, the rail still refuses anything outside the mandate. If the rail is loose, Enruta already refused. The [five checks](https://enruta.ai/docs/checks) page lists each moment.

## Versions

Policies are immutable once signed. An edit creates version `n+1`; mandates issued under `n` remain valid until they expire or are consumed, and every record names the version it was decided under. Your auditor can read version 7 in 2033 exactly as it was in 2026.

## Fail closed

A request that matches no policy is denied with reason `no_matching_policy`. There is no default allow. A paused agent is denied with `agent_paused` before any policy is read.
