Spend7

How to set x402 spend limits your agent cannot argue with

AcademySpend7 Content Team6 min read
Bundled fibre optic cables, representing the machine-speed traffic that x402 spend limits have to bound
Bundled fibre optic cables, representing the machine-speed traffic that x402 spend limits have to bound Photo via Unsplash.

TL;DR: key takeaways

  • x402 spend limits belong in a service the agent cannot reach, not in a system prompt it can be talked out of.
  • Use rolling windows. A calendar-day cap lets an agent spend twice its limit across a midnight boundary.
  • Pass the server's quote with the intent, or the x402-specific checks (overrun, asset mismatch, expired quote, nonce replay) cannot run.
  • Only payments that were not denied count towards a cap, otherwise a burst of denials locks out the next legitimate payment.

You gave an agent a budget in its system prompt and it stayed inside it for six weeks. Then a retry loop turned a $4 API call into $3,600 overnight, and it turned out the budget was a suggestion all along. x402 spend limits fix that by moving the ceiling somewhere the agent cannot reach: a service that answers before the money moves. This is the practical version: six steps, roughly ten minutes, with the specific mistakes that cost people money in between.

Why the prompt is the wrong place for a limit

x402 revives HTTP 402 Payment Required as a working status code. A server quotes a price, the client pays, the request retries with a signed payment payload. It shipped in May 2025 and it is genuinely good design; the x402 specification is short enough to read in a sitting, and RFC 9110 gives the status code its formal meaning.

What the protocol deliberately does not do is decide whether you should pay. That is your problem, and it is where x402 spend limits come in.

A limit written into a prompt fails in three ordinary ways. The agent loops and never re-reads its budget. The context gets truncated and the budget falls out. Or something in a fetched page persuades the agent that this payment is an exception. None of those require an attacker. Two of them are just Tuesday.

A limit held in a service fails in none of them, because the agent is not consulted.

"The test we suggest is blunt: can the agent, by reasoning, arrive at a state where it spends more than you intended? If the limit is in the prompt, the answer is always yes, and it does not take an attacker to get there, just a long enough context and a plausible enough reason."

the Spend7 engineering team

That is the whole case for putting x402 spend limits behind an API rather than in an instruction.

Step 1: give every agent its own identifier

This is the step people skip, and it quietly disables everything downstream.

Send a distinct agentId on every check. Not one shared key for the fleet: research-agent-01, procurement-buyer, ci-runner. Per-agent caps bind to that string, and so does the behavioural baseline that decides whether $900 is unusual for that agent.

Share one identifier across twelve agents and you have one blended baseline that describes none of them.

Step 2: set a rolling daily cap

Start at about twice what you expect to spend. You are not budgeting. You are catching the runaway.

Use a rolling window, and be clear about what that means: a daily cap covers the last 24 hours from now, not the time since midnight. Calendar caps have a well-known hole: an agent spends its full allowance at 23:50 and its full allowance again at 00:10, in a timezone nobody agreed on.

Step 3: add a per-payment ceiling

Behavioural scoring needs history. Spend7 wants eight settled payments before it trusts an agent's amount baseline, and until then it says so rather than pretending an unknown is a normal.

A per_transaction cap covers that gap deterministically. Any single payment above it is denied on the rule, whatever the score says. For an x402 agent buying API calls, something like $25 is generous and still catches the decimals bug that turns $0.05 into $5,000.

Step 4: call the check before you settle

The order matters more than the code. Score the intent, then settle, not the other way round.

import os, requests

verdict = requests.post(
    "https://spend7.com/api/v1/risk-check",
    headers={"Authorization": f"Bearer {os.environ['SPEND7_API_KEY']}"},
    json={
        "intent": {
            "agentId": "research-agent-01",
            "amountMinor": 4_000,          # 0.004 USDC, in minor units
            "currency": "USDC",
            "merchantId": "api.example.com",
            "category": "data_api",
            "rail": "x402",
        }
    },
    timeout=5,
).json()

if verdict["decision"] == "deny":
    raise PaymentRefused(verdict["summary"])
if verdict["decision"] == "flag":
    escalate(verdict)          # step-up, do not settle silently
else:
    settle()                   # only now does money move

Note the minor units. USDC has six decimals, not two, and hardcoding two is how 1 USDC renders as 10,000 USDC in a report. We shipped that bug ourselves and fixed it in ruleset 2026.08.2, which is why the field is explicit.

Step 5: pass the quote so the x402 spend limits become protocol-aware

This is the step that separates x402 spend limits from a generic cap.

Include what the server told you: the quoted maximum, the asset, the expiry and the nonce. With those, four protocol checks become possible:

