How to setup x402-enabled endpoints with Monad support
This guide shows how to setup payable endpoints using x402 payments and Thirdweb’s built‑in facilitator. It works on Monad testnet/mainnet.
What is x402?
x402 is the HTTP 402 "Payment Required" status code reborn as a minimal protocol for internet‑native micropayments.
Instead of subscriptions or paywalls that require accounts, x402 lets any HTTP endpoint become instantly payable:
- Client requests a resource
- Server responds 402 with a small JSON payment requirement
- Client pays with a signed transaction
- Server verifies and serves the content
Beyond legacy limitations
x402 is designed for a modern internet economy, solving key limitations of legacy systems:
- Reduce fees and friction: Direct onchain payments without intermediaries, high fees, or manual setup.
- Micropayments & usage-based billing: Charge per call or feature with simple, programmable pay-as-you-go flows.
- Machine-to-machine transactions: Let AI agents pay and access services autonomously with no keys or human input needed.
Why x402 on Monad?
Monad is a fully EVM‑compatible Layer 1 with:
- 10,000 TPS
- ~0.4s block times
- Single‑slot finality
- Parallel execution
- Extremely low fees
These properties make Monad an ideal environment for true micropayments and agent‑to‑agent commerce. Payments settle instantly, low cost, and avoid mempool congestion, perfect when many AI agents pay per API call.
Core Flow (Direct Payment)
Facilitator Flow (Recommended for Production)
A facilitator service is optional but recommended in production. Facilitators can batch transactions, cover gas, handle refunds, and simplify client logic.
Building x402 based app using Thirdweb facilitator
Prerequisites
- Node.js 18+
- An EVM wallet
- Access to Monad testnet funds (USDC test tokens below)
How to get USDC tokens on Monad testnet
You can get USDC tokens for Monad testnet using Circle's faucet:
- Visit https://faucet.circle.com
- Select USDC as the token
- Select Monad Testnet from the Network dropdown
- Enter your wallet address
- Click Send 1 USDC
Limit: One request per (stablecoin, testnet) pair per hour

You'll also need testnet MON tokens for gas fees. Get them from the Monad faucet.
Step 1: Initialize a Next.js App
Create a new Next.js project:
npx create-next-app@latest my-x402-appWhen prompted, select the following options:
- ✅ TypeScript
- ✅ ESLint
- ✅ Tailwind CSS
- ✅
src/directory - ✅ App Router
- ❌ Turbopack (optional)
- Customize default import alias:
@/*(default)
Navigate to your project:
cd my-x402-appStep 2: Create a Thirdweb account and get your API keys
-
Sign in (wallet or email/Google)
-
Create a project
-
Go to Settings → API Keys
-
You'll see two keys:
- Client ID (for frontend)
- Secret Key (for backend - keep this secure!)
-
Add to your
.envfile:NEXT_PUBLIC_CLIENT_ID=your_client_id_hereSECRET_KEY=your_secret_key_here
Step 3: Create a server wallet
A server wallet can be used to receive payments and interact with the blockchain from your backend.
-
In your Thirdweb dashboard, navigate to Wallets > Server Wallets
-
Click Wallets
-
You should already see a server wallet by default or you can choose to create a new one
-
Copy the wallet address and add it to your
.envfile asSERVER_WALLETNEXT_PUBLIC_CLIENT_ID=your_client_id_hereSECRET_KEY=your_secret_key_hereSERVER_WALLET=0xYourWallet
Step 4: Create a server side payable endpoint
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950import { createThirdwebClient } from "thirdweb";import { facilitator, settlePayment } from "thirdweb/x402";import { monadTestnet } from "thirdweb/chains";const client = createThirdwebClient({ secretKey: process.env.SECRET_KEY });const thirdwebX402Facilitator = facilitator({client,serverWalletAddress: process.env.SERVER_WALLET,});export async function GET(request: Request) {try {const paymentData = request.headers.get("x-payment");const result = await settlePayment({resourceUrl: "http://localhost:3000/api/premium",method: "GET",paymentData: req.headers.get("x-payment"),network: monadTestnet, // payable on monad testnetprice: "$0.0001", // Amount per requestpayTo: process.env.SERVER_WALLET!, // payment receiverfacilitator: twFacilitator,});if (result.status === 200) {// If payment is settled, return paid responsereturn NextResponse.json({ message: "Paid! Monad is blazing fast ⚡", tx: result.paymentReceipt });} else {// send payment statusreturn new NextResponse(JSON.stringify(result.responseBody),{status: result.status,headers: { "Content-Type": "application/json", ...(result.responseHeaders || {}) },});}} catch(error) {console.error(error);return new NextResponse(JSON.stringify({ error: "server error" }),{status: 500,headers: { "Content-Type": "application/json" },});}}
Step 5: Client‑side setup (consuming paid endpoint)
Below is an example of consuming the paid endpoint using a Next.js app, however the endpoint can be consumed via an agent script as well.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354"use client";import { useState } from "react";import { createThirdwebClient } from "thirdweb";import { wrapFetchWithPayment } from "thirdweb/x402";import { createWallet } from "thirdweb/wallets";const client = createThirdwebClient({clientId: process.env.NEXT_PUBLIC_CLIENT_ID || "YOUR_PUBLIC_CLIENT_ID",});export default function Home() {const [message, setMessage] = useState("Click to pay $0.0001 USDC (zero gas on Monad testnet)");const payAndFetch = async () => {setMessage("Connecting wallet...");try {const wallet = createWallet("io.metamask");await wallet.connect({ client });setMessage("Wallet connected — paying...");const fetchPay = wrapFetchWithPayment(fetch, client, wallet);// payable endpointconst res = await fetchPay("/api/premium"); // relative URL = no CORSconst json = await res.json();setMessage("PAID SUCCESSFULLY! 🎉\n\n" + JSON.stringify(json, null, 2));} catch (e: any) {setMessage("ERROR: " + e.message);}};return (<div className="flex min-h-screen items-center justify-center bg-zinc-50 font-sans dark:bg-black"><main className="flex min-h-screen w-full max-w-3xl flex-col items-center justify-between py-32 px-16 bg-white dark:bg-black sm:items-start"><div style={{ padding: 24, fontFamily: "system-ui, sans-serif" }}><h1>Monad testnet x402 — Thirdweb</h1>{/* Button to pay and fetch the premium content */}<button onClick={payAndFetch} style={{ padding: 12, fontSize: 16 }}>Pay & Unlock Content</button>{/* Once paid, the content will be fetched and displayed here: */}<pre style={{ marginTop: 16, background: "#111", color: "#0f0", padding: 12 }}>{message}</pre></div></main></div>)};
Running Your x402 App
Now you're ready to test your x402 payment flow:
-
Start your development server:
npm run dev -
Open http://localhost:3000 in your browser
-
Click "Pay & Unlock Content"
-
Connect your wallet
-
Approve the USDC payment
-
See the content unlock instantly!
What's Next?
You've successfully built an x402 payment-enabled app on Monad! Here are some ideas to extend your implementation:
- Add more payable endpoints - Create different pricing tiers for various content or API calls
- Build AI agent integrations - Enable autonomous agents to pay for and access your APIs
Resources
Need Help?
If you run into issues or have questions, join the Monad Developer Discord
Happy building!