x402 · private payTo
Private x402 payments for AI agents.
xSHADE gives every x402 payment its own one-time address. Agents pay the way they already do. Nobody can add up what your API earns, and your server never holds a key that can spend it.
- protocol
- x402 v2
- settles in
- USDC, EIP-3009
- server holds
- public key only
Live x402 call
runs in this tab…
→ GET /v1/weather ← 402 Payment Required payTo … new price 0.01 USDC → PAYMENT-SIGNATURE (signed USDC) ← 200 OK
Each press is one paid call. Same key, a new payTo.
Network
live- network
- Sepolia
- chain id
- 11155111
- block
- …
- shielded addresses
- …
- contract
- 0xE6273F…7Dd619
One paid call, four steps
A normal x402 payment, except the address changes every time and the spend key never touches your server.
- 01agentCalls your APIAny x402 client. No SDK, no plugin, nothing new to learn.
- 02your serverAnswers 402 with a new payToMade from your public xSHADE Key. A different address for every call.
- 03facilitatorSettles the USDCThe agent signs, the facilitator settles, your API returns the data.
- 04you, offlineScan and sweepYour xSHADE Secret finds every payment. USDC moves out with no ETH needed.Scan in the browser
Add it to your API
One hook on your x402 server. xshade-x402 swaps your fixed payTo for a new shielded address and publishes the scan data in batches.
Seller · your API
import { Announcer, shieldedPayTo } from 'xshade-x402' const announcer = new Announcer({ contract, wallet, publicClient })const shield = shieldedPayTo({ xshadeKey: process.env.XSHADE_KEY, // public key only onPaid: announcer.add,}) server.onAfterSettle(shield.onAfterSettle) app.use(paymentMiddleware({ 'GET /v1/weather': { accepts: { scheme: 'exact', network, price: '$0.01', payTo: shield.payTo }, // new address per call },}, server))
Buyer · any agent
import { wrapFetchWithPaymentFromConfig } from '@x402/fetch'import { ExactEvmScheme } from '@x402/evm' // A plain x402 client. It does not know xSHADE exists.const pay = wrapFetchWithPaymentFromConfig(fetch, { schemes: [{ network, client: new ExactEvmScheme(account) }],}) const res = await pay('https://your-api.com/v1/weather')
Nothing changes for the buyer. It sees a normal 402 with a payTo, pays it, and gets the data.
Public, on chain
- Your xSHADE Keyshareable
- Each payment and its payTovisible, unlinked
- Ephemeral point E, tagbatched, delayed
Never on your server
- xSHADE Secretstays offline
- Which payTo addresses are yoursfound by scanning
- Spend keysbuilt when you sweep
Public reputation, private income
List your agent in ERC-8004 with an xSHADE Key instead of a fixed wallet. Each payment returns a signed receipt, so buyers can leave reviews that prove they paid without showing where the money went.
In the receipt
- agentIdwhich agent was paid
- reviewerthe one address that may use it
- receiptIdrandom, so it counts once
- issuedDaythe day, not the second
Signed with EIP-712 by the receipt signer named in the agent's ERC-8004 profile.
Left out on purpose
- payTo addressnot included
- tx hashnot included
- amountnot included
- exact timenot included
Any of these would let someone match the review to a transfer. Copied, forged and missing receipts are rejected.
Under the hood
No pool, no mixer, no relayer. The payTo is plain elliptic-curve math that your server and your secret both compute.
The math, all of it
- server
S = e · Kone-time key e per call, your public xSHADE Key K - you
S = k · Eyour secret k, the announced point E = e · G - both
h = keccak256(S)first 2 bytes of S become the scan tag - both
P = K + h · GpayTo is addr(P) - you
p = k + h mod nthe spend key only you can build
Curve secp256k1. Source: xshade-x402/src/keys.ts and XShade-Main.sol.
On chain now
SepoliaNo announcements yet. The first private payment on this contract shows up here.
This is everything the contract stores: an ephemeral point and a 2-byte tag. No receiver.
Or pay a person by hand
The same shielded addresses work without an API. Send ETH or any token to someone's xSHADE Key from this app.
- Generate an addressCreate an xSHADE Key in your browser. Share the public key. Keep the secret file.Open wallets
- Send any token to itThe sender pastes your key. ETH or any ERC-20 lands on a fresh shielded address.Send privately
- Scan and withdrawYour secret finds every address that is yours. Withdraw to any wallet.Receive