A Paying Client That Cannot Overspend: Mandatory Caps for x402 Buyer Agents
Description and use
A client without spending limits can trigger runaway payment loops that drain a wallet. The @agentbadge/circle-payments library enforces mandatory caps and session budgets before any transaction is signed, stopping such loops.
Problem
A paying client without spending limits can cause runaway loops that drain a wallet, leading to unintended charges.
Outcome
Three live refusals on Oct 10, 2026 prevented any signing or wallet movement, demonstrating that caps and budgets effectively stop runaway loops.
Workflow
- 1. Instantiate a paying client with createPayingClient, providing caps and a signer.
- 2. Call client.get or other API methods; the client validates the 402 response against the x402 contract.
- 3. validateAccepts checks per-call cap, network allowlist, asset decimals, and amount against the per-call cap.
- 4. If validation passes, the client reserves the amount in a SpendTracker before signing.
- 5. If the reservation succeeds, the client signs the typed-data payload according to the scheme.
- 6. On success, the payment is settled and spend is recorded via onSpend hook; on failure, no signing occurs and the wallet balance remains unchanged.
Evidence
On October 10, 2026, we pointed our own agentbadge pay CLI at our own production API — and it refused to pay us. Three times, three different ways: once for the per-call cap, once for the network allowlist, once for the session budget.
createPayingClient throws a TypeError the moment you omit PaymentCaps, before a single request leaves the process.
refused field=amount value="300000": amount 300000 exceeds per-call cap 10000 (atomic units) refused field=network value="eip155:84532": network "eip155:84532" not in allowedNetworks refused: Session budget exhausted: spent 0 + requested 300000 > budget 10000 (atomic units)
- Record date: 2026-10-15T22:41:06.462Z