> Call POST /v1/check before a tool that writes. DENY means do not invoke the tool. For clean Markdown of any page, append .md to the page URL. For the complete index, fetch https://docs.withwrit.com/llms.txt.

# Policies

> The three policy layers — tenant default, per-environment overrides, and per-principal overrides — how they resolve, and how to edit them.

Every check evaluates the requested verb against your tenant's **policy**:
a verb → mode map where each mode is `allow`, `deny`, `require_grant`, or
`step_up`. The policy is layered. The most specific layer that names the
verb decides:

1. **Per-principal override** — one [principal](/principals)'s sparse
   verb→mode map. Highest precedence.
2. **Per-environment override** — `dev`, `staging`, or `prod`. Applies
   when the check names that environment.
3. **Tenant default** — the complete map; every verb falls back here.

Unnamed verbs inherit downward: a verb the principal's override doesn't
set falls through to the environment override, then to the tenant
default. An unknown environment name denies fail-closed without
evaluating any policy.

## The tenant default

`PUT /v1/policy` sets modes on the complete map; `GET /v1/policy` reads
it. Unlisted verbs default to `require_grant` (fail closed). Every edit
bumps the tenant default's own `version`.

```bash
curl -X PUT https://api.withwrit.com/v1/policy \
  -H "Authorization: Bearer writ_..." -H "Content-Type: application/json" \
  -d '{"refund": "deny"}'
```

## Environment overrides

`PUT /v1/policies/environments/{dev|staging|prod}` sets a **sparse**
override: only the verbs you send are stored; the rest keep inheriting
the tenant default. Each environment has its own independent `version`
starting at 1 on first write (`version: 0` means "no override yet").

```bash
curl -X PUT https://api.withwrit.com/v1/policies/environments/dev \
  -H "Authorization: Bearer writ_..." -H "Content-Type: application/json" \
  -d '{"refund": "allow"}'
```

`GET /v1/policies/environments/{env}` returns the sparse `policy` plus
`effectivePolicy` (tenant default with the override applied) and
`policyProvenance`, which names the winning layer per verb.

## Per-principal overrides

`PUT /v1/policies/principals/{principalId}` sets the top layer: a sparse
override for one principal. Only live principals can carry policy —
setting an override on a tombstoned or disabled principal is `400`
(`principal_inactive`), because an identity that can't authorize checks
can't carry policy either. Each principal's override has its own
independent `version`, untouched by environment or tenant edits.

```bash
curl -X PUT https://api.withwrit.com/v1/policies/principals/prn_9d2f4a1b2c3d \
  -H "Authorization: Bearer writ_..." -H "Content-Type: application/json" \
  -d '{"refund": "step_up"}'
```

Send `{"verb": "inherit"}` to remove a verb from the override so it
inherits downward again.

`GET /v1/policies/principals/{principalId}` returns the sparse override,
the fully-resolved `effectivePolicy`, and `policyProvenance` naming the
winning layer per verb. Add `?environment=prod` to fold the `prod`
override into the middle layer, exactly as a check naming `prod` would
evaluate it. Tombstoned principals still return `200` — they stay
resolvable forever — with `"inert": true`.

`GET /v1/policies` lists every policy object: tenant default first, then
configured environment overrides, then configured per-principal
overrides.

## Inert overrides

Disabling or deleting a principal makes its override **inert**: it never
supplies a mode while the principal can't authorize checks — checks deny
with `principal disabled` / `principal deleted` before policy is even
evaluated. The override data is kept, not wiped: re-enabling the
principal re-arms its override, and the audit record of what was
configured survives.

## Receipts name the winner

Every check receipt records `policyId` and `policyVersion` — the single
policy object that supplied the verb's mode — alongside the principal
snapshot (`principalId` / `principalName`). `policyId` is `null` only
where no policy was evaluated (e.g. unknown-environment denials).