concepts
mandates & policy engine
Every spend is evaluated by a deterministic policy engine before the bank is touched. The rules run in a fixed order; the first one that fails decides.
three outcomes
| decision | agent receives | what happens |
|---|---|---|
| allow | status: "paid" / "initiated" | Executed immediately. The issuer can still decline (declined_by_bank). |
| review | status: "pending_approval" + requestId | Nothing charged yet. A human approves or rejects. |
| block | status: "denied" + reason | Nothing charged, logged in the audit log. Not escalated. |
card payments: decision order
Amount must be > 0.
blockMerchant name contains an entry of blockedMerchants (case-insensitive).
allowedMccs is not empty and the mcc is missing or not in the list.
Amount > perTxMax.
Executed spend today + amount > dailyMax.
Executed spend this month + amount > monthlyMax.
Amount > approvalAbove.
Otherwise.
allowSpend windows are calendar day and calendar month in UTC. Only executed card payments and transfers count; denied attempts and pending requests do not. Comparisons are strict: with approvalAbove: 250, a 250.00 EUR payment executes and 250.01 EUR is reviewed.
transfers: decision order
transfersEnabled is false.
Amount must be > 0.
blockExecuted spend this month + amount > monthlyMax.
Amount > transferApprovalAbove (default 0: every transfer).
Otherwise.
allowThe IBAN format is checked before the mandate (invalid IBAN returns 400). Executed transfers count toward daily and monthly spend like card payments.
worked examples
With the sandbox demo-agent mandate: perTxMax 500, dailyMax 2000, approvalAbove 250, no MCC restriction.
| call | context | outcome | rule |
|---|---|---|---|
pay 49 Vercel | nothing spent | allow | fits everything |
pay 250 Notion | allow | not strictly above 250 | |
pay 320 AWS | review | approvalAbove | |
pay 900 Apple | block | perTxMax | |
pay 200 Figma | 1 900 executed today | block | dailyMax |
pay 40 Lucky Casino | blockedMerchants: ["casino"] | block | blockedMerchants |
pay 30 Uber mcc 4121 | allowedMccs: ["5734","7372"] | block | allowedMccs |
send_transfer 100 | default mandate | block | transfersEnabled |
splitting is not a loophole
The MCP server instructs agents to respect each decision and never split an amount to get under a threshold. If one tries anyway, the daily and monthly limits still cap the total, and every attempt is in the audit log for you to see.
changing a mandate
update_agent_policy (or POST /api/agents/:id/policy) merges your patch into the current mandate and validates the result. Changing monthlyMax also updates the card's own limit. Changes apply to the next payment; they do not touch requests already pending.
{
"agentId": "agt_3f2b8d10",
"allowedMccs": [
"5734",
"7372"
],
"blockedMerchants": [
"casino",
"crypto"
],
"approvalAbove": 100
}recipes
| agent | mandate |
|---|---|
| SaaS buyer | perTxMax 200 · dailyMax 600 · monthlyMax 3000 · approvalAbove 100 · allowedMccs [5734, 7372] |
| Travel booker | perTxMax 1500 · dailyMax 3000 · approvalAbove 400 · allowedMccs [4511, 7011, 4112, 4722] |
| Ads spend | perTxMax 1000 · dailyMax 1000 · monthlyMax 20000 · approvalAbove 1000 · allowedMccs [7311] |
| Supplier payer | transfersEnabled true · transferApprovalAbove 0 · monthlyMax 50000 |
MCC values above are common examples; check the category your merchants actually report.