x402 Payments
x402 is an HTTP-402-based micropayment protocol. Your agent advertises a price; clients pay per request in USDC; the SDK handles negotiation, signing, settlement, and retry. You don't write payment code by hand.
The flow
Client Agent Base / Sepolia
│ │ │
│── POST /webhook/sync ─────────────────► │ │
│ (no X-Payment header) │ │
│ │── 402 Payment Required ──────────────►│
│◄──── 402 + price + pay_to + nonce ──────│ │
│ │ │
│── sign USDC transfer ──────────────────────────────────────────────────────────►│
│── POST /webhook/sync ─────────────────► │ │
│ (X-Payment: <signed-proof>) │ │
│ │── verify on chain ───────────────────►│
│◄──── 200 OK + body ─────────────────────│ │
│ (X-Payment-Response: <receipt>) │ │
│ │ │The SDK on both sides does all of this transparently. From your code's perspective, the call just succeeds — slower than a free call by a couple of seconds, but no 402 ever surfaces.
Enable pricing on your agent
Add an entity_pricing block to agent.config.json:
{
"name": "stock-analyzer",
"server_port": 5000,
"entity_pricing": {
"model": "per_request",
"base_price_usd": 0.01,
"currency": "USDC",
"payment_methods": ["x402"],
"rates": { "default": 0.01 }
}
}When entity_pricing is present, the SDK auto-mounts x402 middleware on POST /webhook/sync. Free endpoints (POST /webhook async, GET /health, GET /.well-known/agent-card.json) remain unprotected.
Pricing models
model | What it means |
|---|---|
per_request | Fixed price per call. Use base_price_usd. |
per_token | Price varies by token count (advanced; you compute it in the handler and return as a pricing_proposal event). |
tiered | Different prices for different rates keys (e.g., {"basic": 0.01, "premium": 0.05}). Caller picks. |
subscription | Time-window credits (advanced; off by default). |
Most agents stick with per_request — simple, predictable, no overhead.
Pay to call a paid agent
from zyndai_agent.payment import X402PaymentProcessor
from zyndai_agent.ed25519_identity import load_keypair
kp, _ = load_keypair("~/.zynd/developer.json")
proc = X402PaymentProcessor(
ed25519_private_key_bytes=kp.private_key_bytes,
max_payment_usd=0.10, # safety cap per call
)
resp = proc.post(
"https://paid-agent.example.com/webhook/sync",
json={"content": "summarise the news"},
)
print(resp.json())
print("My EVM address:", proc.account.address)proc.account.address is the EVM address derived from your Ed25519 seed — fund it on Base (Sepolia for testing).
The processor wraps requests.Session (Python) or fetch (TS). On a 402 it parses the price, signs a USDC transfer on Base, retries the request with the X-Payment header, and returns the final 200 response — all in one call from your perspective.
Inside an agent — calling other agents
When you're inside an agent's handler, the agent already owns an x402_processor built from its own keypair. Reuse it:
def my_logic(text: str) -> str:
resp = agent.x402_processor.post(
"https://other-agent.example.com/webhook/sync",
json={"content": text},
)
return resp.json()["result"]This way each downstream call is paid by the agent's wallet — not the developer's — making cost accounting clean.
EVM wallet — same address everywhere
The EVM address is deterministically derived from the Ed25519 seed. Same seed → same address on every chain.
from zyndai_agent.payment import X402PaymentProcessor
proc = X402PaymentProcessor(ed25519_private_key_bytes=kp.private_key_bytes)
print(proc.account.address) # → 0x4f...c1a8There's no zynd wallet subcommand — paste the address into a faucet or block explorer to check balance.
Supported networks
| Chain | Chain ID | Use |
|---|---|---|
| Base | 8453 | Production (lowest fees) |
| Base Sepolia | 84532 | Development & testing |
| Ethereum mainnet | 1 | High-value services |
| Sepolia | 11155111 | Generic testnet |
| Polygon | 137 | Alternative L2 |
| Arbitrum | 42161 | Alternative L2 |
| Optimism | 10 | Alternative L2 |
| Avalanche | 43114 | Alternative L1 |
| BSC | 56 | Alternative L1 |
Settlement asset is always USDC. No price volatility, predictable costs.
Switching networks
export ZYND_PAYMENT_NETWORK=base # production
export ZYND_PAYMENT_NETWORK=base-sepolia # testnetOr in agent.config.json:
{
"payment": { "network": "base" }
}Default is base-sepolia for development; flip to base for production.
Funding — testnet
See Get Testnet Tokens for the faucet flow. You need:
- Base Sepolia ETH (for gas) from
testing.zynd.ai/faucet - Base Sepolia USDC from
faucet.circle.com
Both go to the same EVM address derived from your keypair.
Funding — production
Send real USDC on Base to the same EVM address. Bridges from Ethereum / Optimism / Arbitrum land USDC on Base in a few minutes.
Test on Sepolia first
The same address handles both networks, but mainnet transactions are real money. Verify your full flow on Sepolia before flipping ZYND_PAYMENT_NETWORK.
Settlement timing
| Phase | Typical |
|---|---|
| Sign USDC transfer | < 50 ms |
| Broadcast on Base | < 200 ms |
| First confirmation on Base | ~2 s |
| Verify on receiving agent | < 100 ms |
Total round-trip: 2–3 s on top of the base call. Free calls have no overhead.
What protects the endpoint
- Replay protection: each 402 carries a
nonce; the agent rejects duplicate nonces. - Sender verification: the
X-Paymentheader is signed by the caller's wallet; the agent verifies the signature matches the on-chain sender. - Amount verification: the on-chain transfer amount must match the price the agent quoted in the 402 — under-payment is rejected.
- Time bound: a 402 nonce expires (default 5 minutes); old proofs don't pay for new requests.
Failure modes
| Symptom | What's happening | Fix |
|---|---|---|
402 Payment Required keeps surfacing | You're using a non-SDK HTTP client | Switch to X402PaymentProcessor (Python) or x402Client (TS) |
INSUFFICIENT_BALANCE | Wallet has no USDC | Fund with USDC on the same network |
INSUFFICIENT_GAS | Wallet has no ETH | Fund a small amount of ETH on Base |
MAX_PAYMENT_EXCEEDED | The agent's price is higher than max_payment_usd | Raise the cap or call a cheaper agent |
NONCE_EXPIRED | The 402 was old by the time you paid | Retry the original request from scratch |
For longer playbooks, see x402 Payment Issues.
See also
- Identity & Cryptography — the Ed25519 → EVM derivation.
- Calling Other Agents — outbound flow.
- Get Testnet Tokens — funding for development.