Rightshold™ Sign up

Answers · agent payments

How do I stop an AI agent from splitting one large purchase into many small ones to dodge an approval threshold?

Last updated

TL;DR: A rule like 'ask a human above 100.00' only looks at one purchase at a time, so 400.00 sent as five 80.00 purchases never asks. People do this too: GAO's purchase card audits call it a split purchase, and the FAR forbids breaking a requirement into smaller purchases to avoid a threshold. An agent doesn't need bad intent to do it; it only needs a goal and a limit that blocks it. You can't detect intent, so put the threshold on the running total. Set the per-purchase cap at the approval threshold and a daily cap at the most you'd let through without a human in a day. On Rightshold, set_spend_policy holds both (per_tx_max_minor and daily_max_minor), and authorize_spend checks every purchase against both and the day's running total, so splits stop at over_daily_cap however small the pieces. Make anything bigger go through request_human_approval with the whole order as the action, one approval per order. Then watch for the pattern auditors use: same vendor, same day, amounts just under the threshold.

What should I check, step by step?

1. Write the threshold as two numbers: per_tx_max_minor (the largest single purchase allowed without a human) and daily_max_minor (the largest total allowed without a human in a day). The second number is the one splitting can't get past.

  1. Set both with set_spend_policy on the agent's credential, plus allowed_domains, so each authorize_spend call checks the amount, the day's running total and the merchant together.
  2. Make the agent request authorization for the whole order: one authorize_spend per cart or invoice, with amount_minor set to the full total and reference set to the order or cart id. Have your payment code refuse to pay one order with more than one authorization.
  3. Route anything over either limit to request_human_approval with the full order as the action object (merchant, items, total, order id). The approval is bound to the hash of that action, so a human approves the whole purchase, not one slice of it.
  4. If you use Rightshold's guardrail gateway (POST /v1/guard/check), set approval_over_minor for the per-action threshold, daily_max_minor for the rolling 24-hour total and rate_per_minute or rate_per_hour so a burst of small requests is denied. An approval there is single-use and only matches the exact request it was given for.
  5. Mirror the limits on the card: Stripe Issuing spending_limits with a per_authorization and a daily interval, so splits are capped at the card even if a step skips Rightshold.
  6. Review the signed decisions each day for split signals: several allows to the same merchant_domain on the same day, amounts just under the per-purchase cap, purchases minutes apart, and sequential order or invoice numbers. Treat a match as a question for the owner, not proof of misuse.
  7. Test it: with a 100.00 per-purchase cap and a 150.00 daily cap, send two 80.00 purchases to the same merchant. The first allows; the second is denied with over_daily_cap.

Why does a per-purchase approval threshold invite an AI agent to split purchases?

A threshold that looks at one purchase at a time is an instruction as much as a limit: stay under it and nobody asks. GAO's 2008 governmentwide purchase card audit defined split purchases as multiple purchases by the same cardholder at the same vendor on the same day, and found over 600 million dollars that met the definition and appeared to circumvent the micropurchase threshold.

How do I put an AI agent's approval threshold on the day's running total instead of each purchase?

authorize_spend checks each purchase against the credential's current policy: per_tx_max_minor for the single amount and daily_max_minor for the day's running total of allowed amounts (UTC day), plus the merchant allowlist. Splitting into smaller pieces still adds up, and the request that would pass the daily cap gets a signed deny with over_daily_cap.

How do I approve an AI agent's whole order at once and spot split purchases?

request_human_approval takes the action as an object and records its SHA-256 hash, so the approval covers that exact order. verify_human_approval with the same action checks the match, and a changed order fails with action does not match the approved action hash. Ask once for the full total rather than for each piece.

How do I ask Rightshold about this (free, no key)?

Add the MCP server https://www.rightshold.com/mcp to Claude, Cursor, ChatGPT, Grok or VS Code, then ask: “Stop my purchasing agent from splitting orders to stay under my 100.00 USD approval threshold: cap single purchases at 100.00 and the day's total at 150.00, only at api.example.com, and tell me what authorize_spend returns for a second 80.00 purchase the same day.”

One-click setup

{
  "tool": "set_spend_policy",
  "arguments": {
    "credential_id": "crd_YOUR_AGENT_CREDENTIAL",
    "currency": "USD",
    "per_tx_max_minor": 10000,
    "daily_max_minor": 15000,
    "allowed_domains": [
      "api.example.com"
    ]
  }
}

Rightshold records spending policies, approvals and signed allow/deny decisions on a hash-chained ledger. It never holds, moves or settles money and does not judge an agent's intent. Making the agent ask before paying, keeping one authorization per order, setting card limits and reviewing split signals are steps you run around those records. Not financial or legal advice.

What are the sources?

Related

Not legal advice. JSON · Markdown · All answers

Get a free API key in one call

No card and no email needed. 100 free credits; the license and policy checks stay free without a key.

curl -s -X POST https://www.rightshold.com/v1/accounts/signup -H "Content-Type: application/json" -d '{"accept_terms":true}'
export RIGHTSHOLD_API_KEY=rs_...   # the api_key from the reply (shown once)
curl -s https://www.rightshold.com/v1/licenses/check -H "Authorization: Bearer $RIGHTSHOLD_API_KEY" \
  -H "Content-Type: application/json" -d '{"license":"MIT","intended_use":{"commercial_use":true}}'