x402 — pay for a resource through the gateway
x402 is how a web resource charges per request: it answers 402 Payment Required with what it wants (asset, network, amount, recipient), the caller signs a transfer for exactly that and retries, the resource settles the transfer on chain and answers. With the gateway, the agent only asks for the resource; the paying is Regent’s, under the owner’s budget.
Test networks only. This rail pays on Base Sepolia with test USDC. A 402 that asks for
Base mainnet is recognised and refused (X402_MAINNET_DISABLED). Real money comes after
the gate the roadmap names: a legal opinion and a production organization with a verified
owner.
One call
GET /gw/x402/api.example.com/v1/quote?city=Astana
Authorization: Bearer <control key>
X-Agent-Id: <agent id>
X-Agent-Intent: price a courier for the Astana orderName the resource as <host>/<path>; the gateway reaches it over https only. What comes
back is the resource’s own answer, plus three headers from Regent:
| Header | Meaning |
|---|---|
X-Regent-Decision-Id | the decision this payment ran under |
X-Regent-Settlement | verified (the transfer was read from the chain and the decision is closed), unverified (the resource reported a transaction the chain did not confirm yet; the decision stays open for reconciliation), none (the resource reported no settlement) |
X-Regent-Transaction | the transfer’s transaction hash, when the resource reported one |
A resource that answers without asking for payment is passed through as it is, with no decision: nothing moved.
What happens between the ask and the answer
The 402 names the figures
The gateway reads the offer from the 402 (both x402 versions): the amount in the asset’s
atomic units, the asset, the network, the recipient (payTo). Only an exact offer in
USDC on a registered network is accepted; anything else is refused by name
(X402_UNSUPPORTED says what the resource offered). The agent’s own amount or payee
facts are dropped: the rail’s word is the only one.
The gate decides
On the 402’s figures: the agent’s USDC mandate (per payment, per day, per month), the cap
per seller (the counterparty is the resource’s host, payee_source: rail), the policy, the
risk. A refusal reaches the agent with the gate’s code; the resource is not asked again. A
payment above the owner’s threshold parks for a passkey approval like any other
(ESCALATION_REQUIRED).
Sub-cent amounts count as a cent against the budget: a thousand $0.0001 calls cannot ride under a zero.
A signer signs exactly that
The connected wallet signs an EIP-3009 TransferWithAuthorization for the 402’s recipient
and amount, with a fresh nonce and a validity of at most 300 seconds. What comes back is
compared with what was decided; a different recipient or amount voids the hold before the
retry (X402_SIGNED_MISMATCH).
Who signs is the one thing an organization chooses:
- Your Privy wallet (
wallet: privyon the tool). Your server wallet stays in your Privy app with your money. You register Regent’s authorization key as a signer on it, under a policy Privy enforces itself:eth_signTypedData_v4only, USDC on the named chain, at most X per signature, optional recipients. After the gate allows a payment, the gateway asks Privy to sign the 402’s authorization, signing the request with Regent’s key. Privy’s policy is the backstop under our decision: a signature above its cap is refused by the wallet (X402_WALLET_POLICY_REFUSED) whatever the gate said, and Regent’s key alone moves nothing. See Set it up. - Regent’s demo wallet: Regent’s own test money, for named organizations, test networks only.
There is no field anywhere for a client’s private key. Other providers that can sign only after an outside approval (Turnkey, Rox Custody) follow the same interface; Coinbase CDP has no such hook today, so a CDP wallet is governed on the seller’s side (decide-by-wallet).
The paid retry
The same request again, now with the payment header (X-PAYMENT for v1,
PAYMENT-SIGNATURE for v2). A resource that refuses the payment answers 402 again: the hold
is voided and the agent gets X402_PAYMENT_REJECTED with the resource’s reason. Nothing
moved.
The chain says what settled
The resource’s settlement header is a claim. The gateway reads the transaction’s receipt
from the chain itself and looks for the USDC Transfer to the 402’s recipient for the 402’s
amount from the signing wallet. Only on a match is the decision completed: the transaction
hash becomes settlement_ref and the chain settlement_network in the signed receipt, so
the payment can be checked later without Regent.
Try it in a sandbox
A new organization starts in a sandbox and already has the x402 tool, limited to Regent’s own demo resources, paid for by Regent’s demo wallet on Base Sepolia: your agent needs no wallet and no USDC to see the whole path.
-
Ask the owner for a USDC budget for the agent: the gate decides x402 payments in USDC, so a USD budget does not cover them. Over MCP the agent calls
request_budgetwithcurrency: "USDC"; in the console, Grant → currency USDC. The owner approves it. -
Call a demo resource through the gateway:
GET /gw/x402/api.regentprotocol.org/v1/x402-demo/echo GET /gw/x402/api.regentprotocol.org/v1/x402-demo/weather?latitude=51.1&longitude=71.4echocosts 0.001 USDC and answers with the transaction that paid for it;weathercosts 0.01 USDC and answers with the current weather (Open-Meteo). Both settle through the public facilitator on Base Sepolia; the money goes back to the demo wallet, so the sandbox is self-funding. -
Read the receipt (
receiptover MCP, or Activity in the console):settlement_refis the transaction hash,settlement_networkisbase-sepolia.
The demo wallet pays at most 1 USDC a day per organization (X402_DEMO_DAILY_CAP
when it is used up) and a global amount for everyone; a payment that would exceed it is
refused and nothing is signed. To pay elsewhere than the demo hosts, or with your own
money, connect your own wallet below.
Set it up
-
The tool. An owner adds the rail to the organization’s catalog as a catalog extra:
PUT /v1/organizations/<org>/control/catalog-extras/x402with{"spec": {"type": "x402", "requires_mandate": true, "allowed_hosts": ["api.example.com"]}}.allowed_hosts(optional) limits where agents may pay; a host and its subdomains. -
The budget. A USDC mandate for the agent, approved by the owner. Without one the gate refuses (
MANDATE_NOT_FOUND). -
The wallet. In the console, Connect → Gateway → Your wallet, through Privy: paste the App ID and App Secret of your Privy app (the free plan is enough) and a cap per payment, then Connect wallet. Regent creates, in your app, a server wallet on Base Sepolia with its signer key under a policy Privy enforces itself (USDC only, at most the cap per payment), vaults the credential, and points the x402 tool at the wallet. Fund the address it shows with test USDC (Circle’s faucet). Your app secret stays the wallet’s master; Regent’s key signs only within the policy and cannot change it, the signers or export the key. Disconnect forgets the credential and points the tool back at the demo wallet; the wallet stays in your app.
By API, the same screen is
POST /v1/organizations/<org>/gateway/privywith{"app_id", "app_secret", "cap_usdc"}; Regent’s public key is served athttps://gw.regentprotocol.org/.well-known/x402-signer.jsonfor anyone wiring it by hand.
Refusal codes
| Code | When |
|---|---|
X402_HTTPS_ONLY, X402_BAD_RESOURCE, X402_HOST_NOT_ALLOWED | the resource name |
X402_UNREADABLE, X402_UNSUPPORTED, X402_MAINNET_DISABLED | the 402 cannot be paid here |
MANDATE_LIMIT_EXCEEDED, PER_COUNTERPARTY, POLICY_DENIED, ESCALATION_REQUIRED | the gate’s answer |
X402_NO_WALLET, X402_SIGNER_UNAVAILABLE, X402_SIGNED_MISMATCH | no signature; the hold is released |
X402_WALLET_POLICY_REFUSED, X402_WALLET_AUTH_REFUSED, X402_WALLET_UNSUPPORTED | the wallet provider refused (its policy, Regent’s key not registered, an unknown provider); the hold is released |
X402_PAYMENT_REJECTED | the resource refused the signed payment; the hold is released |
X402_RESOURCE_ERROR | the resource failed after a verified payment; the decision is closed on the chain’s figures |
X402_OBSERVE_MODE | an organization in observe mode decides but never pays |