Skip to main content
Glama
README.md
# x402-starter

**Paywall your Cloudflare Worker for AI agents in 5 minutes.**

Drop-in [x402](https://www.x402.org) starter. Agents pay per call in USDC — no signup, no API keys, no human in the loop. Comes with [Coinbase x402 Bazaar](https://www.x402.org) discoverability and an MCP manifest pre-wired so Coinbase-connected agents can find your API automatically.

## What you get out of the box

- A Cloudflare Worker that returns **HTTP 402 Payment Required** for paid routes until the caller settles a USDC micropayment
- Bazaar-discoverable routes (the non-obvious bit — without `discoverable: true` + schemas, even settled payments never list)
- A `/mcp` manifest so MCP clients and Coinbase AgentKit auto-discover your tools
- One example paid endpoint (`GET /api/echo`) you replace with your real API
- HTTPS-forced redirects, JSON-or-HTML root discovery, health check

## 5-minute quick start

```bash
git clone <this repo> my-paid-api
cd my-paid-api
npm install
npm run dev          # paywall OFF by default — iterate locally
```

Hit `http://localhost:8787/api/echo?msg=hi` — you'll get a 200 with the echo. That's your free dev loop.

### Flip the paywall on (testnet)

1. Edit `wrangler.jsonc`:
   ```jsonc
   "X402_ENABLED": "true",
   "X402_NETWORK": "base-sepolia",
   "PAY_TO": "0xYOUR_TESTNET_WALLET",
   "PRICE_PER_CALL": "$0.01"
   ```
2. `npm run deploy`
3. Hit `/api/echo` — you'll get **HTTP 402** with the payment challenge.

`base-sepolia` uses the default public facilitator. **No keys needed.** This is the right setup for development and demos.

### Flip to mainnet (real USDC)

1. Get a Coinbase CDP Secret API key at [portal.cdp.coinbase.com](https://portal.cdp.coinbase.com).
2. Set the two secrets:
   ```bash
   npx wrangler secret put CDP_API_KEY_ID      # paste the key id when prompted
   npx wrangler secret put CDP_API_KEY_SECRET  # paste the secret when prompted
   ```
3. Set `X402_NETWORK` to `"base"` in `wrangler.jsonc`, set `PAY_TO` to your real Base wallet, and redeploy.

Without the CDP keys on mainnet, the 402 challenge still issues but **payments cannot settle**. Set both secrets before going live.

## Adding your own paid endpoints

Two places to edit `src/index.ts`:

1. **The handler** — add a route under `/api/*`:
   ```ts
   app.get("/api/wash", async (c) => {
     // your logic
     return c.json({ ... });
   });
   ```

2. **The paywall config** — add the same path to `routes` in `applyPaywall()` so it gets paywalled AND Bazaar-discoverable:
   ```ts
   "/api/wash": {
     price: env.PRICE_PER_CALL,
     network: env.X402_NETWORK,
     config: {
       discoverable: true,
       description: "Wash/sybil screen for any Base address",
       inputSchema: { queryParams: { address: { type: "string" } } },
       outputSchema: { type: "object", properties: { sybil_score: { type: "number" } } },
     },
   },
   ```

3. **The MCP manifest** — add a `tools[]` entry with the same `name`/`endpoint`/`price_usd` so MCP clients see it.

That's it. The middleware path matcher (`/api/*`) catches everything under `/api`, so only step 1 is structurally required — but skipping steps 2 and 3 means agents won't *find* your endpoint, which is most of the value.

## Going beyond echo

This template is intentionally minimal — one example endpoint, no business logic. The patterns it shows (Bazaar config, MCP manifest, free landing + JSON discovery, HTTPS forcing, lazy app build) are the load-bearing parts. Wire in your D1 / KV / Workers AI / Durable Objects as needed.

## Pricing

x402-hono accepts dollar-formatted strings: `"$0.01"`, `"$0.05"`, `"$1.00"`. Internally x402 settles in micro-USDC (six decimals), so `"$0.01"` becomes `10000` units. Different routes can charge different prices — set them per-route in the `routes` config.

## License

MIT. Use it, fork it, ship it.