Drop-in x402 payments for your APIs and clients. Gate any HTTP route behind a stablecoin micropayment in a few lines — no accounts, no API keys, no webhooks. Built for Yeetful, MIT-licensed, works anywhere TypeScript does.
npm install yeetful viem// Server — gate a route for 1¢ USDC
import { withPayment } from 'yeetful/next'
export const GET = withPayment(
{ price: '0.01', recipient: '0xYourAddress', network: 'base' },
async () => Response.json({ secret: 'gm' })
)// Client — auto-pay when a server returns 402
import { createPaymentClient } from 'yeetful/client'
import { createWalletClient, http } from 'viem'
import { base } from 'viem/chains'
import { privateKeyToAccount } from 'viem/accounts'
const wallet = createWalletClient({
account: privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`),
chain: base,
transport: http(),
})
const pay = createPaymentClient({ wallet })
const res = await pay('https://api.example.com/premium')
console.log(await res.json()) // → { secret: 'gm' }x402 is a reborn HTTP 402 Payment Required — a protocol where servers quote a price, clients sign a stablecoin authorization, and a facilitator settles on-chain. No accounts, no Stripe dashboards, no webhook retries. Works on EVM chains today (USDC on Base, Optimism, Arbitrum, Polygon, Ethereum).
You get:
- Per-request pricing for any API — LLM calls, data feeds, premium endpoints, MCP tools.
- One-sentence paywalls for agents: an LLM with a wallet can now pay for what it uses.
- Instant settlement on L2 — no chargebacks, no holds, no 30-day payout delay.
npm install yeetful viem
# or
pnpm add yeetful viem
# or
yarn add yeetful viemviem is a peer dependency so the SDK stays light and stays in sync with whatever viem version your app already uses.
// app/api/premium/route.ts
import { withPayment } from 'yeetful/next'
export const GET = withPayment(
{
price: '0.01', // USD
recipient: '0xYourWalletAddress', // gets paid
network: 'base', // or ['base', 'optimism']
description: 'Premium GM endpoint',
},
async (req) => {
return Response.json({ message: 'gm, thanks for the cent' })
}
)import express from 'express'
import { paymentRequired } from 'yeetful/express'
const app = express()
app.get(
'/premium',
paymentRequired({
price: '0.01',
recipient: '0xYourWalletAddress',
network: 'base',
}),
(req, res) => {
res.json({ message: 'gm', payer: req.x402?.payer })
}
)
app.listen(3000)Use the runtime-agnostic gate() helper. Give it a standard Request, get back either a 402 Response or a settle() handle.
import { gate } from 'yeetful/server'
export default {
async fetch(request: Request) {
const result = await gate(request, {
price: '0.01',
recipient: '0xYourWalletAddress',
network: 'base',
})
if (result.type === 'paymentRequired') return result.response
// …do the paid work…
const body = Response.json({ message: 'gm' })
const { header } = await result.settle()
body.headers.set('X-PAYMENT-RESPONSE', header)
return body
},
}import { createPaymentClient } from 'yeetful/client'
const pay = createPaymentClient({
wallet, // any viem WalletClient
maxAmountAtomic: 1_000_000n, // cap: 1 USDC per call
allowedNetworks: ['base'], // only pay on Base
onPaymentRequired: async (req) => {
console.log(`Pay ${req.maxAmountRequired} to ${req.payTo}?`)
return true // return false to cancel
},
})
// Use exactly like fetch.
const res = await pay('https://api.example.com/premium')| Option | Type | Default | Notes |
|---|---|---|---|
price |
string | number |
required | USD amount, e.g. '0.01'. Converted to USDC atomic units. |
recipient |
Address |
required | Address that receives the payment. |
network |
X402Network | X402Network[] |
'base' |
Networks you'll accept. Multi-chain = multi-item discovery. |
asset |
Address |
USDC for network | Override to use a different ERC-20. |
description |
string |
— | Shown to the paying client. |
maxTimeoutSeconds |
number |
600 |
Validity window of the signed authorization. |
facilitator |
FacilitatorConfig | false |
hosted facilitator | Pass false to skip on-chain settlement (testing only). |
Supported networks: base, base-sepolia, ethereum, optimism, arbitrum, polygon.
| Option | Type | Notes |
|---|---|---|
wallet |
WalletClient |
Any viem wallet capable of signing EIP-712 typed data. |
maxAmountAtomic |
bigint |
Reject requirements above this cap — safety belt. |
allowedNetworks |
X402Network[] |
Only pay on these networks. |
onPaymentRequired |
(req) => boolean | Promise<boolean> |
Approval hook; return false to cancel. |
fetch |
typeof fetch |
Override the underlying fetch (e.g. for timeouts). |
- Client requests a paid resource normally.
- Server responds with
402 Payment Requiredand a JSON body listing acceptable requirements (network, asset, amount, recipient). - Client picks the cheapest requirement, signs an EIP-3009
TransferWithAuthorizationwith the user's wallet, and retries the request with anX-PAYMENTheader (base64 JSON). - Server hands the signed payload to a facilitator which
verifys the signature andsettles the transfer on-chain. - Server runs the handler and returns the response with an
X-PAYMENT-RESPONSEheader containing the transaction hash.
The signing is gasless for the payer — the facilitator broadcasts the transfer and picks up gas.
By default the SDK uses the hosted facilitator at https://facilitator.yeetful.com. Override it anywhere you configure the server:
withPayment(
{
price: '0.01',
recipient: '0xYourAddress',
facilitator: {
url: 'https://your-facilitator.example.com',
authHeader: 'Bearer your-token',
},
},
handler,
)Pass facilitator: false to skip verification and settlement entirely — only useful for local testing.
withPayment(
{
price: '0.01',
recipient: '0xYourAddress',
network: ['base', 'optimism', 'arbitrum'],
},
handler,
)Clients automatically pick the cheapest network they're configured to use.
import { signPayment } from 'yeetful/client'
const payment = await signPayment(wallet, {
scheme: 'exact',
network: 'base',
asset: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', // USDC
maxAmountRequired: '10000', // 0.01 USDC
payTo: '0xRecipient',
})x402 is a natural fit for agent tooling — drop withPayment in front of any MCP tool endpoint and agents with wallets can pay per-call. This SDK is what powers paid tools on Yeetful.
gate(request, options)— runtime-agnostic. Returns{ type: 'paymentRequired', response }or{ type: 'ok', payer, settle }.Facilitator— thin wrapper around verify/settle HTTP endpoints.DEFAULT_FACILITATOR_URL— the hosted facilitator URL.
withPayment(options, handler)— wraps a Next.js route handler.
paymentRequired(options)— returns an ExpressRequestHandler. Setsreq.x402.payerafter successful verification.
createPaymentClient(options)— returns afetch-compatible function that handles 402s automatically.signPayment(wallet, requirement)— sign a payment payload by hand.PaymentError— thrown when the client declines to pay.
usdcAddress(network)— canonical USDC contract for a supported network.usdToAtomic(amount, decimals?)— safe USD → atomic-units conversion.encodePayment/decodePayment— base64 JSON codec for headers.
npm install
npm run build # bundles ESM + CJS + d.ts via tsup
npm run typecheck
npm testTo publish:
npm run build
npm publishMIT © Yeetful