# How to add x402 payments to your API: what it actually took · Stacktree

Source: https://stacktr.ee/blog/how-to-add-x402

[Skip to content](#main) PFrom Product Hunt? Your first pages are free, no card. Code `PRODUCTHUNT` takes 50% off your first month of Studio.[Start free &rarr;](https://app.stacktr.ee/?join=1&from=producthunt)

&times; [Stacktree](/)[Developers](/developers)[Agents](/agents)[Docs](/docs)[Use cases](/use-cases)[Pricing](/pricing)[Blog](/blog)[Dashboard](https://app.stacktr.ee)[Sign in →](https://app.stacktr.ee)      By [ Steve Smith ](/about) · Founder, Stacktree  ·  Last updated July 9, 2026          blog · build guide

#  Adding x402 to a real API, step by step.

   Our publish endpoint has accepted autonomous stablecoin payments since June, and the numbers are public. This is the other half: what we actually built to get there, in the order we would build it again, including the parts that failed on mainnet first.

  [ Get started free ](https://app.stacktr.ee/?join=1&from=seo_blog_how_to_add_x402)   install npx stacktree-install
Copy
     No card · 3 pages free · about a minute

##  What does it take to accept x402 payments?

Five things: an endpoint that answers unpaid requests with HTTP 402 and machine-readable payment requirements; a facilitator to verify and settle the signed USDC payment; an idempotent grant so retries cannot double-charge or double-mint; pricing that cannot bleed you (time-box anything with ongoing cost); and a discovery listing so agents can find you. We run all five in production on a Cloudflare Worker with no SDK. Days of work, plus a week of gotchas.

## On this page

  -   01  [ Step 1: price the thing honestly ](#pricing)
-   02  [ Step 2: return a real 402, both versions ](#the-402)
-   03  [ Step 3: verify and settle via a facilitator ](#settle)
-   04  [ Step 4: mint idempotently ](#idempotency)
-   05  [ Step 5: get discovered ](#discovery)
-   06  [ We implemented this twice, on purpose ](#twice)
-   07  [ The gotchas that cost us live transactions ](#gotchas)
-   08  [ The human fallback ](#human-fallback)

## Step 1: price the thing honestly

 Before any code: decide what a one-time payment buys, because an anonymous $1 payment that grants a perpetual obligation is a slow leak. Our model: $1 provisions a persistent API key at free-tier limits; individual unlocks are purchasable on top; and anything with ongoing cost to us (custom domains, elevated limits) is time-boxed at $5 per 30 days, renewed via a fresh 402. Bounded things are one-time, unbounded things expire. This rule shaped everything downstream.

## Step 2: return a real 402, both versions

 The protocol is HTTP with one twist: an unpaid request gets status 402 plus payment requirements (asset, network, amount, receiver). The twist inside the twist is versioning: x402 v2 carries requirements in a `PAYMENT-REQUIRED` header, v1 carries them in the body, and real wallets are split across both. We shipped v2-only first and watched a funded Coinbase wallet fail to pay us because it only spoke v1. Serve both. The client retries with a signed payment header; that is the whole dance. Our 402 requirements look like this (v2 shape; see the [x402 docs](https://docs.x402.org) for the full schema):

```
`{
  "x402Version": 2,
  "accepts": [{
    "scheme": "exact",
    "network": "eip155:8453",            // Base mainnet
    "asset": "0x8335...2913",            // USDC contract
    "maxAmountRequired": "1000000",      // $1.00, 6 decimals
    "payTo": "0xcc98...4333",
    "resource": "https://api.stacktr.ee/provision",
    "extra": { "name": "USD Coin", "version": "2" }  // EIP-712 domain, see gotchas
  }]
}`
```

## Step 3: verify and settle via a facilitator

 You do not need chain infrastructure. A facilitator exposes verify and settle endpoints: you POST the payment header, it checks the EIP-3009 signature, executes the on-chain USDC transfer, and pays the gas. We use Coinbase's CDP facilitator on Base mainnet, called with plain `fetch`; the auth is a per-request Ed25519 JWT we build with `crypto.subtle`, no SDK. Keep the facilitator URL configurable: it is the one dependency worth being able to swap. The two calls ([CDP facilitator docs](https://docs.cdp.coinbase.com/x402/docs/welcome)):

```
`POST {facilitator}/verify  { paymentPayload, paymentRequirements }  // signature + funds check
POST {facilitator}/settle  { paymentPayload, paymentRequirements }  // executes transferWithAuthorization`
```

 The payment itself is an [EIP-3009](https://eips.ethereum.org/EIPS/eip-3009) transferWithAuthorization signed by the payer, so the facilitator can submit it and pay gas without ever holding funds.

## Step 4: mint idempotently

 Settlements retry, headers replay, networks flake. Every grant we issue is recorded with a unique constraint on (protocol, settlement reference), so a replayed settlement returns the original grant instead of minting twice:

```
`CREATE TABLE payment_grants (
  id            TEXT PRIMARY KEY,
  protocol      TEXT NOT NULL,        -- 'x402' | 'mpp' | 'stripe' | 'credit'
  external_ref  TEXT NOT NULL,        -- settlement tx / session id
  payer         TEXT, amount TEXT, currency TEXT,
  api_key_id    TEXT NOT NULL,
  UNIQUE (protocol, external_ref)     -- the whole idempotency story
);`
```

  This one table is also your ledger, and it will disagree with the block explorer: [our month-one numbers](/blog/x402-in-production) show why you should log every 402, verify, settle, and mint so you can reconcile the two.

## Step 5: get discovered

 Nobody types your API into a wallet. Agents find paid endpoints through scanners, so we self-host the discovery feed paths x402 scanners probe and appear on x402scan. That listing alone produced a buyer every two to three days, all month, with zero announcement. Config gotchas: your OpenAPI spec needs `x-payment-info` on paid routes (an empty `security` array means "free" to the scanner), and each route needs an input schema or the listing is rejected.

## We implemented this twice, on purpose

 Everything above describes the hand-rolled integration on `api.stacktr.ee`: our worker owns the 402, the facilitator calls, and the grants table. But there is a second, much faster mode, and we run that too: `agents.stacktr.ee` fronts the same product through [agentcash](https://agentcash.dev)'s router, which handles the payment negotiation for us and is what carries the MPP-on-Tempo rail. Build-it-yourself buys you control, your own ledger, and no dependency; the router buys you extra rails in an afternoon. If you just want to charge agents for an API, start with a router and graduate to owning the 402 when the volume justifies it.

## The gotchas that cost us live transactions

  - Base mainnet USDC signs as "USD Coin". The EIP-712 domain name is USD Coin (version 2); testnet uses USDC. Wrong name means an on-chain revert at settlement. Two live attempts died here; make the asset name configurable.
 - v1-only wallets are common. Including, at the time, Coinbase's own tooling. Dual-serve or lose payers silently.
 - Some agent payment clients cannot pay v2 endpoints at all. Check the client ecosystem's issue trackers before assuming a failed payment is your bug.
 - Never advertise rails that are not live. Our MPP, ACP, and AP2 code paths exist but gate off unless configured, and the scaffolded ones fail closed. A discovery badge that lies gets found out by an agent with money.

## The human fallback

 The dominant real-world case is still a human driving an agent in a terminal. So the 402 also advertises a fallback: the agent requests a pay session, prints a QR code, the human scans and pays by card via ordinary Stripe Checkout, and the agent polls to collect its key. Overpay and the surplus becomes a credit balance the agent draws down silently on later purchases, no 402 round-trip. Same grants table, different rail. Building this doubled the audience for the whole system.

     See it in one call, no account

```
`curl -X POST https://api.stacktr.ee/sites -F file=@index.html
# &rarr; a private URL, live for 24h. Claim it free to keep it.`
```

If your agents emit HTML, they need a durable publish target: publish once, then
`update_site`
on every revision keeps the same URL forever. MCP, REST, and skills all speak it:
        the map is at [agent.txt](https://stacktr.ee/agent.txt).

[
Get an API key free
](https://app.stacktr.ee/?join=1&from=bridge_blog_how_to_add_x402)
FAQ

## Frequent questions

     How long does it take to add x402 to an API? +  The core loop (402 with requirements, verify and settle via a facilitator, grant on settlement) was days of work, not weeks, on a Cloudflare Worker with no SDK, just fetch. The long tail was gotchas: EIP-712 domain names, protocol version splits, and discovery-listing schemas. Budget a week to be honest.   Do you need your own crypto infrastructure? +  No. A facilitator (we use Coinbase's CDP facilitator; the endpoint is configurable) does on-chain verification and settlement, and even pays the gas. You need a wallet address to receive USDC and an off-ramp when it matters. What you must own is the application side: idempotent grants and honest SKUs.   What should you charge over x402? +  Small, flat, and honest to your costs. We charge $1 to provision a persistent API key and $1 to $5 for feature unlocks. The rule that matters: anything with ONGOING cost (a custom domain, elevated limits) is time-boxed and renews via a fresh 402; one-time payments only buy bounded things. A $1 payment that buys a perpetual obligation is a slow leak.   How do agents find an x402 endpoint? +  Discovery listings. We self-host the x402 discovery feed paths that scanners probe, and the listing on [x402scan](https://www.x402scan.com) has generated a steady drip of real buyers (roughly one every two to three days) with zero announcement. Listing config gotcha: paid routes need x-payment-info in your OpenAPI spec; marking them with an empty security array means "free" to the scanner.   Does x402 replace Stripe? +  Not for us. Every subscription we have still arrives through ordinary Stripe Checkout; x402 serves a different buyer (agents and their operators, no signup, pay-per-action). We also accept MPP on Tempo over the same 402 flow (set up with [agentcash](https://agentcash.dev)'s tooling, which is what unlocked the MPP rails for a UK entity), and built a QR scan-to-pay card fallback for the common case where a human is driving the agent. Treat x402 as a new front door, not a checkout replacement.
Keep reading

## Related guides

   -  [ The numbers What 30 days of real traffic to this implementation looked like. ](/blog/x402-in-production)
-  [ x402 and MPP, explained The protocol-level explainer behind this guide. ](/blog/x402-mpp-explained)
-  [ The live endpoint Point an agent at it and watch the flow in this post happen. ](/x402)
-  [ The launch story The first settlement, documented end to end. ](/blog/agent-paid-to-provision-itself)

References

## Sources and further reading

   -  [ x402 protocol docs ↗ The spec: requirements schema, payment header format, v1 vs v2. ](https://docs.x402.org)
-  [ CDP facilitator docs ↗ Verify/settle endpoints we call in step 3. ](https://docs.cdp.coinbase.com/x402/docs/welcome)
-  [ EIP-3009 ↗ transferWithAuthorization, the signature scheme underneath every x402 payment. ](https://eips.ethereum.org/EIPS/eip-3009)
-  [ coinbase/x402 on GitHub ↗ Reference implementation; where client version gaps surface first. ](https://github.com/coinbase/x402)
-  [ x402scan ↗ The scanner that generates most of our discovery; your listing lives here. ](https://www.x402scan.com)

##  See the flow live.

 The endpoint this guide describes is in production: a 402, a $1 settlement, a persistent key. No signup on either side.

 [ Sign up free → ](https://app.stacktr.ee/?join=1&from=cta_blog_how_to_add_x402)   install npx stacktree-install
Copy
       Private hosting for the HTML your agents make.

[](https://betalist.com/startups/stacktree?utm_campaign=badge-stacktree&utm_medium=badge&utm_source=badge-featured)[Featured on](https://devhunt.org/tool/stacktree)[](https://buildlist.io)[Get started free](https://app.stacktr.ee/?join=1)

## Product

- [Page video](/page-video)
- [Ask this page](/ask-this-page)
- [Client spaces](/client-spaces)
- [Templates](/templates)
- [Example deliverables](/examples)
- [Custom domains](/custom-domains)
- [Client feedback](/client-feedback-loop)
- [One-time links](/one-time-view-links)
- [See how pages get read](/page-engagement)
- [Made with Stacktree](/made-with)
- [Security](/security)
- [Watch the demo](/demo)

## Agents

- [All integrations](/agents)
- [Claude Code](/claude-code)
- [OpenAI Codex](/codex)
- [Cursor](/cursor)
- [Claude.ai connector](/claude-ai-connector)
- [Claude Cowork](/what-is-claude-cowork)
- [Meta Muse](/muse)
- [MCP server](/mcp-publish-html)
- [Deploy from Claude Code](/deploy-html-from-claude-code)
- [Skills](/skills)
- [Slack app](/slack)
- [n8n node](/n8n)
- [Agent payments (x402)](/x402)

## Alternatives

- [All comparisons](/alternatives)
- [Head-to-head comparisons](/compare)
- [Tiiny Host](/tiiny-host-alternative)
- [GitHub Pages (private)](/github-pages-private-alternative)
- [Vercel](/vercel-alternative-for-agents)
- [ngrok](/ngrok-alternative-for-html)
- [Display.dev](/display-dev-alternative)
- [Static.app](/static-app-alternative)
- [OpenAI Codex Sites](/openai-codex-sites-alternative)
- [here.now](/here-now-alternative)
- [Shippage](/shippage-ai-alternative)
- [Best private hosting](/best-private-html-hosting)

## Use cases

- [All use cases](/use-cases)
- [Share with clients](/share-with-clients)
- [Send a file to a client](/share-html-file-with-client)
- [Share Claude artifacts](/share-claude-artifacts)
- [Share Jupyter notebooks](/share-jupyter-notebook-html)
- [Host Storybook privately](/host-storybook-privately)
- [Architecture diagrams](/share-architecture-diagrams)
- [AI-generated reports](/host-ai-reports)
- [Internal HTML tools](/internal-tool-hosting)
- [Private HTML hosting](/private-html-hosting)
- [Vibe-coded page hosting](/vibe-coding-hosting)
- [Leave a website builder](/website-builder-migration)

## Learn

- [Blog](/blog)
- [Glossary](/glossary)
- [FAQ](/faq)
- [Where AI output should live](/where-ai-output-lives)
- [Agent-loop hosting](/agent-loop-hosting)
- [Why agents need a publish primitive](/blog/why-agents-need-a-publish-primitive)
- [MCP servers explained](/blog/mcp-servers-explained-for-developers)
- [What changed in the 2026-07 MCP spec](/blog/mcp-2026-spec-changes)
- [Sites in Codex explained](/blog/sites-in-codex-explained)
- [Private-by-default hosting](/blog/private-by-default-html-hosting)
- [An agent paid us $1 (x402)](/blog/agent-paid-to-provision-itself)
- [When a loop hits a paywall](/blog/loop-engineering-paywall)
- [Pricing](/pricing)
- [Self-host (new)](/self-host)
- [Changelog](/changelog)
- [Docs](https://stacktr.ee/docs)
- [About](/about)
© 2026 Stacktree · stacktr.ee

[Privacy](/privacy)[Terms](/terms)[Security](/security)[Dashboard](https://app.stacktr.ee)[npm](https://www.npmjs.com/package/stacktree-mcp)[Sitemap](/sitemap.xml)[llms.txt](/llms.txt)[API spec](/openapi.json)

---
Full markdown summary of the Stacktree marketing surface: https://stacktr.ee/llms-full.txt
