How do I set spend limits on x402 payments?

Short answer

x402 lets an agent pay any HTTP 402 paywall by itself, and the protocol carries no budget. Rein adds one on the paying side. It reads each 402, turns it into an intent, checks the intent against your policy, and lets the agent sign the payment only if it is allowed. You can limit per payment, per rolling window, per vendor host, per resource path and per task.

Where the limit sits in an x402 payment

An x402 payment has four steps: the agent requests a resource, the server answers 402 with what it accepts, the agent signs a USDC transfer authorization (EIP-3009), and a facilitator settles it on Base. Rein sits between the second step and the third. The decision is made after the price is known and before anything is signed, so a refused payment leaves nothing on chain.

A policy for an x402 agent

Pay only the vendors you list, cap the price of a call, and budget the day. With "default": "deny", a host that no allow rule names is refused, and the deny rules still win over the allow:

policy.json
{
  "policyId": "x402-agent",
  "appliesTo": { "agents": ["agt_01J..."] },
  "rules": [
    { "id": "known-vendors", "allow": { "vendorHostIn": ["api.example.com", "data.example.org"] } },
    { "id": "tx-cap",        "deny":  { "amountGt": "0.25" } },
    { "id": "day-budget",    "deny":  { "rollingSum": { "window": "24h", "gt": "10.00" } } },
    { "id": "velocity",      "deny":  { "txCount": { "window": "1h", "gt": 100 } } }
  ],
  "default": "deny"
}

Wire the agent

The guard wraps fetch. The payer signs with the agent's own key, inside the agent's process; the policy engine only ever sees the intent, never the key.

agent.mjs
import { createGuard } from '@reinconsole/sdk';
import { createX402Payer, TESTNET } from '@reinconsole/x402-rails';

const guard = createGuard({
  engineUrl: 'http://127.0.0.1:8787',
  agentId: 'agt_01J...',
  networks: ['base-sepolia'],
  payer: createX402Payer({ privateKey: process.env.AGENT_KEY, profile: TESTNET }),
});

const res = await guard.wrap()('https://api.example.com/v1/report'); // paid only if allowed

Leave out payer and the guard runs in advisory mode: every paywall is checked and logged, and nothing is paid. It is a safe way to watch what an agent would spend before it can.

Checks x402 itself does not make

Running an API instead of an agent? @reinconsole/gate prices it per call over x402. Otherwise, start with the runbook.

Related questions