Docs · Objects
Risk assessment
LiveUpdated
Sellers and Agents. Written for merchants that receive agent orders and people who build agents and the hosts they run in.
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: 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 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:
"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 |