Bazaar — Service Discovery
What is Bazaar?
Bazaar is the discovery layer for x402. It answers: "What can I buy here, and how much does it cost?" Agents browse a structured catalog of services, prices, and input/output schemas at /.well-known/x402.json.
How it works
Publish /.well-known/x402.json:
{
"x402Version": 2,
"name": "Your Service",
"description": "What your service does",
"network": "base",
"facilitator": "coinbase",
"payTo": "0xYourWallet",
"services": [
{
"method": "POST",
"path": "/api/generate",
"description": "Generate content",
"amount": "100000",
"discoverable": true,
"outputSchema": {
"input": {
"type": "http",
"method": "POST",
"bodyFields": {
"prompt": { "type": "string", "required": true }
}
},
"output": {
"type": "json",
"schema": { "result": { "type": "string" } }
}
}
}
]
}
Required fields
- x402Version — Spec version (use
2; the scanner also accepts1— see below) - name — Provider name
- network — Blockchain (e.g.,
base, or the CAIP formeip155:8453under v2) - facilitator — Settlement provider (e.g.,
coinbase) - payTo — Wallet address that receives payments
- services[] — Array of paid endpoints with
methodandpath
v1 vs v2
x402 v2 launched in December 2025 and is the current spec, but v1 servers are still common in the wild. AgentGrade accepts both. Every protocol-level difference:
- Header names: v1 used
X--prefixed headers (X-PAYMENTon the retry request,X-PAYMENT-RESPONSEon success). v2 drops theX-prefix (PAYMENT-SIGNATURE,PAYMENT-RESPONSE) — theX-convention was deprecated by RFC 6648 back in 2012. - Where the challenge lives: v1 put the payment challenge in the JSON body of the 402 response. v2 moves it into the
PAYMENT-REQUIREDresponse header (base64-encoded JSON), freeing the body for a human-readable paywall page. amountfield rename: v1'saccepts[]entries usedmaxAmountRequiredfor the price. v2 renames this toamount.resourceshape: v1 putresource(as a string URL),description, andmimeTypeinside eachaccepts[]entry. v2 hoists these into a top-levelresourceobject withurl,description, andmimeTypefields — declared once for the whole 402 response instead of duplicated per accepts entry.- Chain identification: v1 used free-form strings like
"base"for thenetworkfield. v2 standardizes on CAIP IDs likeeip155:8453, so the same field works for non-EVM chains and off-chain rails. - Dynamic
payTo: v1 declared one static recipient per service in the discovery catalog. v2 lets the server computepayToper request — useful for marketplaces routing payments to individual sellers. - Sessions: v1 required the full payment dance on every call. v2 adds wallet-controlled sessions (Sign In With X402) — sign once, the server issues a session, subsequent calls skip the handshake.
If you're building new, build v2. If you're auditing an existing v1 server, the scanner won't penalize you for being on v1 — but the v2 differences above are real efficiency wins.
How to return a 402
The discovery catalog above declares what you sell. The 402 response is what actually triggers payment when an agent calls a paid endpoint without one.
v2 form (current spec)
The payment challenge goes in the PAYMENT-REQUIRED response header as a base64-encoded JSON object. The response body is yours to use however you want — typically a human-readable paywall page. The retry request arrives with the signed payment in the PAYMENT-SIGNATURE request header (also base64-encoded JSON). On success, the server may set a PAYMENT-RESPONSE header with settlement details.
app.post('/api/generate', async (req, res) => {
if (!req.get('PAYMENT-SIGNATURE')) {
const challenge = {
x402Version: 2,
resource: {
url: 'https://yourdomain.com/api/generate',
description: 'Generate content from a prompt',
mimeType: 'application/json'
},
accepts: [{
scheme: 'exact',
network: 'base', // or CAIP form: 'eip155:8453'
amount: '100000', // smallest units; 6-decimal USDC → $0.10
asset: '0x833589fCD6EDb6E08f4c7C32D4f71b54bdA02913',
payTo: '0xYourWalletAddress',
maxTimeoutSeconds: 60
}],
extensions: { bazaar: { discoverable: true } }
};
res.status(402);
res.set('PAYMENT-REQUIRED', Buffer.from(JSON.stringify(challenge)).toString('base64'));
return res.send('<h1>$0.10 to generate content</h1>');
}
// Verify the signature with your facilitator, then run the work.
// The @coinbase/x402 SDK handles the verification call for you.
res.json({ result: '...' });
});
v1 form (legacy, body-based)
v1 servers put the challenge in the JSON body of the 402 response. The retry request used the X-PAYMENT header, and a successful settlement was returned in X-PAYMENT-RESPONSE. v1 also uses maxAmountRequired instead of v2's amount.
app.post('/api/generate', async (req, res) => {
if (!req.get('X-PAYMENT')) {
return res.status(402).json({
x402Version: 1,
error: 'Payment required to access this resource',
accepts: [{
scheme: 'exact',
network: 'base',
maxAmountRequired: '100000',
asset: '0x833589fCD6EDb6E08f4c7C32D4f71b54bdA02913',
payTo: '0xYourWalletAddress',
resource: 'https://yourdomain.com/api/generate',
maxTimeoutSeconds: 60
}]
});
}
// ...verify the X-PAYMENT signature, then serve the work
});
Heads up — AgentGrade's live-402 check is v2-only. Our scanner reads the PAYMENT-REQUIRED response header to confirm a working paywall. A v1 server that puts its challenge in the body will pass the discovery-catalog check (/.well-known/x402.json) but won't score on the live-402 check. If that matters to you, build v2.
Use the SDK
You almost certainly don't want to hand-roll signature verification — call the @coinbase/x402 middleware instead. It wraps both response shapes and the facilitator round-trip in one line of Express/Fastify/Hono setup.
extensions.bazaar in the live 402 header
When an agent calls a paid endpoint without payment, your server returns HTTP 402 with a base64-encoded Payment-Required header. That header's JSON payload should declare extensions.bazaar so agents know this endpoint is part of a discoverable catalog. The JSON payload before base64 encoding (v2 form):
{
"x402Version": 2,
"accepts": [
{
"scheme": "exact",
"network": "base",
"amount": "100000",
"asset": "0xUSDC...",
"payTo": "0xYourWallet",
"maxTimeoutSeconds": 60
}
],
"extensions": {
"bazaar": { "discoverable": true }
}
}
Without extensions.bazaar, agents that hit a 402 have no signal that the endpoint is also catalogued at /.well-known/x402.json — they may treat it as a one-off paid endpoint instead of part of a browseable service.
Spec maturity
Part of x402 v2. Bazaar discovery is defined within the x402 specification.
Learn more
- x402.org — x402 protocol spec (includes Bazaar)