x402 · USDC on Base + Solana

x402-approval-page

An agent hits its spending cap, opens a request, and sends a person a link. The human pays with their own wallet — that payment is the approval — and the agent fetches the signed grant.

git clone https://github.com/nirholas/x402-approval-page && npm install && npm run dev

Pricing

RoutePriceWhat you get back
POST /requestsfreeThe approval link, the poll URL, and a one-time agentToken. Free by design — an agent that already hit its cap shouldn't pay more to ask for help.
GET /requests/:id$0.001The signed outcome as of right now: pending, approved (with the grant token and the human's on-chain receipt), declined (with their note), or expired.
POST /requests/:id/approvethe amount itselfThe human's route. Priced dynamically at exactly what the agent asked them to authorize, so the 402 challenge is the invoice. Returns the signed grant.
POST /requests/:id/declinefreeDecline with an optional note, or withdraw your own request with the agentToken. Nobody should pay to say no.
GET /a/:id · GET /requests · POST /verifyfreeThe human's page, recent requests, and signature verification.

How x402 works here

1 · 402

Request

Call a paid route. The server answers 402 Payment Required with an accepts array holding both rails.

2 · Sign

Pay

The 402 offers two rails — USDC on Base and USDC on Solana. The human's wallet picks one in the modal; the agent's x402 client picks one for its polls.

3 · Settle

Verify

That rail's facilitator verifies and settles on-chain. The server holds no key and pays no gas.

4 · 200

Artifact

The signed grant arrives in the response body — decision, amount, merchant, timestamp, and the transaction hash — with the receipt in X-PAYMENT-RESPONSE.

Quickstart

npm install && npm run dev                    # on :4040, both rails live
open http://localhost:4040/                   # play both sides

# the agent half: opens a request, then polls until you decide
PRIVATE_KEY=0xAgentWallet npx tsx examples/agent-client.ts

Payment rails

RailNetworkAssetPays toFacilitator
EVMbase-sepolia (default) · baseUSDC0x40252CFDF8B20Ed757D61ff157719F33Ec332402x402.org/facilitator
Solanasolana (default) · solana-devnetUSDCWwwuGbqHrwF5RG89KhUbmRWEvjnRH9k5kVM5p7T3WwWfacilitator.payai.network

Both entries appear in every 402. Pay in USDC on Base or Solana — your client picks the rail. Set PAY_TO_ADDRESS / SOLANA_PAY_TO_ADDRESS to receive the funds yourself.

Why the payment is the approval

A separate approved flag leaves two things to reconcile: a decision, and money that may or may not have moved. Here there is one event. The human's payment settles on-chain and the same response hands back a signed grant — decision, amount, merchant, timestamp, transaction hash — that the agent can present as proof that a person authorized this specific purchase for this specific amount.

That is also why the approve route has no fixed price. It is priced dynamically at the request's own amountUsd, so the 402 challenge is the invoice. Ask for $0.05 and the challenge says maxAmountRequired: "50000". A request that is already decided or expired has no price at all and answers 409 rather than taking money for nothing.

The human's side

The approval page is server-rendered so the person sees this request — who is asking, for what, how much, and why the agent couldn't decide alone — before any wallet appears. The button is @three-ws/x402-payment-modal from the CDN: it reads the dual-rail 402 and drives Phantom for Solana or an EVM wallet for Base.

Its SIWX re-entry matters here more than anywhere else in the suite — someone who approves several of these a day signs in once instead of every time. Its client-side spending caps give the human the same guardrail the agent has.

Pairs with

RepoRole
x402-agent-walletPolicy checks that tell an agent when it needs approval.
x402-mcp-commerceThe commerce toolbox whose spending-cap errors point here.
x402-agent-sandboxRehearse the whole escalation flow against fake merchants first.