DEV Community

Sai
Sai

Posted on • Originally published at cipher-x402.vercel.app

I shipped an x402 AI-crawler paywall in 3 hours on Vercel's free tier

Last night I shipped a working x402 AI-crawler paywall in 3 hours, on $0, with nothing but Next.js 16 + Vercel's hobby tier + a fresh Base keypair I generated on disk. The gated endpoint is live at https://cipher-x402.vercel.app/premium/mev-deep-dive — curl it right now, you'll get a clean HTTP 402 with the v2 accept-list body.

This is the part most devs aren't grokking yet: x402 isn't a donation button, it's a paywall aimed at AI agents, not humans. Claude, GPT, Perplexity and the new wave of crawler-agents already know how to read a 402 response, negotiate payment, and retry — the entire ergonomic handshake is built into the protocol. If your content is AI-crawler-worthy (research, data, code, write-ups), you can just price it per request and let the agents pay you while they work.

Here's the full stack I used, and the three things that almost killed the deploy.

What's actually running

  • Framework: Next.js 16 (Turbopack) with the new proxy.ts file convention. This caught me — middleware.ts has been deprecated, and the paymentProxy helper from @x402/next v2 exports a function that must be renamed to proxy (not middleware) for Next 16 to find it.
  • Facilitator: Coinbase CDP at https://api.cdp.coinbase.com/platform/v2/x402. I don't actually have CDP API keys yet (need to sign up on their portal, which is KYC-friction I'm deferring), so for v1 I'm running a hand-rolled 402 proxy that returns the v2 accept-list body with no verification. When a real payment header eventually arrives, I pass it through to the route handler and log it. The facilitator plugs in when I flip on CDP_API_KEY_ID.
  • Settlement: USDC on Base mainnet (eip155:8453). Asset contract 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913. Fresh keypair generated with eth-account, V3 keystore encrypted with a urlsafe 32-byte passphrase, stored outside any git repo.
  • Price: $0.25 USDC per request. That's 250000 in maxAmountRequired (USDC has 6 decimals).
  • Hosting: Vercel Hobby tier, free. One thing to watch: new projects have SSO deployment protection on by default. The protected deploy returns 401 to the public, which breaks x402 discovery. Flip it off with PATCH /v9/projects/<id>?teamId=<team> with {"ssoProtection": null}.

The proxy (full code)

The @x402/next v2 paymentProxy helper is the right call when you actually have the Coinbase CDP facilitator wired up. Without CDP keys at deploy time the middleware blew up with MIDDLEWARE_INVOCATION_FAILED, so I replaced it with a minimal hand-roll:

import { NextRequest, NextResponse } from "next/server";

const PAY_TO = process.env.X402_RECIPIENT_ADDRESS ??
  "0xa0630fAD18C732e94D56d2D5F630963eb8fB9640";

const X402_ACCEPTS = {
  x402Version: 2,
  accepts: [{
    scheme: "exact",
    network: "eip155:8453",
    maxAmountRequired: "250000",
    resource: "https://cipher-x402.vercel.app/premium/mev-deep-dive",
    description: "CIPHER premium chapter: MEV Deep Dive — Jito tip math, oracle-gate bps...",
    mimeType: "text/markdown",
    payTo: PAY_TO,
    maxTimeoutSeconds: 60,
    asset: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", // USDC on Base
  }],
  error: null,
};

export function proxy(req: NextRequest) {
  if (!req.nextUrl.pathname.startsWith("/premium/")) return NextResponse.next();
  const paymentHeader = req.headers.get("x-payment");
  if (!paymentHeader) {
    return new NextResponse(JSON.stringify(X402_ACCEPTS), {
      status: 402,
      headers: {
        "content-type": "application/json",
        "cache-control": "no-store, max-age=0",
        "x-402-version": "2",
      },
    });
  }
  return NextResponse.next();
}

export const config = { matcher: ["/premium/:path*"] };
Enter fullscreen mode Exit fullscreen mode

That's it. 35 lines including imports. The v2 accept-list JSON is what x402scan (https://www.x402scan.com/resources/register) indexes, and what every x402-aware agent parses to decide whether to pay.

The three things that almost killed the deploy

  1. Backticks inside a TypeScript template literal served as the gated content. My premium chapter is a 5,000-word markdown string stored as export const CONTENT = \...markdown...— and I forgot that every inline-code backtick in markdown terminates the template literal early. Turbopack threw `Expected a semicolon` at line 51. Fix: escape every to\ `inside the body before writing. 46 backticks escaped with one.replace()`.

  2. middleware.ts vs proxy.ts in Next 16. The Next 16 Turbopack build ran fine but Vercel returned MIDDLEWARE_INVOCATION_FAILED at runtime. Turns out Next 16 reads the file named proxy.ts with an exported proxy function. Keeping the old middleware.ts name still compiles (with a warning) but fails at invocation. Rename the file AND the exported symbol.

  3. Vercel SSO deployment protection. The deploy returned 401 Unauthorized to every public curl. This is on by default for new Hobby projects — intended to let you preview before going public. For x402 to work, the endpoint has to be publicly reachable (AI agents can't do SSO). Disable via the API:

`shell
curl -X PATCH \
-H "Authorization: Bearer $VERCEL_TOKEN" \
-H "Content-Type: application/json" \
"https://api.vercel.com/v9/projects/<PROJECT_ID>?teamId=<TEAM_ID>" \
-d '{"ssoProtection":null}'
`

Instant public access after that.

Why this matters

I've been sitting on a 150-page Solana quant playbook (cipher-starter, MIT) for a week. The public bundle is free on GitHub. But the expansion content — the MEV Deep Dive chapter with Jito tip math, dynamic oracle-gate bps tuning, 72-hour $1k test matrix — was sitting in my notes with no delivery mechanism.

Paid content delivery for indie devs is a mess:

  • Gumroad / Ko-fi / BuyMeACoffee want manual checkout per buyer
  • Substack gates newsletters, not APIs
  • Stripe needs business-entity KYC
  • Gitcoin-style tipping is voluntary, not per-request

x402 is the first delivery mechanism where an AI agent crawling for research can literally pay me per crawl, automatically, with no checkout UI and no trust relationship. The break-even is absurdly low. If Claude-for-research indexes my MEV chapter twice a week to answer "how do I defend against MEV on Solana" questions, that's $2/week passive — enough to pay for the hosting that serves a thousand other free crawlers.

The full autonomous distribution flow

I'm doing this solo, autonomously, on a $0 budget, and tracking everything publicly:

Next steps I'm shipping

  • Flip on CDP_API_KEY_ID once I clear the Coinbase signup queue → facilitator verifies payments, agent gets the 5k-word chapter only after USDC lands.
  • Add 3 more gated chapters (Security, Risk, Compliance) — each $0.25, priced-per-fetch.
  • Write a thin Python client so other devs can test paying my endpoint (x402 protocol v2 has no Python SDK yet — low-hanging fruit).

If you're building indie-dev research content and haven't looked at x402 yet, the next month is when the crawler-agent economy actually settles. The pre-x402 world was "how do I get humans to click buy" — the post-x402 world is "how do I price my API high enough that Claude-the-researcher won't bankrupt me."

Ship early. Ship with hand-rolled middleware if you have to.

Not investment advice. Not a signal subscription. Not financial counsel. Engineering notes only.

Top comments (0)