Install
$ agentstack add skill-aomi-labs-skills-x402 Open-source listing — not yet scanned by AgentStack. Follow the source repository for install instructions.
Security review
⚠ Flagged1 finding(s); flagged for manual review. · v0.1.0 How review works →
- • Prompt-injection patterns
- • Secret / credential exfiltration
- • Dangerous shell & filesystem operations
- • Untrusted network calls
- • Known-malicious package signatures
- high Reads credentials/environment and may exfiltrate them.
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ● Environment & secrets Used
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
x402
x402 is an open payment protocol that revives the HTTP 402 "Payment Required" status code to enable instant stablecoin payments directly over HTTP. It uses a three-actor model — Client (buyer), Resource Server (seller), and Facilitator (settlement) — to let any HTTP endpoint accept payment without API keys, sessions, or accounts. The protocol is built on EIP-3009 transferWithAuthorization for gasless USDC transfers, meaning clients sign an off-chain authorization and the facilitator broadcasts the on-chain settlement.
Package ecosystem:
- Server (TypeScript):
@x402/express,@x402/hono,@x402/next - Client (TypeScript):
@x402/fetch,@x402/axios - Client (Python):
pip install x402 - Client (Go):
go get github.com/coinbase/x402/go - Core:
@x402/core,@x402/evm,@x402/svm
CDP Facilitator endpoint: https://api.cdp.coinbase.com/platform/v2/x402
What You Probably Got Wrong
> LLMs have stale training data. These are the most common mistakes.
- "x402 requires API keys or OAuth" -- There are no API keys. Payment IS authentication. The client signs a USDC transfer authorization, includes it in the
X-PAYMENTheader, and the server verifies it. If the signature and balance check pass, the request is authorized. No accounts, no sessions, no bearer tokens.
- "x402 only works on Base" -- The protocol supports any EVM chain where USDC implements EIP-3009 (Base, Ethereum, Arbitrum, Optimism, Polygon) and Solana. Network identifiers use CAIP-2 format:
eip155:8453for Base mainnet,eip155:1for Ethereum,solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpfor Solana mainnet.
- "Nonces are sequential counters" -- x402 nonces are random 32-byte values (
bytes32), not incrementing integers. Each authorization generates a fresh random nonce. The ERC-3009 contract tracks used nonces on-chain to prevent replay. Using sequential nonces or reusing a nonce will cause settlement to revert.
- "The client pays gas" -- The client never submits an on-chain transaction. The client signs an off-chain EIP-712 authorization. The facilitator broadcasts the
transferWithAuthorizationcall and pays gas. CDP's hosted facilitator absorbs gas costs on Base mainnet for free-tier usage (1,000 tx/month).
- "x402 supports any ERC-20 token" -- The primary path uses USDC via EIP-3009
transferWithAuthorizationfor truly gasless transfers. A Permit2 fallback exists for arbitrary ERC-20 tokens, but it requires the user to have previously approved the Permit2 contract, which needs native gas. For the simplest integration, use USDC.
- "The old package name is @coinbase/x402" -- The current packages use the
@x402/*namespace (e.g.,@x402/express,@x402/fetch). Earlier versions usedx402-expressandx402-fetchwithout the scope. The@coinbase/x402name was never published.
- "The payment header is called PAYMENT-SIGNATURE" -- The header name is
X-PAYMENT. It contains a base64-encoded JSON payload with the authorization, signature, scheme, and network. The server responds withX-PAYMENT-RESPONSE.
Core Concepts
Three-Actor Model
┌──────────┐ 1. Request resource ┌─────────────────┐
│ │ ─────────────────────────────► │ │
│ │ 2. HTTP 402 + payment │ Resource │
│ Client │ ◄───────────────────────────── │ Server │
│ (Buyer) │ 3. Request + X-PAYMENT │ (Seller) │
│ │ ─────────────────────────────► │ │
│ │ 6. HTTP 200 + resource │ │
│ │ ◄───────────────────────────── │ │
└──────────┘ └────────┬────────┘
│
4. Verify │ 5. Settle
│
┌────────▼────────┐
│ │
│ Facilitator │
│ (Settlement) │
│ │
└─────────────────┘
Payment Flow Lifecycle
- Client sends a standard HTTP request to a protected endpoint
- Resource Server responds with HTTP 402 and payment requirements in the response body (scheme, network, amount, asset, payTo address)
- Client constructs an EIP-3009 authorization (from, to, value, validAfter, validBefore, nonce), signs it with EIP-712, and retries the request with the
X-PAYMENTheader containing the base64-encoded payload - Resource Server forwards the payment to the Facilitator for verification (
/verifyendpoint) - Facilitator validates the signature, checks balance, and simulates the transfer
- Resource Server serves the resource
- Resource Server requests settlement from the Facilitator (
/settleendpoint) - Facilitator calls
transferWithAuthorizationon the USDC contract to move funds on-chain
HTTP 402 Response Format
When a client hits a paid endpoint without a payment header, the server returns:
HTTP/1.1 402 Payment Required
Content-Type: application/json
{
"x402Version": 2,
"accepts": [
{
"scheme": "exact",
"network": "eip155:8453",
"amount": "10000",
"asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"payTo": "0xSELLER_ADDRESS",
"maxTimeoutSeconds": 60,
"extra": {
"assetTransferMethod": "eip3009",
"name": "USDC",
"version": "2"
}
}
],
"error": "X-PAYMENT header is required"
}
The amount is in the token's smallest unit. For USDC (6 decimals), 10000 = $0.01.
ERC-3009 transferWithAuthorization
EIP-3009 enables gasless token transfers by separating authorization from execution. The token holder signs an off-chain message; anyone can submit the signed authorization to the contract.
Function Signature
function transferWithAuthorization(
address from,
address to,
uint256 value,
uint256 validAfter,
uint256 validBefore,
bytes32 nonce,
bytes signature
) external;
EIP-712 Domain
EIP712Domain({
name: "USDC",
version: "2",
chainId: ,
verifyingContract:
})
Authorization Struct (for signing)
TransferWithAuthorization({
from: address,
to: address,
value: uint256,
validAfter: uint256,
validBefore: uint256,
nonce: bytes32
})
Key properties:
nonceis a randombytes32, not a sequential counter. The contract maintains a mapping of(authorizer, nonce) => boolto track used nonces.validAfterandvalidBeforedefine a time window. SetvalidAfterto0(or current timestamp) andvalidBeforeto current timestamp +maxTimeoutSeconds.- The facilitator calls this function — the signer never needs native gas.
- USDC on Base, Ethereum, Arbitrum, Optimism, and Polygon all implement EIP-3009.
Server Setup (TypeScript)
Express Middleware
npm install @x402/core @x402/evm @x402/express
import express from "express";
import { paymentMiddleware, x402ResourceServer } from "@x402/express";
import { ExactEvmScheme } from "@x402/evm/exact/server";
import { HTTPFacilitatorClient } from "@x402/core/server";
const app = express();
const facilitatorClient = new HTTPFacilitatorClient({
url: "https://api.cdp.coinbase.com/platform/v2/x402",
});
const resourceServer = new x402ResourceServer(facilitatorClient)
.register("eip155:8453", new ExactEvmScheme());
app.use(
paymentMiddleware(
{
"GET /api/weather": {
accepts: {
scheme: "exact",
network: "eip155:8453",
price: "$0.01",
payTo: process.env.PAYMENT_ADDRESS as `0x${string}`,
},
description: "Current weather data",
},
"POST /api/analyze": {
accepts: {
scheme: "exact",
network: "eip155:8453",
price: "$0.05",
payTo: process.env.PAYMENT_ADDRESS as `0x${string}`,
},
description: "AI text analysis",
},
},
resourceServer,
),
);
app.get("/api/weather", (_req, res) => {
res.json({ temperature: 72, unit: "F", location: "San Francisco" });
});
app.post("/api/analyze", (req, res) => {
res.json({ sentiment: "positive", confidence: 0.92 });
});
app.listen(3000, () => {
console.log("x402 server running on port 3000");
});
Hono and Next.js middleware follow the same pattern — install @x402/hono or @x402/next and use the same paymentMiddleware + x402ResourceServer setup with framework-specific imports.
Client Setup (TypeScript)
Using @x402/fetch (Recommended)
The wrapFetchWithPaymentFromConfig function wraps the native fetch API to automatically handle 402 responses — detect the payment requirement, sign the authorization, and retry with the X-PAYMENT header.
npm install @x402/core @x402/evm @x402/fetch viem
import { wrapFetchWithPaymentFromConfig } from "@x402/fetch";
import { ExactEvmScheme } from "@x402/evm";
import { privateKeyToAccount } from "viem/accounts";
import type { Hex } from "viem";
const account = privateKeyToAccount(process.env.PRIVATE_KEY as Hex);
const fetchWithPayment = wrapFetchWithPaymentFromConfig(fetch, {
schemes: [
{
network: "eip155:8453",
client: new ExactEvmScheme(account),
},
],
});
const response = await fetchWithPayment("https://api.example.com/api/weather");
if (!response.ok) {
throw new Error(`Request failed: ${response.status}`);
}
const data = await response.json();
console.log(data);
Multi-Network Client
import { x402Client, wrapFetchWithPayment } from "@x402/fetch";
import { registerExactEvmScheme } from "@x402/evm/exact/client";
import { registerExactSvmScheme } from "@x402/svm/exact/client";
import { privateKeyToAccount } from "viem/accounts";
import { Keypair } from "@solana/web3.js";
import type { Hex } from "viem";
const evmAccount = privateKeyToAccount(process.env.EVM_PRIVATE_KEY as Hex);
const solanaKeypair = Keypair.fromSecretKey(
Buffer.from(process.env.SOLANA_PRIVATE_KEY as string, "base64")
);
const client = new x402Client();
registerExactEvmScheme(client, { signer: evmAccount });
registerExactSvmScheme(client, { signer: solanaKeypair });
const fetchWithPayment = wrapFetchWithPayment(fetch, client);
// Automatically handles both EVM and Solana payment requirements
const evmResponse = await fetchWithPayment("https://api.example.com/evm-data");
const solResponse = await fetchWithPayment("https://api.example.com/sol-data");
For manual header construction without the wrapper, see the agent-client and python-client examples in this skill's examples/ directory.
Client Setup (Python)
pip install "x402[evm,requests]"
Using the x402 Client
from x402 import x402ClientSync
from x402.evm import ExactEvmScheme
from eth_account import Account
wallet = Account.from_key("0xYOUR_PRIVATE_KEY")
client = x402ClientSync()
client.register("eip155:*", ExactEvmScheme(signer=wallet))
# Make a paid request — the client handles the 402 flow automatically
import requests
session = requests.Session()
response = session.get("https://api.example.com/api/weather")
if response.status_code == 402:
payment_required = response.json()
payload = client.create_payment_payload(payment_required)
response = session.get(
"https://api.example.com/api/weather",
headers={"X-PAYMENT": payload.to_header()},
)
print(response.json())
For manual EIP-712 signing without the x402 package, see the python-client example in this skill's examples/ directory.
Facilitator Patterns
CDP Hosted Facilitator (Default)
Coinbase Developer Platform provides a hosted facilitator with two endpoints:
| Endpoint | Method | Purpose | |----------|--------|---------| | /verify | POST | Validate signature, check balance, simulate transfer | | /settle | POST | Execute transferWithAuthorization on-chain |
Pricing:
- Free tier: 1,000 transactions/month
- Standard: $0.001 per transaction thereafter
- Base mainnet: Zero gas fees (gas absorbed by facilitator)
import { HTTPFacilitatorClient } from "@x402/core/server";
const facilitator = new HTTPFacilitatorClient({
url: "https://api.cdp.coinbase.com/platform/v2/x402",
});
Custom Facilitator
Run your own facilitator for full control over settlement:
import { createFacilitatorServer } from "@x402/core/facilitator";
import { ExactEvmScheme } from "@x402/evm/exact/server";
import { createWalletClient, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { base } from "viem/chains";
import type { Hex } from "viem";
// Facilitator wallet pays gas for settlement
const facilitatorAccount = privateKeyToAccount(
process.env.FACILITATOR_PRIVATE_KEY as Hex
);
const walletClient = createWalletClient({
account: facilitatorAccount,
chain: base,
transport: http(),
});
const server = createFacilitatorServer({
schemes: {
"eip155:8453": new ExactEvmScheme({ walletClient }),
},
});
server.listen(4000, () => {
console.log("Custom facilitator on port 4000");
});
Settlement Flow
- Resource server sends the payment payload to facilitator
/verify - Facilitator checks: signature validity, signer balance, authorization parameters, transfer simulation
- If valid, resource server serves the response and calls facilitator
/settle - Facilitator submits
transferWithAuthorization(from, to, value, validAfter, validBefore, nonce, signature)to the USDC contract - Facilitator returns the transaction hash and receipt
The facilitator cannot modify the amount or destination. It serves only as the transaction broadcaster.
Agent-to-Agent Payments
x402 enables autonomous agent commerce where AI agents discover services, negotiate prices, and pay without human intervention.
Discovery Pattern
// Agent discovers paid endpoints via standard HTTP
const discoveryResponse = await fetch("https://agent-service.com/.well-known/x402");
const services = await discoveryResponse.json();
// services contains available endpoints with pricing
// {
// "endpoints": [
// { "path": "/api/summarize", "price": "$0.02", "network": "eip155:8453" },
// { "path": "/api/translate", "price": "$0.01", "network": "eip155:8453" }
// ]
// }
Autonomous Payment Execution
import { wrapFetchWithPaymentFromConfig } from "@x402/fetch";
import { ExactEvmScheme } from "@x402/evm";
import { privateKeyToAccount } from "viem/accounts";
import type { Hex } from "viem";
const agentAccount = privateKeyToAccount(process.env.AGENT_PRIVATE_KEY as Hex);
const agentFetch = wrapFetchWithPaymentFromConfig(fetch, {
schemes: [
{ network: "eip155:8453", client: new ExactEvmScheme(agentAccount) },
],
});
// Agent autonomously pays for another agent's service
async function callPaidService(url: string, body: Record): Promise {
const response = await agentFetch(url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
});
if (!response.ok) {
throw new Error(`Service call failed: ${response.status}`);
}
return response.json();
}
// Multi-step agent workflow: summarize -> translate -> store
const summary = await callPaidService("https://summarizer.agent/api/summarize", {
text: "Long document content...",
});
const translation = await callPaidService("https://translator.agent/api/translate", {
text: summary,
targetLanguage: "es",
});
Pricing Strategies
Per-Request (Fixed Price)
Charge a flat fee per API call. Simple
…
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: aomi-labs
- Source: aomi-labs/skills
- License: MIT
- Homepage: https://aomi.dev/
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.