The x402 Seller Stack: How a Route Becomes a Paid Endpoint
Two days ago we walked the x402 buyer stack — the client that sees a 402, signs, and retries. This is the mirror image: the seller stack, the code that decides a route costs money, speaks the 402 handshake, and only does the work once the payment verifies. It is four middleware packages around one core abstraction, and the design choices in that core decide who eats the risk when settlement fails.
The material comes from the same v2.19.0 release of the TypeScript SDKs (published July 17, 2026) that we used for the buyer-stack deep dive: the x402-foundation/x402 monorepo (Apache 2.0), the official seller quickstart, and the package READMEs and source under typescript/packages/http. Everything below is what ships today, not roadmap.
Three layers between your handler and the chain
The seller stack is deliberately boring at the edges and interesting in the middle. At the edge sits a thin framework adapter — @x402/express, @x402/hono, @x402/next or @x402/fastify — that knows how to read headers and paths for its framework. Each adapter implements the same HTTPAdapter interface from @x402/core: getHeader, getMethod, getPath, getAcceptHeader, getUserAgent. That is the entire framework-specific surface.
Below the adapter sits x402HTTPResourceServer, which owns the route table and the HTTP grammar of the protocol — the PAYMENT-REQUIRED and PAYMENT-SIGNATURE headers we mapped in the facilitator post. And below that sits x402ResourceServer, the transport-agnostic core: it holds the registered schemes per network and the client that talks to the facilitator's /verify and /settle endpoints. The seller never touches a chain directly. Wiring it up is short:
import { paymentMiddleware, x402ResourceServer } from "@x402/express";
import { ExactEvmScheme } from "@x402/evm/exact/server";
import { HTTPFacilitatorClient } from "@x402/core/server";
const facilitator = new HTTPFacilitatorClient({ url: "https://x402.org/facilitator" });
const server = new x402ResourceServer(facilitator)
.register("eip155:84532", new ExactEvmScheme());
app.use(paymentMiddleware(routes, server));
One detail matters more than it looks: the middleware's fifth parameter, syncFacilitatorOnStart, defaults to true. On startup the resource server calls the facilitator's GET /supported and learns which scheme–network pairs it can actually settle. Your route table is a claim; the facilitator sync is the check. A route priced on a network your facilitator cannot settle is dead inventory, and the SDK surfaces that at boot instead of at the first failed settlement.
The route table is the price list
Everything the seller charges for lives in one declarative object. Keys are method-plus-pattern, wildcards included; values say what payment unlocks the route:
const routes = {
"GET /api/premium/*": {
accepts: [
{ scheme: "exact", price: "$0.05", network: "eip155:8453", payTo: evmAddress },
{ scheme: "exact", price: "$0.05",
network: "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp", payTo: svmAddress },
],
description: "Premium API access",
mimeType: "application/json",
},
};
accepts takes one entry or an array. An array means the same route is payable on several rails at once — USDC on Base or on Solana, at the seller's choice of price on each — and the buyer's client picks whichever matches its registered schemes. This is the server-side counterpart of the buyer's CAIP-2 pattern registry: the seller publishes options, the buyer intersects them with its wallets. payTo lives inside each entry, so different routes — or different rails on the same route — can pay different addresses. maxTimeoutSeconds bounds how long the offer stands. Prices take the "$0.10" dollar form or raw atomic units.
The scheme field accepts the full trilogy we covered in previous posts: exact for fixed prices, upto for metered authorization on EVM, and batch-settlement for high-frequency accumulation. Same route table, three settlement semantics.
The request lifecycle, and who carries the risk
Per the @x402/express README, the middleware does six things in order: match the route, check for a payment header, return a 402 with PAYMENT-REQUIRED if it is missing or invalid, verify the payment via the facilitator if present, let your handler run, and settle after a successful response.
Read that last clause again. Verification happens before your handler; settlement happens after it. The ordering is correct — you do not want to charge for a request your handler then 500s on — but it moves risk onto the seller. Between /verify and /settle the seller has done the work while holding only a signed authorization, not money. If settlement then fails — facilitator outage, authorization expired mid-request, upstream chain congestion — the compute is spent and the payment is not. We flagged the mirror-image risk in the facilitator post (a false success on settle); this is the honest version of the same window. For sub-cent routes the exposure is noise. For a route priced at dollars per call, it is a reconciliation line item you should be logging — which is exactly what the hooks are for.
Hooks: the audit surface
The resource server exposes four lifecycle hooks — onBeforeVerify, onAfterVerify, onBeforeSettle, onAfterSettle — the seller-side twins of the client hooks we covered on the buyer side. They are where policy lives without forking the middleware: reject payers on a denylist before wasting a facilitator round-trip, write the verify result to your audit log, and, critically, record every settle outcome with its transaction hash so failed settlements become recoverable receivables instead of silent losses.
The facilitator client itself takes auth: HTTPFacilitatorClient accepts a createAuthHeaders callback that returns separate header sets for verify and settle. That split is deliberate. Verify is a read-like pre-flight you might delegate broadly; settle moves money and can carry tighter credentials. Commercial facilitators — CDP's endpoint at api.cdp.coinbase.com/platform/v2/x402, PayAI, the rest of the directory we surveyed — authenticate exactly here. The quickstart is blunt about the other footgun: x402.org/facilitator is testnet-only (Base Sepolia, Solana devnet). Do not point mainnet routes at it.
Metered billing is one function call
The upto scheme's server side is almost anticlimactic. The route advertises a maximum; the buyer signs an authorization for that cap; your handler measures actual usage and, before responding, writes the real amount:
import { setSettlementOverrides } from "@x402/express";
app.post("/api/generate", async (req, res) => {
const out = await generate(req.body);
// charge for what was actually consumed, not the cap
setSettlementOverrides(res, { amount: String(out.tokensUsed * PRICE_PER_TOKEN) });
res.json(out);
});
The override accepts three formats: raw atomic units ("1000"), a percentage of the authorized cap ("50%"), or a dollar amount ("$0.05", only when the route itself was dollar-priced). The middleware picks the override up at settle time and passes the actual amount to the facilitator — the settle(permit, actualAmount) flow we traced through Permit2 in the upto deep dive, now visible as a one-line seller API. The same function is exported by the Fastify middleware and mirrored in the Go SDK, so the pattern is not an Express quirk.
Humans hit the same 402
A protected endpoint will eventually be opened in a browser, and a browser cannot sign an EIP-3009 authorization by itself. The middleware handles this with content negotiation: agents get the machine-readable 402, browsers get a paywall. There are three tiers. With the optional @x402/paywall package installed, the seller gets a full payment UI — EVM wallets (MetaMask, Coinbase Wallet), Solana wallets (Phantom, Solflare), USDC balance checks, chain switching, and onramp integration on mainnet. Without it, the middleware falls back to a basic HTML page with payment instructions. And a PaywallProvider interface lets you replace the whole thing with your own UI.
This is a quiet strategic point. The same route table serves the walk-up machine buyer and the human who clicked a shared link, with no separate checkout integration. The paywall is not a product feature of any one seller; it is a default of the rail.
MCP tools as paid endpoints
The seller stack does not stop at HTTP routes. @x402/mcp — the server half of the package whose client side closed the buyer post — wraps individual MCP tool handlers in payment enforcement:
const accepts = await server.buildPaymentRequirements({
scheme: "exact", network: "eip155:84532", payTo: "0x...", price: "$0.10",
});
const paid = createPaymentWrapper(server, { accepts });
mcpServer.tool("financial_analysis", "Costs $0.10.", { ticker: z.string() },
paid(async (args) => ({ content: [{ type: "text", text: await analyze(args.ticker) }] })));
Free tools register normally; paid tools take the wrapper. Pricing granularity is per tool, not per server — a health-check ping and a ten-cent analysis coexist in one process. The wrapper's hooks encode a rule worth stealing: onBeforeExecution runs after verification but before the tool executes, and returning false aborts the call without charging. Rate limiting, quota checks, and abuse filters slot in there — deny service, take no money. onAfterSettlement fires with the transaction hash for receipts. It is the same verify → work → settle spine as the HTTP middleware, transplanted onto tool calls.
One more field closes the loop with discovery: route extensions accept the declareDiscoveryExtension metadata that feeds the Bazaar index. Declaring an input schema alongside the price is what turns a paid endpoint from something agents must be told about into something they find.
What it means for LLM4Agents
LLM4Agents sits on both sides of this stack, and the seller side is the one we operate in production. Our reserve → proxy → settle billing pipeline is, structurally, the same spine as paymentMiddleware: verify capacity before the model call, do the work, settle the true amount after — and our metered settlement is the same idea setSettlementOverrides ships as a public API. That convergence is validation, and it is also commoditization: any Express shop is now five imports away from the walk-up x402 flow we charge for. The durable difference is not the middleware; it is what sits behind the route — 345+ models, fallback chains, workspace and memory — and the operational maturity around the settle-failure window the SDK leaves as an exercise.
Concretely, the stack gives us three things. First, a conformance target: our x402 walk-up surface should behave byte-for-byte like a v2.19.0 resource server, because that is what buyer SDKs are tested against. Second, the MCP payment wrapper matters for our 67-tool MCP server — per-tool pricing with abort-without-charge semantics is exactly the enforcement model a tool catalog needs. Third, the paywall tier means the gap between "agent-payable" and "human-payable" is closing from the seller side; our endpoints should assume both audiences on every route.
Staying on the frontier
Ordered by leverage. First, run conformance against the reference: stand up an @x402/express server from the quickstart and drive our own buyer client and our walk-up flow against it in CI, pinned to each SDK release, so header or error-shape drift surfaces the week it ships and not in a customer report. Second, instrument the settle-failure window in our own pipeline — every verify that is not followed by a successful settle becomes a logged receivable with payer, amount, and reason; that report is the difference between a rail and a leak. Third, adopt the payment wrapper pattern on the MCP surface: per-tool prices, onBeforeExecution quota checks that abort without charging, receipts from onAfterSettlement. Fourth, publish declareDiscoveryExtension metadata on every priced route so Bazaar-crawling agents find us without integration work. Fifth, watch the Fastify and Go seller SDKs for feature lag against Express — the ecosystem's center of gravity is visible in which middleware gets setSettlementOverrides-class features first, and that tells us where buyer traffic will come from.
Sell to agents without running the plumbing
LLM4Agents runs the verify–settle spine in production — 345+ models behind one x402-payable gateway.
Register your agent