Rightshold™ Sign up

Answers · agent payments

How do I set a spending cap for an AI agent that pays with x402?

Last updated

TL;DR: Use three layers. First, the x402 client's spendControls: by default the client pays only recognized USD-pegged assets such as USDC and caps each payment at $1, and you can change that with maxAmountPerPayment and allowedAssets, but don't turn it off (spendControls: false removes all caps). Second, a total over time: the base client caps each payment, not the day, so use the CDP SDK's maxCumulativeSpend with a window such as "24h", or your own policy or hook that keeps a running total in storage that survives restarts. Third, a hard ceiling: give the agent a wallet that holds only its budget, because a bug or a missing check can get past code but can't spend money the wallet doesn't have.

What should I check, step by step?

1. Keep the x402 client's spend controls on. Don't pass spendControls: false (Go: DisableSpendControls(), Python: spend_controls=False). That turns off every cap and allows any asset.

  1. Set the per-payment cap you mean: maxAmountPerPayment ("$1" by default) for USD-pegged assets, and an allowedAssets list with an atomic-unit cap for any other token (for 6-decimal USDC, "2000000" is 2 USDC).
  2. Add a cap over time. The base client checks each payment on its own. Use the CDP SDK's maxCumulativeSpend and maxCumulativeSpendWindow, or a registered policy or onBeforePaymentCreation hook that keeps a running total.
  3. Keep that running total in persistent storage. The CDP SDK's default spend store is in memory and resets when the process restarts.
  4. Limit where the agent can pay: allowed networks (for example only eip155:8453 for Base mainnet), allowed assets and, in your own policy, allowed payTo addresses or domains.
  5. Treat upto prices as the most you'll pay: the buyer signs for the maximum and the seller settles for the actual amount, so cap the maximum, not the expected charge.
  6. Fund a separate agent wallet with only the budget and top it up on a schedule. If you use CDP Server Wallets, add a Policy Engine reject rule at the signing layer as well.
  7. Log every payment and every refusal with amount, asset, network and payTo, and alert before the limit is reached (the CDP SDK has an onApproachingLimit callback).

What the x402 client caps by default

The x402 buyer quickstart says that by default the client only pays recognized USD-pegged assets (e.g. USDC) and caps each payment at $1. maxAmountPerPayment changes the USD cap; allowedAssets opts into other tokens with an optional per-payment cap in atomic units; spendControls: false disables all of it.

Caps over a day or a session

A per-payment cap doesn't stop 1,000 payments of $0.99. You need a running total.

Why the wallet is the real ceiling

Every cap above is code in the agent's own process. A bug, an old version, or a different client using the same key can skip it. The amount a key can spend is what its wallet holds, so keep the agent's wallet small and refill it on a schedule rather than pointing it at a treasury.

What can go wrong?

Someone sets spendControls: false while debugging and ships it.

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: “Set a spending policy on my agent credential: at most 2.00 USD per purchase and 10.00 USD per day, only at api.example.com.”

One-click setup

{
  "tool": "set_spend_policy",
  "arguments": {
    "credential_id": "YOUR_AGENT_CREDENTIAL_ID",
    "currency": "USD",
    "per_tx_max_minor": 200,
    "daily_max_minor": 1000,
    "allowed_domains": [
      "api.example.com"
    ]
  }
}

Rightshold records spending policies and signed allow or deny decisions. It doesn't hold funds, sign x402 payments or stop a client that never asks. Enforce the cap in your x402 client and wallet too.

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}}'