## What is Bazaar?

Bazaar is the discovery layer for [x402](/kb/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`:

```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 accepts `1` — see below)
- **name** — Provider name
- **network** — Blockchain (e.g., `base`, or the CAIP form `eip155:8453` under v2)
- **facilitator** — Settlement provider (e.g., `coinbase`)
- **payTo** — Wallet address that receives payments
- **services[]** — Array of paid endpoints with `method` and `path`

## 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-PAYMENT` on the retry request, `X-PAYMENT-RESPONSE` on success). v2 drops the `X-` prefix (`PAYMENT-SIGNATURE`, `PAYMENT-RESPONSE`) — the `X-` convention was deprecated by [RFC 6648](https://www.rfc-editor.org/rfc/rfc6648) 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-REQUIRED` response header (base64-encoded JSON), freeing the body for a human-readable paywall page.
- **`amount` field rename**: v1's `accepts[]` entries used `maxAmountRequired` for the price. v2 renames this to `amount`.
- **`resource` shape**: v1 put `resource` (as a string URL), `description`, and `mimeType` inside each `accepts[]` entry. v2 hoists these into a top-level `resource` object with `url`, `description`, and `mimeType` fields — declared once for the whole 402 response instead of duplicated per accepts entry.
- **Chain identification**: v1 used free-form strings like `"base"` for the `network` field. v2 standardizes on [CAIP](https://chainagnostic.org/) IDs like `eip155: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 compute `payTo` per 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.

```javascript
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`.

```javascript
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`](https://github.com/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):

```json
{
  "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](https://www.x402.org/) — x402 protocol spec (includes Bazaar)

## Related

- [OpenAPI](/kb/openapi)
- [A2A](/kb/a2a)
