# Client
Source: https://docs.semanticpay.io/client
This guide shows how to pay for x402-protected resources using a [WDK](https://docs.wallet.tether.io) self-custodial wallet on Plasma or Stable. By the end you'll have a working `fetch` wrapper that automatically handles `402 Payment Required` responses with USD₮.
See a full working demo at [github.com/SemanticPay/x402-usdt0-demo](https://github.com/SemanticPay/x402-usdt0-demo)
## Install
```bash theme={null}
npm install @x402/fetch @x402/evm @tetherto/wdk-wallet-evm
```
## Create a wallet
Create a `WalletAccountEvm` pointed at the chain you want to pay on. The account derives keys locally from your seed phrase.
```typescript theme={null}
import { WalletAccountEvm } from "@tetherto/wdk-wallet-evm";
const account = new WalletAccountEvm(process.env.SEED_PHRASE, {
provider: "https://rpc.plasma.to", // or "https://rpc.stable.xyz"
});
const address = await account.getAddress();
console.log("Buyer address:", address);
```
## Register with x402
`WalletAccountEvm` already satisfies the signer interface that x402 expects.
```typescript theme={null}
import { x402Client, wrapFetchWithPayment } from "@x402/fetch";
import { registerExactEvmScheme } from "@x402/evm/exact/client";
const client = new x402Client();
registerExactEvmScheme(client, { signer: account });
const fetchWithPayment = wrapFetchWithPayment(fetch, client);
```
That's it. `fetchWithPayment` now intercepts any `402 Payment Required` response, signs an EIP-3009 `transferWithAuthorization` using your WDK wallet, and retries the request with the payment header attached.
## Make a paid request
```typescript theme={null}
const response = await fetchWithPayment("https://api.example.com/weather", {
method: "GET",
});
const data = await response.json();
console.log("Response:", data);
```
If the endpoint requires payment, the x402 client handles the full flow automatically:
1. Initial request returns `402` with a `PAYMENT-REQUIRED` header
2. Client parses the payment requirements (amount, token, network, recipient)
3. Client signs an EIP-3009 authorization with your WDK wallet
4. Client retries the request with the `PAYMENT-SIGNATURE` header
5. The facilitator settles the payment on-chain and the server returns the resource
## Full example
```typescript theme={null}
import { WalletAccountEvm } from "@tetherto/wdk-wallet-evm";
import { x402Client, wrapFetchWithPayment, x402HTTPClient } from "@x402/fetch";
import { registerExactEvmScheme } from "@x402/evm/exact/client";
// --- Config ---
const SEED_PHRASE = process.env.SEED_PHRASE;
const RPC = process.env.RPC_URL || "https://rpc.plasma.to";
const ENDPOINT = process.env.ENDPOINT || "https://api.example.com/weather";
// --- Wallet ---
const account = new WalletAccountEvm(SEED_PHRASE, {
provider: RPC,
});
console.log("Address:", await account.getAddress());
// --- x402 client ---
const client = new x402Client();
registerExactEvmScheme(client, { signer: account });
const fetchWithPayment = wrapFetchWithPayment(fetch, client);
// --- Request ---
const response = await fetchWithPayment(ENDPOINT, { method: "GET" });
const body = await response.json();
console.log("Response:", body);
// --- Receipt ---
if (response.ok) {
const httpClient = new x402HTTPClient(client);
const receipt = httpClient.getPaymentSettleResponse(
(name) => response.headers.get(name),
);
console.log("Payment receipt:", JSON.stringify(receipt, null, 2));
}
```
## Environment variables
```bash theme={null}
# .env
SEED_PHRASE="your twelve word seed phrase here"
RPC_URL="https://rpc.plasma.to" # Plasma mainnet
# RPC_URL="https://rpc.stable.xyz" # Stable mainnet
ENDPOINT="https://api.example.com/weather"
```
Your seed phrase controls your funds. Never commit it to version control. Use environment variables or a secrets manager.
## Checking your balance
Before making paid requests, verify your wallet has USDT0 on the target chain:
```typescript theme={null}
const USDT0_PLASMA = "0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb";
const USDT0_STABLE = "0x779Ded0c9e1022225f8E0630b35a9b54bE713736";
const balance = await account.getTokenBalance(USDT0_PLASMA);
console.log("USDT0 balance:", Number(balance) / 1e6, "USD₮");
```
USDT0 uses 6 decimals. A balance of `1000000` equals 1.00 USD₮.
## Using Axios instead of fetch
```typescript theme={null}
import { x402Client, wrapAxiosWithPayment } from "@x402/axios";
import { registerExactEvmScheme } from "@x402/evm/exact/client";
import axios from "axios";
const client = new x402Client();
registerExactEvmScheme(client, { signer: account });
const api = wrapAxiosWithPayment(
axios.create({ baseURL: "https://api.example.com" }),
client,
);
const response = await api.get("/weather");
console.log("Response:", response.data);
```
## What happens under the hood
x402 uses EIP-3009 (`transferWithAuthorization`) for payment settlement. When your WDK wallet signs a payment, it creates an off-chain authorization that allows the facilitator to transfer a specific amount of USDT0 from your address to the seller's address. The facilitator then submits this authorization on-chain in a single transaction.
Because Plasma and Stable both support EIP-3009 natively on their USDT0 contracts, the facilitator can settle payments without any gas cost to the buyer. The buyer only pays the exact amount specified in the payment requirements.
# Facilitator API
Source: https://docs.semanticpay.io/endpoints
API reference for the Semantic x402 facilitator.
Base URL: `https://x402.semanticpay.io`
The facilitator verifies and settles x402 payments on behalf of sellers. It currently supports USDT0 on Plasma (`eip155:9745`) and Stable (`eip155:988`).
***
## Verify
Validates a payment payload against the seller's requirements. Called by the seller's middleware before serving a paid resource.
```
POST /verify
```
**Request body**
| Field | Type | Description |
| --------------------- | -------- | --------------------------------------------------------------- |
| `paymentPayload` | `object` | The signed payment from the buyer (from the `X-PAYMENT` header) |
| `paymentRequirements` | `object` | The seller's price and token requirements |
**Headers**
| Header | Required | Description |
| ------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `X-Event-Callback` | No | URL to receive lifecycle events. The facilitator will POST `verify_started` and `verify_completed` (or `verify_failed`) events to this URL. |
```json theme={null}
{
"paymentPayload": {
"network": "eip155:9745",
"payload": "0x...",
"scheme": "exact"
},
"paymentRequirements": {
"network": "eip155:9745",
"maxAmountRequired": "1000000",
"asset": "0x0000000000000000000000000000000000000001",
"payTo": "0xSELLER",
"facilitator": "https://x402.semanticpay.io",
"scheme": "exact"
}
}
```
**Response**
```json theme={null}
{
"isValid": true
}
```
If invalid, `isValid` is `false` and an `invalidReason` field explains why.
***
## Settle
Settles a verified payment on-chain. Transfers USDT0 from the buyer to the seller via the facilitator's account.
```
POST /settle
```
**Request body**
Same shape as `/verify`.
**Headers**
| Header | Required | Description |
| ------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `X-Event-Callback` | No | URL to receive lifecycle events. The facilitator will POST `settle_started` and `settle_completed` (or `settle_failed`) events to this URL. |
```json theme={null}
{
"paymentPayload": { ... },
"paymentRequirements": { ... }
}
```
**Response (success)**
```json theme={null}
{
"success": true,
"transaction": "0xabc123...",
"network": "eip155:9745"
}
```
**Response (failure)**
```json theme={null}
{
"success": false,
"errorReason": "Insufficient allowance",
"network": "eip155:9745"
}
```
***
## Supported
Returns the payment schemes and networks this facilitator supports.
```
GET /supported
```
**Response**
```json theme={null}
{
"schemes": [
{
"scheme": "exact",
"network": "eip155:9745"
},
{
"scheme": "exact",
"network": "eip155:988"
}
]
}
```
***
## Health
Health check. Returns the facilitator's address and supported networks.
```
GET /health
```
**Response**
```json theme={null}
{
"status": "ok",
"facilitator": "0xFACILITATOR",
"networks": [
{
"name": "Plasma",
"network": "eip155:9745",
"chainId": 9745,
"usdt0": "0x0000000000000000000000000000000000000001"
},
{
"name": "Stable",
"network": "eip155:988",
"chainId": 988,
"usdt0": "0x..."
}
]
}
```
***
## Lifecycle Events
The facilitator can push real-time lifecycle events to a callback URL during verification and settlement. This is useful for building UIs that visualize the payment flow as it happens.
### How it works
Pass the `X-Event-Callback` header with a URL on your `/verify` and `/settle` requests. The facilitator will POST events to that URL at each stage of the process.
```bash theme={null}
curl -X POST https://x402.semanticpay.io/verify \
-H "Content-Type: application/json" \
-H "X-Event-Callback: https://your-server.com/events" \
-d '{ "paymentPayload": { ... }, "paymentRequirements": { ... } }'
```
Events are fire-and-forget — they don't block the verify/settle response. If the callback URL is unreachable, events are silently dropped.
### Event format
Each event is a JSON POST with this shape:
```json theme={null}
{
"type": "verify_started",
"step": 6,
"title": "Payment Verification Started",
"description": "Facilitator is verifying the payment signature and requirements",
"details": { ... },
"actor": "facilitator",
"timestamp": 1708123456789
}
```
### Event types
**Verification events** (sent during `/verify`):
| Type | Step | Description |
| ------------------ | ---- | -------------------------------------------------------------------------- |
| `verify_started` | 6 | Facilitator has begun verifying the payment signature and requirements |
| `verify_completed` | 7 | Verification finished successfully. `details.isValid` indicates the result |
| `verify_failed` | 7 | Verification threw an error. `details.error` contains the message |
**Settlement events** (sent during `/settle`):
| Type | Step | Description |
| ------------------ | ---- | ------------------------------------------------------------------------------ |
| `settle_started` | 9 | Facilitator is broadcasting the `receiveWithAuthorization` transaction |
| `settle_completed` | 10 | Transaction confirmed on-chain. `details.transactionHash` contains the tx hash |
| `settle_failed` | 10 | Settlement threw an error. `details.error` contains the message |
### Example: receiving events
Set up an endpoint on your server to receive the POSTed events:
```js theme={null}
app.post("/events", (req, res) => {
const { type, step, title, details } = req.body;
console.log(`[step ${step}] ${type}: ${title}`);
// Forward to SSE clients, WebSocket, etc.
res.json({ ok: true });
});
```
Then pass the URL when calling the facilitator:
```js theme={null}
const response = await fetch("https://x402.semanticpay.io/verify", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Event-Callback": "https://your-server.com/events",
},
body: JSON.stringify({ paymentPayload, paymentRequirements }),
});
```
If `X-Event-Callback` is not provided, no events are sent. The facilitator behaves identically — this is purely opt-in.
***
## Errors
All endpoints return standard HTTP status codes.
| Status | Meaning |
| ------ | ------------------------------------------------------------------- |
| `200` | Success |
| `400` | Missing required fields (`paymentPayload` or `paymentRequirements`) |
| `429` | Too many requests — rate limit exceeded |
| `500` | Internal error — the response body contains an `error` string |
# Hyperswarm
Source: https://docs.semanticpay.io/hyperswarm
Hyperswarm integration is coming soon. This page describes the architecture and why it matters for agent-to-agent payments.
## What is Hyperswarm
[Hyperswarm](https://docs.pears.com/building-blocks/hyperswarm) is a peer-to-peer networking stack built by [Holepunch](https://holepunch.to). It provides two primitives:
* **Distributed Hash Table (DHT)** for peer discovery: find other nodes by topic without a central server
* **Encrypted connections** between peers: once discovered, nodes communicate over authenticated, encrypted streams
There is no central server, no registry, no DNS lookup. Peers announce themselves on a topic (a 32-byte key), discover each other through the DHT, and establish direct encrypted connections using Noise protocol handshakes.
```
Agent A DHT Agent B
│ │ │
├──── announce(topic) ────────►│ │
│ │◄──── announce(topic) ────────┤
│ │ │
├──── lookup(topic) ──────────►│ │
│◄──── peer: Agent B ──────────┤ │
│ │ │
├──────────── encrypted connection ──────────────────────────►│
│ │ │
```
## Why Hyperswarm matters for x402
Today, x402 payments flow through HTTP: a buyer makes a request to a server URL, gets a 402 response, signs a payment, and retries. This works, but it assumes the buyer already knows the server's URL, which means centralized discovery (DNS, API registries, hardcoded endpoints).
Hyperswarm removes this assumption entirely.
**Peer-to-peer service discovery.** Instead of looking up `api.example.com`, an agent joins a topic on the DHT. Every other agent on that topic is a potential counterparty. No DNS, no registry, no single point of failure.
**Peer-to-peer transport.** Instead of routing x402 requests through HTTP to a centralized server, agents exchange payment messages directly over encrypted Hyperswarm connections. The x402 protocol works the same, 402 challenge, signed authorization, settlement, but the transport is a direct peer stream instead of an HTTP round-trip.
**No infrastructure dependency.** An agent running a local WDK wallet, connecting to peers via Hyperswarm, and settling payments on chain has zero dependency on any centralized service. It discovers peers, negotiates prices, signs payments, and settles on-chain.
## Agent-to-agent payments
This is where the architecture converges. Combine:
* **WDK** — self-custodial wallets, local key management, no API dependency
* **Hyperswarm** — peer discovery and encrypted transport, no server dependency
* **x402 on Plasma/Stable** — near-instant USD₮ settlement, no gas token management
**The result is fully peer-to-peer, trustless agent-to-agent ecosystem**. No intermediary holds keys, routes traffic, or settles funds. Every layer is decentralized.
## QVAC
[QVAC](https://qvac.tether.io) is Tether's local AI runtime: models that run on-device, privately, without cloud dependencies. QVAC is built on [Pear](https://docs.pears.com), Holepunch's peer-to-peer application platform, which uses Hyperswarm for networking.
The combination means an agent can run inference locally (QVAC), manage its own wallet (WDK), discover peers (Hyperswarm), and settle payments (x402 on Plasma/Stable), with no centralized service in the loop at any layer.
## What's coming
Semantic is building Hyperswarm transport support for x402. This will include:
* **Hyperswarm transport adapter** for x402 — same protocol, peer-to-peer delivery
* **Topic-based service discovery** — agents publish and discover paid services on DHT topics
* **Reference implementations** — buyer and seller agents using Hyperswarm + WDK + x402
## Next steps
* [Hyperswarm docs](https://docs.pears.com/building-blocks/hyperswarm) — Networking primitives
* [Pear docs](https://docs.pears.com) — P2P application platform
* [QVAC](https://qvac.tether.io) — Tether's local AI runtime
* [WDK integration](/wdk) — Self-custodial wallets for agents
# Welcome
Source: https://docs.semanticpay.io/index
Semantic is the payment infrastructure for AI agents. Trustless, P2P, and permissionless by design, so any agent can pay for data, compute, or capabilities, with private stablecoin settlement and no intermediary in the loop.
# Semantic
As AI agents graduate from simple tasks to sophisticated workflows — researching, reasoning, and acting autonomously across the internet — they hit a wall: **payments**.
Consider an agent tasked with generating a daily investment memo. It pulls market data from Kaiko, queries on-chain analytics from The Graph, runs sentiment analysis through an LLM, and compiles the output into a formatted report. Four providers, four API keys, four billing accounts, four sets of credentials to provision, rotate, and monitor. All before the agent has done anything useful. Add a fifth provider tomorrow and the whole stack needs re-plumbing.
This is the bottleneck. Every provider requires its own authentication, its own payment method, its own billing relationship. The agent can't just pay for what it needs and move on. It needs a human to pre-negotiate access at every layer.
## Agents need to pay for things
The use cases are already here. What's missing is the infrastructure to make them work.
A research agent purchasing a premium dataset, a live feed, or a proprietary API mid-workflow.
An orchestrator spinning up inference on demand, paying per-token or per-job to the cheapest available provider.
A coding agent spinning up a cloud sandbox to test its output. Paid instantly, no API key required.
A procurement agent ordering physical inventory, booking logistics, or purchasing supplies.
## The infrastructure is missing
Traditional payment rails weren't designed for machines transacting with machines at the speed and scale that agents demand. What's needed is a fundamentally different approach.
Spend limits, approval flows, and kill switches. Agents operate within boundaries their owners define.
Every payment is tied to an identity and a policy. Know which agent spent what, why, and whether it was authorized.
Full audit trail from intent to settlement. Every request, authorization, and transfer is logged and verifiable.
Near-instant finality, low and predictable fees, privacy by default, and programmable transactions.
## How Semantic works
Semantic provides an [x402-compatible](https://www.x402.org) payment facilitator that makes agent payments as simple as an HTTP header.
We leverage **USD₮**, the world's most liquid stablecoin, on chains purpose-built for stablecoin transactions: [Plasma](https://plasma.to) and [Stable](https://stable.xyz). These chains deliver the speed, cost, and privacy properties that agent commerce requires, without the congestion and gas volatility of general-purpose L1s.
The x402 protocol extends HTTP with a native payment layer. When a resource requires payment, the server responds with `402 Payment Required` and the client settles automatically, no redirects, no checkout flows, no API keys.
USD₮0 supports EIP-3009 (`transferWithAuthorization`), enabling gasless, signature-based transfers. The payer signs, the facilitator submits, no gas tokens needed in the agent's wallet.
Native Bitcoin payments via Spark invoices, bringing the world's hardest asset into the agent economy alongside stablecoins.
## Get started
Get your first payment working in 5 minutes
Explore the facilitator endpoints
See what's coming next
**Ready to build?** Start with the [Quick Setup](/client) to get your first agent payment working in under 5 minutes.
# MCP
Source: https://docs.semanticpay.io/mcp-integration
[MCP](https://modelcontextprotocol.io) (Model Context Protocol) is an open standard for connecting AI agents to external tools and services. Semantic supports MCP as a transport for x402, meaning AI agents can discover, call, and pay for tools through a standard MCP server.
## How it works
A normal MCP flow: an agent connects to an MCP server, discovers available tools via `listTools()`, and calls them via `callTool()`. With x402, the server can require payment for specific tools. The `@x402/mcp` package handles the payment flow transparently:
1. Agent calls a tool via MCP
2. Server responds with a `402 Payment Required` challenge (embedded in the MCP response)
3. The x402 MCP client automatically signs a payment authorization
4. Client retries the tool call with the payment header
5. The facilitator settles on-chain, and the tool returns its result
From the agent's perspective, it just calls a tool. Payment happens in the background.
## Install
```bash theme={null}
npm install @x402/mcp @x402/evm @modelcontextprotocol/sdk
```
## Client setup
Use `createx402MCPClient` to create an MCP client with built-in x402 payment handling.
```typescript theme={null}
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
import { ExactEvmScheme } from "@x402/evm/exact/client";
import { createx402MCPClient } from "@x402/mcp";
import { WalletAccountEvm } from "@tetherto/wdk-wallet-evm";
// WDK wallet as the payment signer
const account = new WalletAccountEvm(process.env.SEED_PHRASE, {
provider: "https://rpc.plasma.to",
});
// Create x402-enabled MCP client
const mcpClient = createx402MCPClient({
name: "my-agent",
version: "1.0.0",
schemes: [
{ network: "eip155:9745", client: new ExactEvmScheme(account) },
],
autoPayment: true,
onPaymentRequested: async (context) => {
const price = context.paymentRequired.accepts[0];
console.log(`Payment: ${price.amount} on ${price.network} for ${context.toolName}`);
return true; // approve
},
});
// Connect to MCP server
const transport = new SSEClientTransport(new URL("http://localhost:4022/sse"));
await mcpClient.connect(transport);
```
## Discovering and calling tools
Once connected, use standard MCP methods. Payment is handled automatically when a tool requires it.
```typescript theme={null}
// Discover available tools
const { tools } = await mcpClient.listTools();
for (const tool of tools) {
console.log(`${tool.name}: ${tool.description}`);
}
// Call a tool — payment happens automatically if required
const result = await mcpClient.callTool("get-weather", { city: "Dubai" });
console.log(result.content[0]?.text);
// Check if payment was made
if (result.paymentMade) {
console.log("Settled:", result.paymentResponse.transaction);
}
```
## Payment control
The `onPaymentRequested` callback gives you control over which payments to approve. Return `true` to pay, `false` to reject.
```typescript theme={null}
const mcpClient = createx402MCPClient({
// ...
autoPayment: false, // require explicit approval
onPaymentRequested: async (context) => {
const price = context.paymentRequired.accepts[0];
const amount = Number(price.amount) / 1e6; // USDT0 has 6 decimals
// Reject anything over $1
if (amount > 1.0) {
console.log(`Rejected: $${amount} is too expensive`);
return false;
}
return true;
},
});
```
## Using with LLMs
The standard pattern is: connect to MCP, convert tools to your LLM's format, let the LLM decide when to call tools, execute via MCP.
### OpenAI
```typescript theme={null}
import OpenAI from "openai";
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
// Convert MCP tools to OpenAI function calling format
const { tools } = await mcpClient.listTools();
const openaiTools: OpenAI.ChatCompletionTool[] = tools.map((tool) => ({
type: "function",
function: {
name: tool.name,
description: tool.description || "",
parameters: tool.inputSchema as Record,
},
}));
// Let the LLM decide which tools to call
const response = await openai.chat.completions.create({
model: "gpt-4o",
messages: [{ role: "user", content: "What's the weather in Dubai?" }],
tools: openaiTools,
tool_choice: "auto",
});
// Execute tool calls via MCP (payment handled automatically)
for (const toolCall of response.choices[0].message.tool_calls || []) {
const args = JSON.parse(toolCall.function.arguments);
const result = await mcpClient.callTool(toolCall.function.name, args);
console.log(result.content[0]?.text);
}
```
### Anthropic
```typescript theme={null}
import Anthropic from "@anthropic-ai/sdk";
const anthropic = new Anthropic();
// Convert MCP tools to Anthropic format
const { tools } = await mcpClient.listTools();
const anthropicTools = tools.map((tool) => ({
name: tool.name,
description: tool.description || "",
input_schema: tool.inputSchema,
}));
const response = await anthropic.messages.create({
model: "claude-sonnet-4-20250514",
max_tokens: 1024,
messages: [{ role: "user", content: "What's the weather in Dubai?" }],
tools: anthropicTools,
});
// Execute tool calls via MCP
for (const block of response.content) {
if (block.type === "tool_use") {
const result = await mcpClient.callTool(block.name, block.input);
console.log(result.content[0]?.text);
}
}
```
## Multi-network support
Register multiple chains so the client can pay on whichever network the server requires:
```typescript theme={null}
const mcpClient = createx402MCPClient({
name: "my-agent",
version: "1.0.0",
schemes: [
{ network: "eip155:9745", client: new ExactEvmScheme(plasmaAccount) },
{ network: "eip155:988", client: new ExactEvmScheme(stableAccount) },
],
autoPayment: true,
});
```
## Cleanup
Always close the client when done:
```typescript theme={null}
await mcpClient.close();
```
## Next steps
* [@x402/mcp on npm](https://www.npmjs.com/package/@x402/mcp) — Package reference
* [MCP specification](https://modelcontextprotocol.io) — Protocol docs
* [Buyer quickstart](/client) — x402 buyer setup without MCP
* [WDK integration](/wdk) — Self-custodial wallets
* [Supported chains](/supported-chains) — Network config and contract addresses
# Roadmap
Source: https://docs.semanticpay.io/roadmap
What we're building and where Semantic is headed.
## Live
Zero-fee USD₮0 settlement. Sub-second finality, protocol-level Paymaster.
USD₮0 as native gas token. Gas-free transfers, sub-second blocks, full EVM.
***
## Soon
USD₮ on Solana is natively issued and already supports transfer authorization, the core primitive x402 needs.
[Spark](https://www.spark.money) is a Bitcoin L2 with near-instant, fee-free transactions via Spark invoices. This brings native BTC into x402.
WDK accounts with spending policies enforced at SDK level. Per-transaction limits, whitelists, daily caps. Humans define the guardrails, agents operate freely within them.
***
## Planned
### Tools Gateway
A unified x402 proxy in front of premium data and infrastructure APIs. Instead of managing API keys, subscriptions, and rate limits for each provider, agents make requests through the gateway and pay per call from their wallet.
Kaiko, CoinGecko, CoinMarketCap
The Graph, Nansen, Dune
RPC endpoints, indexers, oracles
For providers, the gateway is a distribution channel: plug in your API once and every x402 agent is a potential customer with zero integration on their end.
### LLM Router Gateway
Same proxy pattern, applied to inference. One endpoint, any model, OpenAI, Anthropic, Google, Mistral, paid per token via x402.
The router adds model selection (route by cost, latency, or capability), fallback chains (automatic retry on rate limits or outages), and cost optimization (cheapest model that meets a quality threshold).
An agent with an on-chain wallet and the LLM Router can use any model from any provider without its operator pre-negotiating API access.
### Hyperswarm
[Hyperswarm](https://docs.pears.com/building-blocks/hyperswarm) replaces centralized infrastructure with peer-to-peer discovery (DHT) and encrypted direct connections (Noise protocol). Agents find each other, negotiate, and settle without DNS, registries, or servers.
Combined with [QVAC](https://qvac.tether.io) for local inference and WDK for self-custodial wallets, this is the fully trustless stack: no cloud, no intermediary, no single point of failure.
***
## The big picture
Agents pay for APIs over HTTP. A facilitator settles on-chain. Live now on Plasma and Stable.
Solana for USD₮, Spark for native BTC. Agents choose their chain and asset.
Tools and LLM Router turn x402 into a universal access layer. One wallet, any tool, any model.
Hyperswarm for discovery, QVAC for inference, WDK for wallets. Every layer decentralized.
# Server
Source: https://docs.semanticpay.io/server
This guide shows how to accept x402 payments for your API endpoints using Semantic's facilitator on Plasma or Stable. By the end you'll have an Express server that gates routes behind USD₮ payments.
See a full working demo at [github.com/SemanticPay/x402-usdt0-demo](https://github.com/SemanticPay/x402-usdt0-demo)
## Install
```bash theme={null}
npm install @x402/express @x402/evm @x402/core express dotenv
```
## How it works
Your server doesn't handle payments directly. It delegates to Semantic's facilitator:
1. A buyer hits your endpoint without a payment header
2. Your middleware responds with `402 Payment Required` and the payment terms
3. The buyer's x402 client signs an EIP-3009 authorization and retries
4. Your middleware forwards the signed payload to Semantic's facilitator
5. The facilitator verifies the signature, settles on-chain, and confirms
6. Your route handler runs and returns the resource
You never touch private keys, gas tokens, or on-chain transactions. You just specify the price and the address to receive funds.
## Pricing
The `price` field is a structured object that tells the buyer exactly what token to pay, how much, and on which chain. USDT0 uses 6 decimals, so `"1000"` = \$0.001.
```typescript theme={null}
price: {
amount: "1000", // base units (6 decimals)
asset: "0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb", // USDT0 contract
extra: { name: "USDT0", version: "1", decimals: 6 }, // EIP-712 domain info
}
```
The `extra` fields are passed through to the buyer's client for EIP-712 signature construction. `name` and `version` must match what the on-chain USDT0 contract expects.
## USDT0 Deployments
`eip155:9745` · `0xB8C...5ebb`
`eip155:988` · `0x779...3736`
Full deployment list at [docs.usdt0.to](https://docs.usdt0.to/technical-documentation/deployments).
## Minimal server (single chain)
```typescript theme={null}
import { config } from "dotenv";
import express from "express";
import { paymentMiddleware, x402ResourceServer } from "@x402/express";
import { ExactEvmScheme } from "@x402/evm/exact/server";
import { HTTPFacilitatorClient } from "@x402/core/server";
config();
// --- Config ---
const PAY_TO = process.env.PAY_TO_ADDRESS as `0x${string}`;
const FACILITATOR_URL = "https://x402.semanticpay.io/";
const PLASMA_NETWORK = "eip155:9745";
const USDT0_PLASMA = "0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb";
const PRICE = "1000"; // $0.001 in base units
// --- Facilitator client ---
const facilitatorClient = new HTTPFacilitatorClient({ url: FACILITATOR_URL });
// --- Express app ---
const app = express();
app.use(
paymentMiddleware(
{
"GET /weather": {
accepts: [
{
scheme: "exact",
network: PLASMA_NETWORK,
price: {
amount: PRICE,
asset: USDT0_PLASMA,
extra: { name: "USDT0", version: "1", decimals: 6 },
},
payTo: PAY_TO,
},
],
description: "Weather data",
mimeType: "application/json",
},
},
new x402ResourceServer(facilitatorClient).register(
PLASMA_NETWORK,
new ExactEvmScheme(),
),
),
);
app.get("/weather", (req, res) => {
res.json({ weather: "sunny", temperature: 70 });
});
app.get("/health", (req, res) => {
res.json({ status: "ok", chain: "plasma", payTo: PAY_TO });
});
const PORT = process.env.PORT || 4021;
app.listen(PORT, () => {
console.log(`Server listening at http://localhost:${PORT}`);
console.log(`Network: ${PLASMA_NETWORK}`);
console.log(`USDT0: ${USDT0_PLASMA}`);
console.log(`Pay to: ${PAY_TO}`);
});
```
## Multi-chain server (Plasma + Stable)
Accept payments on both chains. The buyer's client picks whichever network it has funds on.
```typescript theme={null}
import { config } from "dotenv";
import express from "express";
import { paymentMiddleware, x402ResourceServer } from "@x402/express";
import { ExactEvmScheme } from "@x402/evm/exact/server";
import { HTTPFacilitatorClient } from "@x402/core/server";
config();
const PAY_TO = process.env.PAY_TO_ADDRESS as `0x${string}`;
const FACILITATOR_URL = "https://x402.semanticpay.io/";
// --- Network config ---
const NETWORKS = {
plasma: {
network: "eip155:9745" as const,
usdt0: "0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb",
},
stable: {
network: "eip155:988" as const,
usdt0: "0x779Ded0c9e1022225f8E0630b35a9b54bE713736",
},
};
const PRICE = "1000"; // $0.001
function priceOnChain(chain: keyof typeof NETWORKS) {
return {
amount: PRICE,
asset: NETWORKS[chain].usdt0,
extra: { name: "USDT0", version: "1", decimals: 6 },
};
}
// --- Facilitator + resource server ---
const facilitatorClient = new HTTPFacilitatorClient({ url: FACILITATOR_URL });
const resourceServer = new x402ResourceServer(facilitatorClient)
.register(NETWORKS.plasma.network, new ExactEvmScheme())
.register(NETWORKS.stable.network, new ExactEvmScheme());
// --- Express app ---
const app = express();
app.use(
paymentMiddleware(
{
"GET /weather": {
accepts: [
{
scheme: "exact",
network: NETWORKS.plasma.network,
price: priceOnChain("plasma"),
payTo: PAY_TO,
},
{
scheme: "exact",
network: NETWORKS.stable.network,
price: priceOnChain("stable"),
payTo: PAY_TO,
},
],
description: "Weather data",
mimeType: "application/json",
},
},
resourceServer,
),
);
app.get("/weather", (req, res) => {
res.json({ weather: "sunny", temperature: 70 });
});
app.get("/health", (req, res) => {
res.json({ status: "ok" });
});
const port = process.env.PORT || 4021;
app.listen(port, () => {
console.log(`Server listening at http://localhost:${port}`);
});
```
## Route configuration
The first argument to `paymentMiddleware` maps routes to payment requirements. The key format is `METHOD /path`.
```typescript theme={null}
paymentMiddleware(
{
"GET /api/data": {
accepts: [
{
scheme: "exact",
network: "eip155:9745",
price: {
amount: "10000", // $0.01
asset: "0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb",
extra: { name: "USDT0", version: "1", decimals: 6 },
},
payTo: PAY_TO,
},
],
description: "Premium data feed",
mimeType: "application/json",
},
"POST /api/generate": {
accepts: [
{
scheme: "exact",
network: "eip155:9745",
price: {
amount: "50000", // $0.05
asset: "0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb",
extra: { name: "USDT0", version: "1", decimals: 6 },
},
payTo: PAY_TO,
},
],
description: "AI generation endpoint",
mimeType: "application/json",
},
},
resourceServer,
);
```
Routes not listed in the config are not gated — they behave like normal Express routes. This is why `/health` works without payment in the examples above.
## Lifecycle events
The Semantic facilitator supports an optional `X-Event-Callback` header on `/verify` and `/settle` requests. When provided, the facilitator POSTs real-time lifecycle events to that URL as verification and settlement happen. This is useful for building dashboards, logging pipelines, or payment flow visualizations.
Events are fire-and-forget and do not block the facilitator's response. If the callback URL is unreachable, events are silently dropped. If no header is provided, no events are sent.
### Event types
| Type | When | Key fields |
| ------------------ | ---------------------------------------------------- | ----------------------------------- |
| `verify_started` | Facilitator begins verifying the payment | `details.network`, `details.checks` |
| `verify_completed` | Verification finished | `details.isValid` |
| `verify_failed` | Verification threw an error | `details.error` |
| `settle_started` | Facilitator is broadcasting the on-chain transaction | `details.network` |
| `settle_completed` | Transaction confirmed on-chain | `details.transactionHash` |
| `settle_failed` | Settlement threw an error | `details.error` |
### Example: receiving events
Add a POST endpoint to your server:
```typescript theme={null}
app.post("/payment-events", (req, res) => {
const { type, title, details } = req.body;
console.log(`[${type}] ${title}`, details);
res.json({ ok: true });
});
```
Then configure the facilitator client to include the callback header. Since `HTTPFacilitatorClient` from `@x402/core` doesn't support custom headers directly, wrap fetch:
```typescript theme={null}
const CALLBACK_URL = "http://localhost:4021/payment-events";
const facilitatorClient = new HTTPFacilitatorClient({
url: FACILITATOR_URL,
fetch: (url, init) =>
fetch(url, {
...init,
headers: {
...init?.headers,
"X-Event-Callback": CALLBACK_URL,
},
}),
});
```
With this in place, every `/verify` and `/settle` call to the facilitator will include the callback header, and your `/payment-events` endpoint will receive events like:
```json theme={null}
{
"type": "settle_completed",
"step": 10,
"title": "Settlement Confirmed",
"description": "Payment transaction confirmed on blockchain",
"details": {
"success": true,
"transactionHash": "0xabc123...",
"network": "eip155:9745"
},
"actor": "blockchain",
"target": "facilitator"
}
```
For the full event reference, see the [Facilitator API docs](/endpoints).
## Environment variables
```bash theme={null}
# .env
PAY_TO_ADDRESS=0xYourReceivingAddress
PORT=4021
```
## Using with other frameworks
x402 also provides middleware for Hono and Next.js:
```bash theme={null}
# Hono
npm install @x402/hono
# Next.js
npm install @x402/next
```
The pattern is the same: create a facilitator client, register the EVM scheme for your network(s), and apply middleware. See the [x402 examples](https://github.com/coinbase/x402/tree/main/examples) for framework-specific code.
# Supported Chains
Source: https://docs.semanticpay.io/supported-chains
## Plasma
A Layer 1 blockchain backed by Bitfinex, designed as a dedicated settlement layer for USD₮. [Plasma](https://www.plasma.to/) uses PlasmaBFT consensus for sub-second block finality and a protocol-level Paymaster that sponsors direct USDT0 transfers with zero gas fees.
### Mainnet
| Parameter | Value |
| ------------------------------- | -------------------------------------- |
| **Network name** | Plasma Mainnet Beta |
| **Chain ID** | `9745` |
| **Network identifier (CAIP-2)** | `eip155:9745` |
| **Native token** | XPL |
| **RPC** | `https://rpc.plasma.to` |
| **WebSocket** | — |
| **Block explorer** | [plasmascan.to](https://plasmascan.to) |
| **Block time** | Sub-second |
| **EVM compatible** | Yes |
### Testnet
| Parameter | Value |
| ------------------ | ------------------------------------------------------ |
| **Network name** | Plasma Testnet |
| **Chain ID** | `9746` |
| **RPC** | `https://testnet-rpc.plasma.to` |
| **Block explorer** | [testnet.plasmascan.to](https://testnet.plasmascan.to) |
| **Faucet** | [gas.zip/faucet/plasma](https://gas.zip/faucet/plasma) |
### USDT0 on Plasma
| Contract | Address | |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------ | - |
| **USDT0 token** | [`0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb`](https://plasmascan.to/address/0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb) | |
| **OFT adapter** | [`0x02ca37966753bDdDf11216B73B16C1dE756A7CF9`](https://plasmascan.to/address/0x02ca37966753bDdDf11216B73B16C1dE756A7CF9) | |
| **Decimals** | 6 | |
| **LayerZero EID** | 30383 | |
### Links
* [Plasma docs](https://www.plasma.to/docs/plasma-chain/introduction/start-here)
* [Zero-fee USDT transfers](https://www.plasma.to/docs/plasma-chain/stablecoin-native-contracts/zero-fee-usdt-transfers)
* [Bridge via Stargate](https://stargate.finance/bridge?srcChain=ethereum\&srcToken=0xdAC17F958D2ee523a2206206994597C13D831ec7\&dstChain=plasma\&dstToken=0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb)
***
## Stable
A "stablechain" — a Layer 1 where USDT0 is the native gas and settlement token. [Stable](https://www.stable.xyz/) features sub-second block finality, full EVM compatibility, and native USDT0 transfers at the protocol level. Gas fees are paid in USDT0 directly.
### Mainnet
| Parameter | Value |
| ------------------------------- | ---------------------------------------- |
| **Network name** | Stable Mainnet |
| **Chain ID** | `988` |
| **Network identifier (CAIP-2)** | `eip155:988` |
| **Gas token** | USDT0 |
| **Governance token** | STABLE |
| **RPC** | `https://rpc.stable.xyz` |
| **WebSocket** | `wss://rpc.stable.xyz` |
| **Block explorer** | [stablescan.xyz](https://stablescan.xyz) |
| **Block time** | ≈0.7 seconds |
| **EVM compatible** | Yes |
### Testnet
| Parameter | Value |
| ------------------ | -------------------------------------------------------- |
| **Network name** | Stable Testnet |
| **Chain ID** | `2201` |
| **RPC** | `https://rpc.testnet.stable.xyz` |
| **WebSocket** | `wss://rpc.testnet.stable.xyz` |
| **Block explorer** | [testnet.stablescan.xyz](https://testnet.stablescan.xyz) |
| **Faucet** | [faucet.stable.xyz](https://faucet.stable.xyz) |
### USDT0 on Stable
| Contract | Address |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------- |
| **USDT0 token** | [`0x779Ded0c9e1022225f8E0630b35a9b54bE713736`](https://stablescan.xyz/address/0x779Ded0c9e1022225f8E0630b35a9b54bE713736) |
| **OFT adapter** | [`0xedaba024be4d87974d5aB11C6Dd586963CcCB027`](https://stablescan.xyz/address/0xedaba024be4d87974d5aB11C6Dd586963CcCB027) |
| **Decimals** | 6 |
| **LayerZero EID** | 30396 |
### Links
* [Stable docs](https://docs.stable.xyz/en/introduction/why-stable)
* [Mainnet information](https://docs.stable.xyz/en/developers/mainnet/mainnet-information)
* [Quick start](https://docs.stable.xyz/en/developers/quick-start)
***
## Funding your wallet
To use x402 on Plasma or Stable, you need USDT0 on the target chain. Bridge USDT from Ethereum or any supported chain using the [USDT0 bridge](https://usdt0.to/transfer).
1. Go to [usdt0.to/transfer](https://usdt0.to/transfer)
2. Select your source chain and USDT
3. Select **Plasma** or **Stable** as the destination
4. Enter the amount and confirm the transaction
USDT is converted to USDT0 automatically during the bridge. No separate swap needed.
***
## Quick reference
For copy-paste into your code:
```typescript theme={null}
import { WalletAccountEvm } from "@tetherto/wdk-wallet-evm";
// Plasma
export const PLASMA_CHAIN_ID = 9745;
export const PLASMA_NETWORK = "eip155:9745";
export const PLASMA_RPC = "https://rpc.plasma.to";
export const PLASMA_USDT0 = "0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb";
// Stable
export const STABLE_CHAIN_ID = 988;
export const STABLE_NETWORK = "eip155:988";
export const STABLE_RPC = "https://rpc.stable.xyz";
export const STABLE_USDT0 = "0x779Ded0c9e1022225f8E0630b35a9b54bE713736";
// Shared
export const USDT0_DECIMALS = 6;
// Plasma wallet
const plasmaAccount = new WalletAccountEvm(process.env.SEED_PHRASE, {
provider: PLASMA_RPC,
});
// Stable wallet
const stableAccount = new WalletAccountEvm(process.env.SEED_PHRASE, {
provider: STABLE_RPC,
});
// Check USDT0 balance on either chain
const plasmaBalance = await plasmaAccount.getTokenBalance(PLASMA_USDT0);
const stableBalance = await stableAccount.getTokenBalance(STABLE_USDT0);
console.log("Plasma USDT0:", Number(plasmaBalance) / 10 ** USDT0_DECIMALS);
console.log("Stable USDT0:", Number(stableBalance) / 10 ** USDT0_DECIMALS);
```
***
## Coming soon
### Solana
Solana support is coming soon. Semantic will support x402 payments settled on Solana using native USDT.
### Spark (Bitcoin)
Spark is a Layer 2 for Bitcoin that enables near-instant, fee-free BTC transactions via Spark invoices. Semantic will support x402 payments settled on Spark, bringing Bitcoin payments to the x402 protocol without on-chain fees or confirmation delays.
# USD₮ vs. USDC
Source: https://docs.semanticpay.io/usdt
The choice of stablecoin determines the cost, performance, custody model, and long-term viability of an x402 deployment. This page compares the two options available today and explains why Semantic builds on USD₮.
## Market position
**USD₮ holds roughly 60% of the stablecoin market** (\$186B market cap) with daily trading volume 5× that of USDC (\$40–200B vs \$5–40B). It's the default unit of account across global exchanges, P2P markets, and cross-border corridors. For x402, this means building on USD₮ gives agents access to the deepest liquidity and broadest acceptance available.
| Metric | USD₮ | USDC |
| -------------------- | ---------------- | ------------- |
| Market cap (Q3 2025) | ≈\$186B | ≈\$75B |
| Market share | ≈60% | ≈25% |
| Daily trading volume | \$40–200B | \$5–40B |
| Active users | 350M+ | Not disclosed |
| Blockchain presence | 20+ chains | 15+ chains |
| Primary corridors | Global, EM-heavy | US/EU |
## Settlement infrastructure
x402 payments settle on-chain. The choice of network directly affects finality speed, transaction cost, and fee predictability.
### CDP facilitator (USDC on Base and Solana)
Coinbase's CDP facilitator currently offers a [free tier of 1,000 transactions per month](https://docs.cdp.coinbase.com/x402/core-concepts/facilitator), after which each transaction costs \$0.001. The facilitator absorbs gas fees on the buyer's behalf.
This works for prototyping, but raises questions at scale. Coinbase subsidizes gas today, but subsidies have a known trajectory: attract usage, then reprice once lock-in occurs. Base and Solana are general-purpose chains where fees fluctuate with network demand, and gas must be paid in volatile tokens (ETH, SOL), adding operational complexity for agents that otherwise only need to manage stablecoins.
### Semantic facilitator (USD₮ on Plasma and Stable)
Semantic settles on [Plasma](https://www.plasma.to) and [Stable](https://stable.xyz): Layer 1 chains purpose-built for USD₮ transactions.
Both chains share a design principle. **Stablecoin transfers are the primary workload, not an afterthought.** This means:
* **Near-zero-fee USD₮ transfers.** are a protocol-level feature, not a temporary subsidy. The chains are economically designed around this use case.
* **No gas token friction.** Agents hold USD₮ and pay in USD₮ (or pay nothing at all for simple transfers).
* **Sub-second finality.** Enables real-time settlement for high-frequency agent interactions.
* **Predictable costs.** Because these chains are optimized for stablecoin throughput rather than general computation, fee behavior is more stable and less susceptible to congestion spikes from unrelated workloads.
## Wallet custody
x402 requires wallets on both sides of every payment. How those wallets manage private keys determines who actually controls the funds.
### CDP Server Wallets
Coinbase's [CDP Server Wallets](https://docs.cdp.coinbase.com/server-wallets/v2/introduction/welcome) store private keys inside Trusted Execution Environments (TEEs) on Coinbase infrastructure. All wallet operations, creation, signing, management, happen through Coinbase's API.
This means:
* **Keys live on Coinbase infrastructure.** The keys depend on Coinbase's infrastructure being available and uncompromised.
* **API dependency.** Every wallet operation requires a network call to Coinbase's API.
* **Platform coupling.** Wallets created through CDP exist within the CDP ecosystem.
### WDK (Wallet Development Kit) *by Tether*
Semantic integrates seamlessly with [WDK](https://docs.wallet.tether.io), an open-source wallet toolkit for self-custodial wallets. WDK generates and stores private keys locally. Keys never leave the local environment.
This means:
* **True self-custody.** No third party can freeze, seize, or block an agent's funds. The agent (or its operator) has sole control.
* **No infrastructure dependency.** WDK is an OS library, not a service. Wallets work offline for signing operations. No API calls to any third party.
* **Runs anywhere.** Desktop, server, mobile, embedded, same library, same keys, same behavior.
| | CDP Server Wallets | WDK |
| ----------------------------- | ------------------------------ | ------------------------------ |
| **Key storage** | Coinbase TEE | Local (device/server/agent) |
| **Key access** | Via Coinbase API | Direct, local |
| **Infrastructure dependency** | Coinbase API must be available | None (library) |
| **Open source** | No | Yes |
| **Vendor lock-in** | CDP ecosystem | None (BIP standard derivation) |
| **Freeze/seize risk** | Subject to Coinbase policies | None |
## The path to trustless infrastructure
Semantic's choice of USD₮, purpose-built chains, and self-custodial wallets is part of a broader architectural direction.
Today, x402 facilitators, including Semantic's, are centralized services. A server trusts the facilitator to verify and settle honestly. This works, and it's the practical starting point for any payment infrastructure. But it's not the end state.
Semantic will integrate with [QVAC](https://qvac.tether.io) as an agent runtime and [Hyperswarm](https://docs.pears.com/building-blocks/hyperswarm) as a transport layer, replacing HTTP servers with direct peer-to-peer transactions. Combined with self-custodial WDK wallets and on-chain settlement, this removes every centralized dependency from the payment flow: discovery, inference, signing, and settlement all happen without a third party.
**The vision is a fully peer-to-peer, trustless, permissionless agentic economy.**
# WDK
Source: https://docs.semanticpay.io/wdk
[WDK (Wallet Development Kit)](https://docs.wallet.tether.io/) *by Tether* is an open-source toolkit for building multi-chain, self-custodial wallets. It handles key derivation, signing, and chain interactions across EVM, Bitcoin, Solana, Spark, and more.
This guide covers using WDK with x402 as a buyer (paying for resources) and as a seller (accepting payments).
See a full working demo at [github.com/SemanticPay/x402-usdt0-demo](https://github.com/SemanticPay/x402-usdt0-demo)
## Buyer
`WalletAccountEvm` satisfies the `ClientEvmSigner` interface that x402 expects. No adapter needed.
```bash theme={null}
npm install @tetherto/wdk-wallet-evm @x402/fetch @x402/evm
```
```typescript theme={null}
import WalletManagerEvm from "@tetherto/wdk-wallet-evm";
import { x402Client, wrapFetchWithPayment } from "@x402/fetch";
import { registerExactEvmScheme } from "@x402/evm/exact/client";
const account = await new WalletManagerEvm(process.env.SEED_PHRASE, {
provider: "https://rpc.plasma.to",
}).getAccount();
const client = new x402Client();
registerExactEvmScheme(client, { signer: account });
const fetchWithPayment = wrapFetchWithPayment(fetch, client);
const response = await fetchWithPayment("https://api.example.com/data");
```
Keys are derived locally from the seed phrase and never leave your environment. See the [buyer quickstart](/client) for the full walkthrough.
## Seller
### Option 1: Hosted facilitator
Use Semantic's facilitator to handle verification and settlement. Your server never interacts with the chain directly.
* Automatic verification of buyer payment signatures
* No gas fees, no native token required in your wallet
* No transaction monitoring or retry logic
* No facilitator infrastructure to deploy
Derive a receiving address from your seed phrase:
```bash theme={null}
npm install @tetherto/wdk-wallet-evm @x402/express @x402/evm @x402/core
```
```typescript theme={null}
import WalletManagerEvm from "@tetherto/wdk-wallet-evm";
const account = await new WalletManagerEvm(process.env.SEED_PHRASE, {
provider: "https://rpc.plasma.to",
}).getAccount();
const sellerAddress = await account.getAddress();
```
Then wire up Express with `paymentMiddleware` pointing to Semantic's facilitator:
```typescript theme={null}
import express from "express";
import { paymentMiddleware, x402ResourceServer } from "@x402/express";
import { ExactEvmScheme } from "@x402/evm/exact/server";
import { HTTPFacilitatorClient } from "@x402/core/server";
const facilitatorClient = new HTTPFacilitatorClient({
url: "https://x402.semanticpay.io/",
});
const app = express();
app.use(
paymentMiddleware(
{
"GET /weather": {
accepts: [
{
scheme: "exact",
network: "eip155:9745", // Plasma mainnet
price: {
amount: "1000000", // $1.00 in base units (6 decimals)
asset: "0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb", // USDT0 contract on Plasma
extra: { name: "USDT0", version: "1", decimals: 6 }, // EIP-712 domain for signature
},
payTo: sellerAddress,
},
],
description: "Weather data",
mimeType: "application/json",
},
},
new x402ResourceServer(facilitatorClient).register(
"eip155:9745",
new ExactEvmScheme()
)
)
);
app.get("/weather", (req, res) => {
res.json({ weather: "sunny", temperature: 70 });
});
app.listen(4021);
```
See the [seller quickstart](/server) for the full walkthrough.
### Option 2: Self-hosted facilitator
Run your own facilitator for full control over verification and settlement. The `@semanticio/wdk-wallet-evm-x402-facilitator` module wraps a WDK wallet as an x402 `FacilitatorEvmSigner`.
```bash theme={null}
npm install @semanticio/wdk-wallet-evm-x402-facilitator @tetherto/wdk-wallet-evm @x402/core @x402/evm @x402/express
```
#### 1. Create the facilitator signer
```typescript theme={null}
import WalletManagerEvm from "@tetherto/wdk-wallet-evm";
import WalletAccountEvmX402Facilitator from "@semanticio/wdk-wallet-evm-x402-facilitator";
const walletAccount = await new WalletManagerEvm(process.env.MNEMONIC, {
provider: "https://rpc.plasma.to",
}).getAccount();
const evmSigner = new WalletAccountEvmX402Facilitator(walletAccount);
```
#### 2. Initialize the facilitator
Register the signer with an `x402Facilitator` instance. Lifecycle hooks are optional but useful for logging:
```typescript theme={null}
import { x402Facilitator } from "@x402/core/facilitator";
import { registerExactEvmScheme } from "@x402/evm/exact/facilitator";
const facilitator = new x402Facilitator()
.onAfterVerify(async (ctx) => {
console.log(`[verify] valid=${ctx.result?.isValid}`);
})
.onAfterSettle(async (ctx) => {
console.log(`[settle] tx=${ctx.result?.transaction}`);
});
registerExactEvmScheme(facilitator, {
signer: evmSigner,
networks: "eip155:9745",
});
```
#### 3. Wire into Express
Same `paymentMiddleware` pattern as the hosted facilitator, but instead of an `HTTPFacilitatorClient`, pass the in-process `facilitator` directly to `x402ResourceServer`. Verification and settlement happen locally.
For a complete working example, see [`server.js`](https://github.com/baghdadgherras/x402-usdt0/blob/main/x402/server.js).
`@semanticio/wdk-wallet-evm-x402-facilitator` is currently in beta. Test thoroughly before using in production.
## Summary
| Role | Package | Adapter |
| ------------------------ | --------------------------------------------- | -------------------------------------------------------------- |
| **Buyer** | `@tetherto/wdk-wallet-evm` | None. `WalletAccountEvm` satisfies `ClientEvmSigner` directly. |
| **Seller** (hosted) | - | None. Use your address and point to Semantic's facilitator. |
| **Seller** (self-hosted) | `@semanticio/wdk-wallet-evm-x402-facilitator` | Wraps `WalletAccountEvm` as `FacilitatorEvmSigner`. |
## Next steps
* [Buyer quickstart](/client)
* [Seller quickstart](/server)
* [Supported chains](/supported-chains)
* [WDK documentation](https://docs.wallet.tether.io)
* [Facilitator module on GitHub](https://github.com/SemanticPay/wdk-wallet-evm-x402-facilitator)
# x402
Source: https://docs.semanticpay.io/x402
x402 is an open payment protocol that enables instant, programmatic stablecoin payments directly over HTTP, turning every API into a payable endpoint.
# What is x402?
x402 is an open payment protocol, [originally developed by Coinbase](https://docs.cdp.coinbase.com/x402/welcome), that enables instant, programmatic stablecoin payments directly over HTTP.
It revives the long-reserved [HTTP 402 Payment Required](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/402) status code and gives it a concrete, blockchain-native meaning: if you want this resource, pay for it, right now, in this request.
No accounts. No API keys. No checkout flows. Just HTTP.
x402 picks up where HTTP left off. It makes payment a first-class citizen of the web, which is especially critical now that AI agents need to pay for resources programmatically.
Clients don't need to register, authenticate, or manage sessions. A wallet is the only credential.
Works with existing web infrastructure. Standard headers, status codes, and request-response.
AI agents can discover payment requirements and settle them autonomously.
Supports multiple networks through facilitators. EVM, Solana, and more, identified by [CAIP-2 standards](https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/caip-2.md).
## How it works
The x402 flow is a simple extension of HTTP. A client requests a resource, the server says "pay me," the client pays, and the server delivers.
```mermaid theme={null}
sequenceDiagram
participant Client as Client (Buyer)
participant Server as Resource Server
participant Facilitator as Facilitator
participant Chain as Blockchain
Client->>Server: 1. GET /api/resource
Server-->>Client: 2. 402 Payment Required + payment details
Note over Client: 3. Sign payment with wallet
Client->>Server: 4. GET /api/resource + X-PAYMENT header
Server->>Facilitator: 5. POST /verify (payment payload)
Facilitator-->>Server: 6. Valid ✓
Note over Server: 7. Perform the task (generate response, run inference, fetch data...)
Server->>Facilitator: 8. POST /settle (payment payload)
Facilitator->>Chain: 9. Submit transaction
Chain-->>Facilitator: 10. Confirmed ✓
Facilitator-->>Server: 11. Settlement receipt
Server-->>Client: 12. 200 OK + resource + X-PAYMENT-RESPONSE
```
### Step by step
The client sends a standard HTTP request.
If payment is required, the server returns `402 Payment Required` with a JSON body describing what to pay: amount, token, network, and the recipient address.
```json theme={null}
{
"x402Version": 1,
"accepts": [{
"scheme": "exact",
"network": "eip155:9745",
"maxAmountRequired": "1000000",
"asset": "0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb",
"resource": "https://api.example.com/data",
"payTo": "0x1234...abcd"
}]
}
```
The client reads the requirements, constructs an ERC-3009 payment authorization, and signs it with their wallet. No tokens leave the wallet yet. It's a signed intent, not a transfer. The client retries the same request with the signed payload in the `X-PAYMENT` header.
The server forwards the payment payload to the facilitator's `/verify` endpoint. The facilitator checks that the signature is valid, the amount is sufficient, and the payer has enough funds. If everything checks out, it returns valid. No money has moved yet.
Now that payment is verified, the server does the actual work (running inference, querying a database, generating a report, whatever the resource requires).
After the task is complete, the server calls the facilitator's `/settle` endpoint. The facilitator submits the signed authorization on-chain, transferring tokens from the client to the seller. Once the blockchain confirms the transaction, the facilitator returns a settlement receipt.
The server returns `200 OK` with the requested resource in the body and a settlement receipt in the `X-PAYMENT-RESPONSE` header. From the client's perspective, this is a single request-response cycle.
The key insight: **verify before doing work, settle after**. The server never spends resources on unverified requests, and the client's funds only move after the task is complete.
## The three roles
The entity requesting a paid resource. Can be a human application, an AI agent, or any programmatic service with a wallet. The client's only job is to read payment requirements from a `402` response, sign a payment, and resubmit.
The service providing the paid resource (an API, a dataset, a tool). The server defines payment requirements (amount, token, network) and returns `402` for unpaid requests. It delegates verification and settlement to a facilitator.
An intermediary service that verifies payment signatures and submits transactions on-chain. The facilitator never holds funds. it executes signed authorizations. It exposes two endpoints: `/verify` (is this payment valid?) and `/settle` (submit it on-chain).
Semantic operates a public facilitator at `https://x402.semanticpay.io` supporting USD₮0 on Plasma and Stable chains.
## x402 is an open standard
x402 is not proprietary to any single provider. Anyone can build a client, a resource server, or a facilitator. The protocol is defined by its HTTP semantics and payment payload format, not by a specific SDK or platform.
**Semantic operates the first USD₮-enabled x402 facilitator, bringing the world's most liquid stablecoin to the protocol**.
For the full protocol specification and reference implementations, visit [x402.org](https://www.x402.org) and the [x402 GitHub repository](https://github.com/coinbase/x402).
## Next steps
Learn about Tether's Wallet Development Kit that powers Semantic's facilitator
Understand why we chose USD₮ and stablecoin-optimized chains
Get your first payment working in 5 minutes
Explore the facilitator endpoints