CheckFires whenWhy it matters
Quote overrunAuthorisation exceeds the server's quoted maximumThe classic decimals bug, and the shape of a substituted payment requirement
Asset mismatchPaying in an asset the server did not quoteWrong-chain and wrong-token errors that silently succeed
Expired quoteQuote's validity window has passedReplayed or stale response, price may have moved
Nonce replayNonce already seen for this merchantThe same authorisation being submitted twice

Table: x402 protocol checks and what each one is actually protecting against. Rail defects score up to 60 points in ruleset 2026.08.2 and can be marked decisive.

Omit the quote and these simply cannot fire. The cap still works; the protocol layer goes dark.

Step 6: read the log, then tune

After a fortnight, open the transaction log and look at what fired rather than what you feared.

The usual finding is dull and useful: the daily cap never came close, one agent trips off-hours constantly because it runs a 04:00 batch, and there was exactly one retry loop nobody had noticed. Tune to that. Raise the ceiling that never binds, and leave the one that caught something.

A worked example

A research agent pulls paid market data over x402. Typical call: $0.004. Typical day: 600 calls, about $2.40.

Caps set at per_transaction $5 and daily $25 (roughly ten times expected, deliberately loose).

At 02:00 a webhook is dropped and the agent stops seeing its own settlements. It retries. Within four minutes it has attempted the same call 41 times.

  • Retry loop fires at three identical repeats: up to 30 points.
  • Velocity burst fires above 12 intents in five minutes: up to 25 points.
  • Combined: 55, past the flag threshold of 50, under deny at 80.

So the first several retries are flagged, not denied. The agent keeps going. At attempt 6,250 (roughly $25) the daily cap breaches and every subsequent attempt is denied outright.

Total exposure: $25 instead of the $3,600 the loop would have run to by morning. The flag at attempt three is what should have woken someone; the cap is what made it not matter that nobody was awake.

Common pitfalls with x402 spend limits

Counting denials against the cap. A denied payment never moved money. Count it and a burst of denials eats the allowance, locking out the legitimate payment that follows. Only non-denied payments should consume a cap.

One cap for the whole fleet. A global cap is a good backstop and a bad primary control. It tells you the fleet overspent; it does not tell you which agent. Set per-agent caps with a global cap on top, and the tightest one binds.

Scoring nothing under a dollar. Reasonable for latency, dangerous alone. Micropayment abuse is thousands of tiny payments, none of which would ever trip a per-payment threshold. Let the rolling cap cover what the threshold skips.

Treating a flag as a warning. A flag means something crossed a step-up threshold. If your code logs it and settles anyway, you have a very well-documented loss. Either stop, or escalate.

Forgetting the asset's decimals. Six for USDC, eighteen for most ERC-20s, two for USD. Get it wrong and every cap you set is off by orders of magnitude in whichever direction hurts.

Where to go next

The x402 rail page covers the protocol checks in more depth, and the API reference has the full request shape including the context object for stateless testing. If your agent runs under a framework, the integration guides show where in LangChain, CrewAI and the OpenAI Agents SDK the check belongs: inside the payment tool, after the arguments are known and before the transfer.

And if you are weighing x402 against AP2 rather than already committed, the rails comparison is the shorter read.

Frequently asked questions

How do I set a spend limit for an x402 agent?
Create an agent-scoped cap against the agentId your payment client sends, choose a rolling window, then call the risk check before each settlement with the rail set to x402. The cap is stored server-side, so nothing in the agent's context can move it. The check returns allow, flag or deny along with every cap it evaluated and how much of each is used.
Do x402 spend limits work for sub-cent micropayments?
Yes, and the rolling window is what makes them useful there. A single $0.004 API call is never going to breach anything on its own; four thousand of them in an hour will. Most callers score everything above a dollar and let smaller calls through on the cap alone, so the latency cost stays where it belongs.
What is a quote overrun and why does it matter?
On x402 the server states the maximum it will accept, and the client signs an authorisation for some amount. A quote overrun is an authorisation for more than the server asked for. It is usually a decimals bug or a substituted payment requirement from a response nobody validated, and it has no equivalent on a card rail, so generic fraud tooling has never learned to look for it.
Can I test a spend policy before I turn it on?
Yes. Send the policy and a sample history in the request's context object and the check scores statelessly against exactly what you sent, reading nothing stored, writing nothing. It runs the same engine, so what you see in the test is what the stored policy would have done.

Score a payment before it settles

Spend7 returns allow, flag or deny in one call, with the signals that produced it. The free tier covers a single agent, its spend caps and its full decision log.

Keep reading