Building Self-Describing Payment APIs with x402 Discovery

๐Ÿ”— The Problem

AI agents need to discover what tools and APIs are available before they can call them. OpenAI's function calling and MCP solved this for free tools โ€” but what happens when the tool costs money?

The x402 protocol adds pay-per-use to HTTP via 402 Payment Required challenges. But the protocol doesn't define how clients discover what endpoints exist or what they do before paying.

We built a solution using three discovery layers, each targeting a different consumer.


๐Ÿช Layer 1: x402 Bazaar โ€” Official Protocol Discovery

x402 ships with a built-in discovery extension called Bazaar (@x402/extensions/bazaar). It decorates route configs with self-description metadata that becomes available in the 402 challenge response headers.

x402 Bazaar docs ยท Source: @x402/extensions

The key API is declareDiscoveryExtension โ€” it accepts three patterns:

How it flows:

Route config โ†’ extensions.bazaar = { info: {...}, schema: {...} }
                 โ†“
 402 challenge response includes extension metadata in headers
                 โ†“
 Client reads metadata to understand endpoint capabilities

We attached Bazaar extensions to all 3 endpoints in our API โ€” stealth_dom, airgap_scrub, rag_shrink โ€” with full input schemas and example outputs.


๐Ÿค– Layer 2: MCP-Style /api/v1/tools โ€” Machine Discovery

AI agents (Claude, OpenAI, etc.) expect OpenAI-compatible function definitions. We added a /api/v1/tools endpoint that returns exactly that:

{
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "stealth_dom",
        "description": "Fetch any URL from Cloudflare edge...",
        "parameters": {
          "type": "object",
          "properties": {
            "url": { "type": "string", "description": "URL to fetch" }
          },
          "required": ["url"]
        }
      }
    }
  ],
  "metadata": {
    "server": "x402-api",
    "base_url": "https://x402-gateway-prod.look-a3a.workers.dev",
    "payment": { "network": "Base", "currency": "USDC", "price": "$0.01" }
  }
}

This matches the format the x402 MCP integration guide recommends. AI agents fetch this once, understand every available tool, and know the payment requirements upfront.


๐Ÿ‘ค Layer 3: Enhanced /health โ€” Human Discovery

Developers need something readable. We enhanced the /health endpoint with:

No Swagger UI needed โ€” just a structured JSON response a developer can curl and understand.


๐ŸŒ How The Rest of the Industry Does It

Three coexisting patterns emerged from researching the x402 ecosystem:

Bazaar (official) โ€” declareDiscoveryExtension built into @x402/extensions. Used by GoPlausible and growing. Protocol-aware clients read extension metadata from challenge headers.

MCP tools (OpenAI format) โ€” /api/v1/tools or similar endpoints returning function definitions. Highest adoption among AI agent integrations. Recommended by x402.

OpenAPI (Swagger) โ€” Standard OpenAPI 3.0/3.1 specs with Swagger UI. Used by Zuplo for human developer docs. Niche but universally understood.

No universal standard exists yet. Our approach: implement Bazaar + MCP tools now, add OpenAPI later if needed.


๐Ÿ› ๏ธ What We Built

API: x402 API Gateway โ€” 3 endpoints on Cloudflare Workers with pay-per-use via x402.

Source: /opt/data/projects/x402-api/src/index.ts โ€” Hono + @x402/hono middleware + Bazaar extensions.

Tests: 6/6 passing โ€” stealth_dom (~168ms DOM fetch), airgap_scrub (4 PII types redacted), rag_shrink (paragraph extraction), /health, /api/v1/tools, and 402 challenge flow.

MCP integration: Memory-melon proxies all 3 tools via Go handlers in internal/mcp/endpoints.go.


โš™๏ธ Implementation Details

Resilient Payment Middleware

The default x402 facilitator (https://x402.org/facilitator) is unreachable. Our middleware wraps facilitator calls in try/catch so failed payments return proper 402 challenges instead of 500 crashes:

try {
  const payment = await createX402Headers({...});
  res.setHeader('X-402-Payment-Required', payment.challenge);
} catch {
  // Return 402 with inline challenge on facilitator failure
  res.status(402).json({ error: 'Payment required', ... });
}

Dev Mode

X-Dev-Bypass: true skips payment verification entirely โ€” useful for local development and testing. Production deploys remove the header.

Per-Endpoint Pricing

wrangler.toml has separate price vars (PRICE_STEALTH_DOM=0.05, etc.) ready to wire into the discovery layer so pricing is self-describing.


๐Ÿ”— Sources


x402paymentsmcpapi-designcloudflare-workers