x402
Pay for AgentMail per request from a crypto wallet, with no account or API key.
Your agent can pay for AgentMail per request over x402, an open protocol for HTTP payments. The wallet is the credential: a signed USDC payment authenticates each request, so the agent needs no account, no signup, and no API key.
Paid requests use the regular AgentMail API paths, starting with /v0, on dedicated hosts:
| Transport | Host |
|---|---|
| API | https://x402.api.agentmail.to |
| WebSocket | wss://x402.ws.agentmail.to |
Pick this path when your agent already holds a funded wallet. If your application has an AgentMail API key, follow the Quickstart and pay through a plan instead. If your agent’s wallet is on Tempo, pay over MPP.
0. Fund a wallet
You need Node.js 18 or later, a wallet private key, and USDC on one of the supported networks:
- Base, Polygon, Avalanche C-Chain, or X Layer with an EVM wallet
- Solana with a Solana wallet
This walkthrough makes one paid request: creating an inbox costs $2.00 in USDC. Every other call on this page is free.
Put the private key in an environment variable so it stays out of your code:
export EVM_PRIVATE_KEY="0x..."1. Create a paying client
Install the AgentMail SDK and the x402 client packages for your chain:
npm install agentmail @x402/fetch @x402/evm viemCreate an x402 client, register the payment scheme for your chain, and pass it to AgentMailClient. The client selects the x402 hosts automatically. Then make a free call to check that everything works: list your inboxes.
You should get back the list of inboxes this wallet owns. On a first run it is empty.
import { privateKeyToAccount } from "viem/accounts";
import { x402Client } from "@x402/fetch";
import { ExactEvmScheme } from "@x402/evm/exact/client";
import { AgentMailClient } from "agentmail";
const signer = privateKeyToAccount(process.env.EVM_PRIVATE_KEY as `0x${string}`);
const x402 = new x402Client();
x402.register("eip155:*", new ExactEvmScheme(signer));
const client = new AgentMailClient({ x402 });
const inboxes = await client.inboxes.list();
console.log(inboxes);{
"count": 0,
"inboxes": []
}Listing is free, but the response still proves the whole payment loop ran: the host answered with a 402 Payment Required challenge quoting a zero-amount payment, and the client signed it and retried. That signature is how AgentMail knows which wallet is calling, which is why the list contains only inboxes your wallet created.
2. Test creating an inbox with a paid request
The price is $2.00 in USDC today, and step 4 shows how to read the current price of any endpoint before you pay it.
By default the x402 client refuses to sign any payment above $1. Raise its per-payment cap to cover the price, then create the inbox. Pick your own username, or omit it and AgentMail generates one.
You should get back the new address. That means the wallet paid and the inbox exists.
x402.setSpendControls({ maxAmountPerPayment: "$2" });
const inbox = await client.inboxes.create({
username: "wallet-agent",
});
console.log(inbox.inboxId);This is the same loop as step 1 at a real price: the host answered the create with a 402 challenge, the client signed a $2.00 USDC payment from your wallet, and the request was retried with the payment attached. The payment is verified before AgentMail processes the request.
{
"organization_id": "7c9e4d2a-1f3b-4e5c-9a8d-6b7f2c1e0d4a",
"pod_id": "7c9e4d2a-1f3b-4e5c-9a8d-6b7f2c1e0d4a",
"inbox_id": "wallet-agent@agentmail.to",
"email": "wallet-agent@agentmail.to",
"created_at": "2026-08-25T10:31:47Z",
"updated_at": "2026-08-25T10:31:47Z"
}Save the inbox_id. You will subscribe to it in the next step. The wallet that paid for the create owns the inbox, so every later request that acts on it must be signed by the same key.
Usernames are first come, first served, and omitting username always works.
3. Test receiving events over the WebSocket
Subscribe to the inbox you created, then send it an email from your personal account.
You should see a subscribed event naming your inbox first, then a message.received event carrying the email you sent.
const socket = await client.websockets.connect();
socket.on("message", (event) => {
if (event.type === "subscribed") {
console.log("Subscribed to", event.inboxIds);
}
if (event.type === "event" && event.eventType === "message.received") {
console.log("From:", event.message.from);
console.log("Subject:", event.message.subject);
}
});
socket.sendSubscribe({
type: "subscribe",
inboxIds: [inbox.inboxId],
});Connecting is free. connect() fetches a zero-amount challenge from the WebSocket host, signs it with the same wallet, and attaches the credentials to the connection. Keep the process running while you wait. The email normally arrives within a couple of seconds of sending.
4. Check a price before you pay
Every endpoint quotes its own price. Send a request with no payment attached and decode the payment-required header on the 402 response, which is base64-encoded JSON:
curl -s -o /dev/null -D - -X POST https://x402.api.agentmail.to/v0/inboxes \
| grep -i "^payment-required:" \
| cut -d " " -f 2 \
| tr -d "\r" \
| base64 -dThe challenge lists one entry per supported network under accepts, each quoting the same price:
{
"x402Version": 2,
"error": "Payment required",
"accepts": [
{
"scheme": "exact",
"network": "eip155:8453",
"amount": "2000000",
"asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"payTo": "0x6e3184C204e596dED89E8A5693B602097F4Ab687",
"maxTimeoutSeconds": 300
}
]
}amount is the price in USDC atomic units with six decimals, so 2000000 is $2.00. The challenge is the authoritative price: what it quotes at request time is what the client pays. Use it to decide the spend cap you set in step 2.
Free endpoints answer the same way with an amount of "0". Probe GET /v0/inboxes with the same pipeline to see one.