Enruta

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

FieldContents
id, policy_version, policy_hashrsk_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
score0 to 100, 0 = no risk: +40 per failed hard check, +15 for a first-time payee, +10 for a velocity failure, capped at 100
actionauthorized · 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

SurfaceWhere
Recordsdecision.risk on every enruta/0.2 payment record; signatures.risk in the bundle
RESTrisk 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}
MCPrisk: { action, score } in every payment result
ConsoleThe checks table on /payments/{id}; the risk signal on the seller’s order page