# Risk assessment

The signed assessment made with every decision, and the risk signal the merchant receives instead of the agent’s own claim.

Status: Live · Updated: 2026-09-13
For: seller, agent
Source: https://enruta.ai/docs/risk-assessment

Today a merchant’s `risk_signals` are whatever the agent platform reports about itself. The risk assessment is made by Enruta at decision time, signed, verifiable with the mandate, and shaped to drop into the enumerations merchants and PSPs already use.

## Fields

| Field | Contents |
|---|---|
| `id`, `policy_version`, `policy_hash` | `rsk_01M2511ZB2K6Q9W3T7N4B8D1XR`; one assessment per decision |
| `checks[]{name, result, detail}` | `result`: `PASS` · `FAIL` · `SKIP`. The seven policy checks (`payee_allowlist`, `per_payment_cap`, `cumulative_budget_atomic`, `time_window`, `category`, `duplicate_idempotency`, `sanctions_screen`) plus `velocity` (more than 5 requests by the agent in 60 s fails), `catalog_snapshot_match` (skipped unless the request carried `catalog_snapshot_hash`) and `deviation_from_intent` (skipped unless `intent.constraints.max_amount` was given; fails when the amount exceeds it) |
| `agent_identity{kya_attestation_id, method, verification_status}` | From the latest [attestation](https://enruta.ai/docs/consent-and-kya): `verified` · `unverified` · `stale` |
| `principal_binding{principal_id_readable, approval_event_id}` | The login id, and the approval event once a step-up completes |
| `score` | 0 to 100, 0 = no risk: +40 per failed hard check, +15 for a first-time payee, +10 for a velocity failure, capped at 100 |
| `action` | `authorized` · `manual_review` (step-up) · `blocked` (deny), the same words ACP and Adyen use |
| `signals_for_merchant` | `{ amount_within_mandate, payee_in_mandate, human_approved, first_time_payee, agent_session_age_seconds }`, the disclosable subset |

The assessment is `decision.risk` in the record and is signed by Enruta inside the bundle (`signatures.risk`, over the JCS digest of the object); the verifier checks that signature. Confirmations inside a model’s UI are not an input: a host’s “Allow once” button has no amount, payee, expiry or signature.

## How the merchant sees it

When the [Accept middleware](https://enruta.ai/docs/accept-middleware) reports an order or a merchant calls `POST /v0/sellers/orders`, the answer carries a `risk_signals` entry the merchant can store next to its PSP’s own:

```json
"risk_signals": [{
  "type": "enruta_mandate_verified",
  "score": 0,
  "metadata": {
    "enruta_record": "https://api.enruta.ai/v0/records/rec_01M25129GZMJXTNQ47TMZRDGHN",
    "mandate": "mnd_01M2512M8FQ7ZK4T1V9W3N6BXC",
    "human_approved": false,
    "amount_within_mandate": true,
    "payee_in_mandate": true
  }
}]
```

## Two examples

Policy: $200 a payment, $1,000 a month, a person above $150. The agent requests $180: `per_payment_cap PASS`, `cumulative_budget_atomic PASS (820.00 remaining)`, approval required → `step_up`, `action: manual_review`, `score: 0`. The controller approves; `principal_binding.approval_event_id` is filled in, `action` becomes `authorized`, `signals_for_merchant.human_approved: true`.

The same agent requests $180 from the same payee thirty seconds later: `duplicate_idempotency FAIL`, `action: blocked`, `score: 40`. The denial is sealed with its assessment like any other decision.

## Interfaces

| Surface | Where |
|---|---|
| Records | `decision.risk` on every `enruta/0.2` payment record; `signatures.risk` in the bundle |
| REST | `risk` summary on `POST /v0/payment-requests` and `GET /v0/payment-requests/{id}`; `risk_signals` on `POST /v0/sellers/orders` and `GET /v0/sellers/orders/{id}` |
| MCP | `risk: { action, score }` in every payment result |
| Console | The checks table on `/payments/{id}`; the risk signal on the seller’s order page |
