# DENIED and reason codes

`DENIED` means denied. The authority engine is deterministic and fail-closed. Surface the reasons, stop, and do not
retry with altered wording, a split amount or a different merchant spelling.

## Anatomy

Real Golden Path denial - `$75.00` against standing authority $50:

```json
{
  "decision": "DENIED", "status": "DRY_RUN",
  "effective_maximum_minor": 5000, "autonomous_maximum_minor": 2000,
  "effective_authority": { "binding_layer": "agent_standing", "amount_within_effective_authority": false },
  "denial_reasons": [
    { "code": "exceeds_effective_authority", "family": "AMOUNT_LIMIT", "severity": "deny",
      "message": "The amount exceeds the effective authority (agent_standing).",
      "effective_maximum_minor": 5000, "requested_minor": 7500, "binding_layer": "agent_standing",
      "remedy": "Lower the amount, or ask the owner to raise the task / envelope limit (owner_amendment).", "registry_version": "1.0.0" },
    { "code": "transaction_limit_exceeded", "family": "AMOUNT_LIMIT", "severity": "deny",
      "message": "Amount exceeds this agent's hard per-transaction limit", "rule": "policy.transaction_limit" }
  ]
}
```

The **first** reason is the binding one. `binding_layer` tells you *which* layer denied: `task` ("the human said
under $20"), `envelope` (compiled intent), `delegation` (parent ceiling), `agent_standing` (policy), `organization`.

## The denials you will meet on the Golden Path

| `code` | family | trigger | what changes it |
|---|---|---|---|
| `exceeds_effective_authority` | AMOUNT_LIMIT | amount > ceiling of the binding layer | lower the amount; owner widens the task/envelope (`owner_amendment`) or the policy |
| `task_amount_exceeded` | AMOUNT_LIMIT | amount > `task.max_amount_minor` (e.g. $20.01 vs $20) | the human's task said so; ask them |
| `transaction_limit_exceeded` | AMOUNT_LIMIT | amount > `policy.transaction_limit` | owner changes the standing policy |
| `commitment_prohibited` | COMMITMENT | e.g. `SUBSCRIPTION` under a task with `prohibited_side_effects: ["subscription"]` or a "one time" envelope | use a permitted commitment class or ask the owner explicitly |
| `merchant_not_allowed` / `category_not_allowed` | COUNTERPARTY / CATEGORY_PURPOSE | outside the allowed set | ask the owner |
| `envelope_deadline_passed`, `envelope_window_not_open` | TIME_WINDOW | "before friday" / "for the next two hours" elapsed | new intent |
| `delegation_exceeds_parent` | DELEGATION | child asks more than the parent still holds | parent re-delegates a narrower slice |
| `wallet_missing` (as `NEEDS_INFORMATION`) | IDENTITY_STATUS | agent has no granted authority | owner attaches wallet + ACTIVE policy |

A hidden commitment is denied even when cheaper: a `$9.99/month SUBSCRIPTION` under a `"$20 one time"` task is
`DENIED / commitment_prohibited`. An **undeclared** commitment is `NEEDS_INFORMATION / commitment_required`, never `ONE_TIME`.

## The stable reason-code registry (public, no credential)

Every reason - in `denial_reasons`, `approval_reasons`, `needs_information`, proof verification and outcome
compliance - carries the same shape: `code`, `canonical`, `family`, `severity`, `message`, `remedy`, `registry_version`.

- `GET /api/v1/public/reason-codes` -> `{registry_version: "1.0.0", count: 246, families[24], severities, codes[]}`
- `GET /api/v1/public/reason-codes?family=AMOUNT_LIMIT`
- `GET /api/v1/public/reason-codes/{code}`
- SDK: `q.reason_codes(family=...)`, `q.describe_reason(code)`; MCP: `reason_codes`.

| family | means |
|---|---|
| `AMOUNT_LIMIT` | a money ceiling (task, envelope, policy, delegation, standing) |
| `AGGREGATE_BUDGET` | remaining / aggregate budget incl. authorize-only reservations |
| `COMMITMENT` | commitment class not authorized / undeclared |
| `COUNTERPARTY`, `CATEGORY_PURPOSE`, `GEOGRAPHY`, `TIME_WINDOW` | outside the authorized set / scope / place / window |
| `DATA_DISCLOSURE` | a data field may not be released for this purpose |
| `DELEGATION`, `ENVELOPE_STATE`, `POLICY_STATE`, `IDENTITY_STATUS` | the authority chain is not in a state that can act |
| `APPROVAL_REQUIRED`, `RISK`, `SERVICE_TRUST`, `VELOCITY_DUPLICATE` | a human, the risk engine, service trust or a velocity rule intervened |
| `INFORMATION_REQUIRED`, `REQUEST_INVALID` | you must supply / fix something |
| `AUTHORIZATION_LIFECYCLE`, `PROOF_INTEGRITY`, `OUTCOME_COMPLIANCE` | not live / proof failed / execution deviated |
| `COMPILER_SIGNAL`, `INFORMATIONAL` | intent-compiler signals and advisories (do not change the decision) |

Codes are **additive**: existing codes and messages never change meaning. Branch on `code` or `family`, never on `message` text.

## Explain a denial to a human

`GET /api/v1/authorizations/{authorization_id}/explain` works for dry runs too:
`plain_language` says who asked, on whose behalf, the standing authority, what narrowed it, the effective
maximum, the policy verdict and the decision.

## Never

- Never retry a `DENIED` request with altered wording, a split amount or a renamed merchant (velocity / duplicate rules and the audit trail record it).
- Never treat a denial as a rate limit. Denials are `200 OK` with `decision: DENIED`; HTTP `429` is a rate limit.
- Never ask the agent to "approve" itself. Only owners approve, amend or change policy.
