Install the SDK
Install the Key0 SDK and its peer dependencies:If you use npm or pnpm, replace
bun add with npm install or pnpm add.Get a wallet
You need an Ethereum wallet address on Base to receive USDC payments. Any wallet works — MetaMask, Coinbase Wallet, a hardware wallet, or a programmatically generated address.For testnet development, get free test USDC from the Circle faucet (select Base Sepolia).Copy your wallet address. You use it as
WALLET_ADDRESS in your configuration.Define plans
Plans describe what you sell and how much it costs. Each plan has a The
planId, a unitAmount (in USD), and an optional description.unitAmount is a string with a dollar sign prefix. The SDK parses it into the correct USDC micro-units on-chain.Implement fetchResourceCredentials
After a payment is verified on-chain, the SDK calls your
fetchResourceCredentials callback to issue a credential. This is where you mint a JWT, generate an API key, or call another service.ACCESS_TOKEN_SECRET must be at least 32 characters. Use a cryptographically random string. Generate one with openssl rand -base64 48.Set up storage
The SDK needs two stores: a Both stores accept an optional
ChallengeStore for tracking payment state machines and a SeenTxStore for preventing double-spend attacks. Both use Redis in production.keyPrefix (default: "key0") if you share a Redis instance with other services.Mount the router
Wire everything together with This mounts the following routes automatically:
key0Router. This creates an Express router that serves the A2A agent card, the JSON-RPC endpoint, and the x402 HTTP payment endpoint.| Route | Description |
|---|---|
GET /.well-known/agent.json | A2A agent card discovery |
POST /a2a/jsonrpc | A2A JSON-RPC endpoint with x402 middleware |
POST /x402/access | Simple x402 HTTP payment endpoint |
Protect your routes
Use The middleware rejects requests with missing, expired, or invalid tokens and returns the appropriate HTTP error.
validateAccessToken middleware to protect any route behind a paid JWT. After an agent pays and receives a token, it includes the token as a Bearer header in subsequent requests.Set environment variables
Create a If you use Bun, environment variables load automatically from
.env file in your project root:.env. For Node.js, use dotenv or pass them via your process manager.Test it
Start your server and test with curl.Discover the agent card:Request access (triggers a 402 challenge):You receive a
402 Payment Required response with the payment requirements: wallet address, chain ID, USDC amount, and a challengeId. An x402-compatible agent uses this information to pay on-chain, then replays the request with a PAYMENT-SIGNATURE header to receive the access grant.Set up refunds
The refund cron is optional but recommended. Without it, payments that fail during credential issuance remain in the PAID state permanently.
processRefunds function scans for PAID records that were never delivered and refunds them on-chain. Run it on a schedule using BullMQ, node-cron, or any job scheduler.gasWalletPrivateKey and optionally redis for distributed locking.Go to mainnet
When you are ready to accept real USDC payments, make three changes:No other code changes are needed. The SDK handles the different chain IDs, USDC contract addresses, and RPC endpoints automatically.
- Switch the network from
"testnet"to"mainnet"in yourSellerConfigandX402Adapter. - Use your production wallet — the address that receives real USDC on Base (chain ID 8453).
- Update the refund cron network to
"mainnet".
Full working example
Here is the complete seller in a single file:seller.ts

