Docs Guides
Five checks M0 to M4
LiveUpdated
A policy is enforced twice by design: Enruta decides, then the rail’s own control decides again. Spelled out, that is five checks, each with a moment, a question and a failure action. The fifth, after settlement, is the one no agent-payment protocol writes down.
| Check | When | What is checked | On failure | Status |
|---|---|---|---|---|
| M0 | Before the mandate is created | Policy applicability: is there a signed policy that covers this agent, payee and category; is the agent paused; is a consent required and present | No mandate; deny with no_matching_policy, agent_paused or consent_revoked | Live |
| M1 | Before the credential is issued | Whether the request carries what the policy needs (intent_coverage); the agent’s identity anchors and the merchant check; the intent rules; payee, category, time window, per-payment cap, cumulative budget (atomic reservation), duplicate and idempotency; the approval threshold; the model’s verdict when the policy uses one | clarify with questions, deny or step_up; under a shadow policy, observe | Live |
| M2 | Before the rail is called | The credential is bound to the mandate; the amount within tolerance; the rail’s finality_class matches what the mandate was stamped with; on a crypto rail, the collateral is read again | deny | Live |
| M3 | When the rail asks | Amount, currency and merchant against the mandate, answered inside the issuer’s two-second window | The authorization is refused: amount_exceeds_mandate, merchant_mismatch, mandate_consumed, mandate_expired | Live; 44 ms in production |
| M4 | After fulfillment and settlement | Fulfillment confirmed, cleared amount, refunds, the merchant’s order, all joined per mandate | An exception with a suggested action on the reconciliation line, into the weekly report | Live |
Around the five there are two states: observe (shadow mode), where the checks run and nothing is enforced, and clarify (intent and clarification), where the request waits for the person’s answers before M1 runs again.
Two failures, two checks
Ten agents each request $100 with $500 left in the month. The only authority on the budget is M1’s atomic reservation (UPDATE budgets … WHERE reserved + spent + amount ≤ cap): the sixth mandate cannot be issued, and the remaining five are denied with over_budget. M3 catches a different class of error: a single-use card presented at another merchant, or a merchant trying to clear $120 on a $100 card, is refused by the rail with merchant_mismatch or amount_exceeds_mandate. Together they are the answer to “you budgeted $100 and spent $1,000”.
Where each check is recorded
| Check | In the record |
|---|---|
| M0, M1 | decision.checks[], decision.reasons[], decision.judgement, decision.risk; request.intent, agent.identity, payee.identity |
| M2 | mandate, card_control_state, execution |
| M3 | rail_authorization_record with answered_in_ms |
| M4 | The settlement_record and reconciliation_line records chained to the payment |