<100 subscribers


X402 introduced a simple idea: let agents pay for API access using the native semantics of the web, HTTP 402. No accounts, no custodians, no browser redirects. Just a request, a payment prompt, and a retry with a signed authorization.
But X402 originally operated on a single chain and token, which restricted how widely agents could function.
AnySpend removes those limits.
It keeps the familiar X402 flow exactly as it is, while adding the ability to pay with any token on any chain. Under the hood, AnySpend performs swaps, bridging, and settlement, completely invisible to the buyer and seller.
With this upgrade, X402 becomes a universal, chain-agnostic payment layer for agents.
Agents rarely hold the “correct” asset for every service.
One might have USDC on Base, another might earn fees in ETH on Optimism, while a service might settle in DAI on Ethereum.
Before AnySpend, these mismatches broke automation.
Now, they don’t matter.
Agents pay with whatever they already have
Sellers receive exactly what they want
No one has to orchestrate routing or bridging logic
This allows autonomous systems to operate fluidly across chains and economies.
Let’s walk through how to set up both server and client that charges users for API access using Anyspend X402.
We’ll be building a weather API that requires payment before access. Users can pay with any tokens to access the weather endpoint, but the server requires B3 tokens on the Base network.
(Watch the video here)
First, let’s start with a basic Express setup. Express is a minimal and flexible Node.js web application framework that provides a robust set of features for web and mobile applications.
import express from "express";
import { config } from "dotenv";
config();
const app = express();
app.use(express.json());Here, we:
Import Express and dotenv for environment variable management
Load environment variables from a .env file
Create an Express application instance
Configure Express to parse JSON request bodies
To enable payment functionality, we need to install and import two key libraries from the Anyspend X402 ecosystem:
@b3dotfun/anyspend-x402-express — Provides Express middleware for handling payments
@b3dotfun/anyspend-x402 — Core library with facilitator configuration utilities
@b3dotfun/anyspend-x402-fetch — library that extends the standard fetch API with payment capabilities
import { paymentMiddleware } from "@b3dotfun/anyspend-x402-express";
import {
createFacilitatorConfig,
} from '@b3dotfun/anyspend-x402';
import { FacilitatorConfig } from "@b3dotfun/anyspend-x402/types";The paymentMiddleware is what we’ll use to protect our API routes, while createFacilitatorConfig helps us set up the facilitator configuration that handles payment processing.
The facilitator is a service that handles payment verification and processing. In our case, we’re using the mainnet facilitator:
const facilitatorUrl = "https://mainnet.anyspend.com/x402";
const payTo = "0x6aed5ca43987221f41ba4bb2ebe29a7f583a53ff" as `0x${string}`This URL points to the production Anyspend facilitator service that will verify payments on the blockchain before allowing requests to proceed.
The payTo address is the cryptocurrency address where payments will be sent. This is your revenue address:
All payments for API access will be sent to this payTo address. Make sure this is an address you control!
The facilitator configuration tells the payment system how to interact with the facilitator service:
const facilitatorConfig: FacilitatorConfig = {
…createFacilitatorConfig(),
url: facilitatorUrl
};We use createFacilitatorConfig() to get default configuration settings.This configuration will be passed to the payment middleware to handle payment verification.
Now comes the exciting part, setting up the payment middleware. This middleware will intercept requests to protected routes and verify that payment has been made before allowing access:
app.use(
paymentMiddleware(
payTo,
{
// Cross-chain: Accept B3 on Base
"GET /weather": {
price: {
amount: '1000000000000000', // 0.001 B3 tokens (18 decimals)
asset: {
address: '0xB3B32F9f8827D4634fE7d973Fa1034Ec9fdDB3B3',
decimals: 18,
eip712: {
name: 'B3',
version: '1',
},
},
},
network: "base",
config: {
description: "Premium weather content paid with B3 on base",
},
},
},
facilitatorConfig,
),
);Let’s break down what’s happening here:
payTo: The address that receives payments
Route configuration: We’re protecting the GET /weather endpoint
Price: Set to 1000000000000000 wei (0.001 B3 tokens, since B3 has 18 decimals)
Asset: The B3 token contract address on Base network
Network: “base” — we’re accepting payments on the Base network
Description: A human-readable description of what the payment is for
The middleware will automatically:
Check if a valid payment proof is included in the request
Verify the payment with the facilitator
Allow the request to proceed if payment is valid
Reject the request if payment is missing or invalid
Finally, we define our actual weather endpoint. This is a simple endpoint that returns weather data:
app.get("/weather", (req, res) => {
res.send({
report: {
weather: "sunny",
temperature: 70,
},
});
});Because we’ve applied the payment middleware earlier, this endpoint is now protected. Users must include a valid payment proof in their request headers to access this endpoint.
We start the server on port 4021:
app.listen(4021, () => {
console.log(`Multi-token server listening at http://localhost:${4021}`);
});The client uses a wrapped fetch function that automatically handles payment processing, so you can make API calls just like you would with regular HTTP requests, but with built-in cryptocurrency payment support.
But first, we need to configure our environment variables. The client requires a private key for signing transactions and an optional API URL:
import { config } from "dotenv";
config();
const privateKey = process.env.PRIVATE_KEY as Hex | string;
const apiUrl = process.env.API_URL || "http://localhost:3001";
const MAX_PAYMENT_VALUE = BigInt("5000000000000000000"); // 5 ethers maxHere’s what each variable does:
PRIVATE_KEY: Your Ethereum private key used to sign payment transactions. This should be kept secret and stored in a .env file.
API_URL: The base URL of the API server. Defaults to http://localhost:3001 if not provided.
MAX_PAYMENT_VALUE: A safety limit for maximum payment amount (5 ETH in this case) to prevent accidental overpayments.
The client uses @b3dotfun/anyspend-x402-fetch, a library that extends the standard fetch API with payment capabilities:
import {
decodeXPaymentResponse,
wrapFetchWithPayment,
createSigner,
type Hex,
} from "@b3dotfun/anyspend-x402-fetch";Let’s break down these imports:
wrapFetchWithPayment: The main function that wraps the standard fetch to add automatic payment handling
createSigner: Creates a cryptographic signer from a private key for signing payment transactions
decodeXPaymentResponse: Decodes payment information from API response headers
Hex: TypeScript type for hexadecimal strings (Ethereum addresses, transaction hashes, etc.)
Before we can make paid requests, we need to create a signer that will sign payment transactions on behalf of the user:
const network = “ethereum”
const signer = await createSigner(network, privateKey);
The signer:
Takes a network identifier (in this case, “ethereum”)
Uses your private key to sign transactions
Enables the client to authorize payments automatically
The signer is what allows the wrapped fetch function to create and sign payment transactions without requiring manual wallet interactions for each request.
This is where the magic happens. We wrap the standard fetch function to add automatic payment processing:
const fetchWithPayment = wrapFetchWithPayment(
fetch,
signer,
MAX_PAYMENT_VALUE,
undefined,
undefined,
{
preferredNetwork: network,
preferredToken: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" // $USDC token on ethereum
}
);Let’s understand each parameter:
fetch: The standard fetch function to wrap
signer: The signer we created earlier for authorizing payments
MAX_PAYMENT_VALUE: Maximum payment amount as a safety limit.
Configuration object:
preferredNetwork: The blockchain network to use for payments (“ethereum” in this case)
preferredToken: The token contract address to use for payments (USDC on Ethereum)
With our payment-enabled fetch function, making a paid API call is as simple as a regular HTTP request:
const response = await fetchWithPayment(`${apiUrl}/weather`, {
method: “GET”,
});
The wrapped fetch handles all the payment complexity behind the scenes. If the API requires payment, it will:
Check the payment requirements from the API
Create and sign a payment transaction
Include the signature in the request
Then retries the API call
The API server includes payment information in response headers. We can extract and decode this information:
// Get payment response header
const paymentResponseHeader = response.headers.get("X-PAYMENT-RESPONSE");
if (paymentResponseHeader) {
const paymentInfo = decodeXPaymentResponse(paymentResponseHeader);
console.log(JSON.stringify(paymentInfo, null, 2));
}The X-PAYMENT-RESPONSE header contains encoded payment details such as:
Transaction hash
Payment amount
Token used
Network
Timestamp
This is useful for tracking payments, displaying receipts, or debugging payment issues.
We include comprehensive error handling:
if (!response.ok) {
const errorData = await response.json();
console.error("❌ Request failed:");
console.error(JSON.stringify(errorData, null, 2));
return;
}If the API call fails (due to payment issues, network problems, or API errors), we:
Check if the response is not OK
Parse the error response
Display it in a readable format
Exit gracefully
Finally, we extract and display the actual API response:
// Get the response data
const data = await response.json();
console.log(JSON.stringify(data, null, 2));In our weather API example, this would display:
{
"report": {
"weather": "sunny",
"temperature": 70
}
}The X402 flow stays exactly the same:
Client requests a resource
Server responds with 402 + payment details
Client retries with a signed payment authorization
Service grants access
The only difference now is what the facilitator is capable of.
Once the signed message arrives, AnySpend handles everything else:
Validates the signature (EIP-2612 / EIP-3009 typed data)
Pulls the specified funds
Swaps the token if needed
Bridges to the seller’s chain, if needed
Settles in the seller’s preferred token
To the client, nothing changes.
To the seller, everything becomes interoperable.
The security guarantees come from the signature standards themselves—not the facilitator.
Standard signature with EIP 2612 and EIP 3009, which offers replay protection via nonces
Signature expiration via deadlines
Signer verification via EIP-712 address recovery by the contract
A facilitator cannot forge or spoof signatures
AnySpend only executes what the wallet owner explicitly authorizes.
AnySpend is designed so both sides can integrate easily:
Express middleware to monetize any endpoint with a 402 paywall
Settlement configuration (desired chain + token)
REST API for low-level control
Client SDK for easy integration.
Preparing and signing typed-data payments is handled automatically.
Drop-in compatibility with existing X402 agent scripts
The integration experience remains simple and consistent.
You keep writing X402 the way you always have.
AnySpend makes it work everywhere.
That’s the entire upgrade.
X402 keeps the simple flow, and AnySpend extends it across chains and assets. Agents pay with what they have, services settle how they prefer, and the whole loop stays automated.
AnySpend Website
HeimLabs
This is the foundation of open, intelligent, multi-chain autonomous commerce.
Share what you’re building with the CDP stack!
Follow HeimLabs for unapologetically practical Web3 dev content.
Twitter, LinkedIn.
Happy Building 🚀
X402 introduced a simple idea: let agents pay for API access using the native semantics of the web, HTTP 402. No accounts, no custodians, no browser redirects. Just a request, a payment prompt, and a retry with a signed authorization.
But X402 originally operated on a single chain and token, which restricted how widely agents could function.
AnySpend removes those limits.
It keeps the familiar X402 flow exactly as it is, while adding the ability to pay with any token on any chain. Under the hood, AnySpend performs swaps, bridging, and settlement, completely invisible to the buyer and seller.
With this upgrade, X402 becomes a universal, chain-agnostic payment layer for agents.
Agents rarely hold the “correct” asset for every service.
One might have USDC on Base, another might earn fees in ETH on Optimism, while a service might settle in DAI on Ethereum.
Before AnySpend, these mismatches broke automation.
Now, they don’t matter.
Agents pay with whatever they already have
Sellers receive exactly what they want
No one has to orchestrate routing or bridging logic
This allows autonomous systems to operate fluidly across chains and economies.
Let’s walk through how to set up both server and client that charges users for API access using Anyspend X402.
We’ll be building a weather API that requires payment before access. Users can pay with any tokens to access the weather endpoint, but the server requires B3 tokens on the Base network.
(Watch the video here)
First, let’s start with a basic Express setup. Express is a minimal and flexible Node.js web application framework that provides a robust set of features for web and mobile applications.
import express from "express";
import { config } from "dotenv";
config();
const app = express();
app.use(express.json());Here, we:
Import Express and dotenv for environment variable management
Load environment variables from a .env file
Create an Express application instance
Configure Express to parse JSON request bodies
To enable payment functionality, we need to install and import two key libraries from the Anyspend X402 ecosystem:
@b3dotfun/anyspend-x402-express — Provides Express middleware for handling payments
@b3dotfun/anyspend-x402 — Core library with facilitator configuration utilities
@b3dotfun/anyspend-x402-fetch — library that extends the standard fetch API with payment capabilities
import { paymentMiddleware } from "@b3dotfun/anyspend-x402-express";
import {
createFacilitatorConfig,
} from '@b3dotfun/anyspend-x402';
import { FacilitatorConfig } from "@b3dotfun/anyspend-x402/types";The paymentMiddleware is what we’ll use to protect our API routes, while createFacilitatorConfig helps us set up the facilitator configuration that handles payment processing.
The facilitator is a service that handles payment verification and processing. In our case, we’re using the mainnet facilitator:
const facilitatorUrl = "https://mainnet.anyspend.com/x402";
const payTo = "0x6aed5ca43987221f41ba4bb2ebe29a7f583a53ff" as `0x${string}`This URL points to the production Anyspend facilitator service that will verify payments on the blockchain before allowing requests to proceed.
The payTo address is the cryptocurrency address where payments will be sent. This is your revenue address:
All payments for API access will be sent to this payTo address. Make sure this is an address you control!
The facilitator configuration tells the payment system how to interact with the facilitator service:
const facilitatorConfig: FacilitatorConfig = {
…createFacilitatorConfig(),
url: facilitatorUrl
};We use createFacilitatorConfig() to get default configuration settings.This configuration will be passed to the payment middleware to handle payment verification.
Now comes the exciting part, setting up the payment middleware. This middleware will intercept requests to protected routes and verify that payment has been made before allowing access:
app.use(
paymentMiddleware(
payTo,
{
// Cross-chain: Accept B3 on Base
"GET /weather": {
price: {
amount: '1000000000000000', // 0.001 B3 tokens (18 decimals)
asset: {
address: '0xB3B32F9f8827D4634fE7d973Fa1034Ec9fdDB3B3',
decimals: 18,
eip712: {
name: 'B3',
version: '1',
},
},
},
network: "base",
config: {
description: "Premium weather content paid with B3 on base",
},
},
},
facilitatorConfig,
),
);Let’s break down what’s happening here:
payTo: The address that receives payments
Route configuration: We’re protecting the GET /weather endpoint
Price: Set to 1000000000000000 wei (0.001 B3 tokens, since B3 has 18 decimals)
Asset: The B3 token contract address on Base network
Network: “base” — we’re accepting payments on the Base network
Description: A human-readable description of what the payment is for
The middleware will automatically:
Check if a valid payment proof is included in the request
Verify the payment with the facilitator
Allow the request to proceed if payment is valid
Reject the request if payment is missing or invalid
Finally, we define our actual weather endpoint. This is a simple endpoint that returns weather data:
app.get("/weather", (req, res) => {
res.send({
report: {
weather: "sunny",
temperature: 70,
},
});
});Because we’ve applied the payment middleware earlier, this endpoint is now protected. Users must include a valid payment proof in their request headers to access this endpoint.
We start the server on port 4021:
app.listen(4021, () => {
console.log(`Multi-token server listening at http://localhost:${4021}`);
});The client uses a wrapped fetch function that automatically handles payment processing, so you can make API calls just like you would with regular HTTP requests, but with built-in cryptocurrency payment support.
But first, we need to configure our environment variables. The client requires a private key for signing transactions and an optional API URL:
import { config } from "dotenv";
config();
const privateKey = process.env.PRIVATE_KEY as Hex | string;
const apiUrl = process.env.API_URL || "http://localhost:3001";
const MAX_PAYMENT_VALUE = BigInt("5000000000000000000"); // 5 ethers maxHere’s what each variable does:
PRIVATE_KEY: Your Ethereum private key used to sign payment transactions. This should be kept secret and stored in a .env file.
API_URL: The base URL of the API server. Defaults to http://localhost:3001 if not provided.
MAX_PAYMENT_VALUE: A safety limit for maximum payment amount (5 ETH in this case) to prevent accidental overpayments.
The client uses @b3dotfun/anyspend-x402-fetch, a library that extends the standard fetch API with payment capabilities:
import {
decodeXPaymentResponse,
wrapFetchWithPayment,
createSigner,
type Hex,
} from "@b3dotfun/anyspend-x402-fetch";Let’s break down these imports:
wrapFetchWithPayment: The main function that wraps the standard fetch to add automatic payment handling
createSigner: Creates a cryptographic signer from a private key for signing payment transactions
decodeXPaymentResponse: Decodes payment information from API response headers
Hex: TypeScript type for hexadecimal strings (Ethereum addresses, transaction hashes, etc.)
Before we can make paid requests, we need to create a signer that will sign payment transactions on behalf of the user:
const network = “ethereum”
const signer = await createSigner(network, privateKey);
The signer:
Takes a network identifier (in this case, “ethereum”)
Uses your private key to sign transactions
Enables the client to authorize payments automatically
The signer is what allows the wrapped fetch function to create and sign payment transactions without requiring manual wallet interactions for each request.
This is where the magic happens. We wrap the standard fetch function to add automatic payment processing:
const fetchWithPayment = wrapFetchWithPayment(
fetch,
signer,
MAX_PAYMENT_VALUE,
undefined,
undefined,
{
preferredNetwork: network,
preferredToken: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" // $USDC token on ethereum
}
);Let’s understand each parameter:
fetch: The standard fetch function to wrap
signer: The signer we created earlier for authorizing payments
MAX_PAYMENT_VALUE: Maximum payment amount as a safety limit.
Configuration object:
preferredNetwork: The blockchain network to use for payments (“ethereum” in this case)
preferredToken: The token contract address to use for payments (USDC on Ethereum)
With our payment-enabled fetch function, making a paid API call is as simple as a regular HTTP request:
const response = await fetchWithPayment(`${apiUrl}/weather`, {
method: “GET”,
});
The wrapped fetch handles all the payment complexity behind the scenes. If the API requires payment, it will:
Check the payment requirements from the API
Create and sign a payment transaction
Include the signature in the request
Then retries the API call
The API server includes payment information in response headers. We can extract and decode this information:
// Get payment response header
const paymentResponseHeader = response.headers.get("X-PAYMENT-RESPONSE");
if (paymentResponseHeader) {
const paymentInfo = decodeXPaymentResponse(paymentResponseHeader);
console.log(JSON.stringify(paymentInfo, null, 2));
}The X-PAYMENT-RESPONSE header contains encoded payment details such as:
Transaction hash
Payment amount
Token used
Network
Timestamp
This is useful for tracking payments, displaying receipts, or debugging payment issues.
We include comprehensive error handling:
if (!response.ok) {
const errorData = await response.json();
console.error("❌ Request failed:");
console.error(JSON.stringify(errorData, null, 2));
return;
}If the API call fails (due to payment issues, network problems, or API errors), we:
Check if the response is not OK
Parse the error response
Display it in a readable format
Exit gracefully
Finally, we extract and display the actual API response:
// Get the response data
const data = await response.json();
console.log(JSON.stringify(data, null, 2));In our weather API example, this would display:
{
"report": {
"weather": "sunny",
"temperature": 70
}
}The X402 flow stays exactly the same:
Client requests a resource
Server responds with 402 + payment details
Client retries with a signed payment authorization
Service grants access
The only difference now is what the facilitator is capable of.
Once the signed message arrives, AnySpend handles everything else:
Validates the signature (EIP-2612 / EIP-3009 typed data)
Pulls the specified funds
Swaps the token if needed
Bridges to the seller’s chain, if needed
Settles in the seller’s preferred token
To the client, nothing changes.
To the seller, everything becomes interoperable.
The security guarantees come from the signature standards themselves—not the facilitator.
Standard signature with EIP 2612 and EIP 3009, which offers replay protection via nonces
Signature expiration via deadlines
Signer verification via EIP-712 address recovery by the contract
A facilitator cannot forge or spoof signatures
AnySpend only executes what the wallet owner explicitly authorizes.
AnySpend is designed so both sides can integrate easily:
Express middleware to monetize any endpoint with a 402 paywall
Settlement configuration (desired chain + token)
REST API for low-level control
Client SDK for easy integration.
Preparing and signing typed-data payments is handled automatically.
Drop-in compatibility with existing X402 agent scripts
The integration experience remains simple and consistent.
You keep writing X402 the way you always have.
AnySpend makes it work everywhere.
That’s the entire upgrade.
X402 keeps the simple flow, and AnySpend extends it across chains and assets. Agents pay with what they have, services settle how they prefer, and the whole loop stays automated.
AnySpend Website
HeimLabs
This is the foundation of open, intelligent, multi-chain autonomous commerce.
Share what you’re building with the CDP stack!
Follow HeimLabs for unapologetically practical Web3 dev content.
Twitter, LinkedIn.
Happy Building 🚀
Share Dialog
Share Dialog
HeimLabs
HeimLabs
No comments yet