# x402 (/integrations/pay-per-use/x402)

<!-- agent-signals: reading_time_min: 5 · est_tokens: 2076 · updated: 2026-09-06 -->
Related: [MPP](/integrations/pay-per-use/mpp.md), [Marketplaces and provisioning](/integrations/marketplaces.md)



# Pay for AgentMail per request over x402

x402 is an open protocol for HTTP payments. An agent with a funded wallet pays for each AgentMail API call with a signed USDC payment, so it needs no account, no signup, and no API key. Use this when the agent holds USDC on Base, Polygon, Avalanche C-Chain, X Layer, or Solana.

## Do this

Requires Node.js 18 or later and a wallet holding USDC on a supported network. The inbox create below is the one paid request, at $2.00 in USDC. Every other call here is free.

```bash
npm install agentmail @x402/fetch @x402/evm viem
export EVM_PRIVATE_KEY="0x..."
```

```typescript
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));

// Selects the x402 hosts automatically. Leave baseUrl and environment unset.
const client = new AgentMailClient({ x402 });

// Free call. Proves the whole loop: 402 challenge, signed, retried.
const inboxes = await client.inboxes.list();
console.log(inboxes); // { count: 0, inboxes: [] } on a first run

// Paid call, $2.00 USDC. The default cap refuses payments above $1, raise it first.
x402.setSpendControls({ maxAmountPerPayment: "$2" });
const inbox = await client.inboxes.create({ username: "wallet-agent" });
console.log(inbox.inboxId);

// Free connection, signed by the same wallet.
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, "Subject:", event.message.subject);
  }
});
socket.sendSubscribe({ type: "subscribe", inboxIds: [inbox.inboxId] });
```

Read any endpoint's price before paying. Send a request with no payment and decode the base64 `payment-required` header on the `402` response:

```bash
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 -d
```

## SDK

TypeScript only. Install for the wallet's chain:

```bash
npm install agentmail @x402/fetch @x402/evm viem                      # EVM
npm install agentmail @x402/fetch @x402/svm @solana/kit @scure/base   # Solana
```

* Register an EVM signer: `x402.register("eip155:*", new ExactEvmScheme(signer))` with a signer from `privateKeyToAccount` in `viem/accounts`.
* Register a Solana signer: `x402.register("solana:*", new ExactSvmScheme(toClientSvmSigner(keypair)))` with a keypair from `createKeyPairSignerFromBytes(base58.decode(...))`.
* Pay per request: `new AgentMailClient({ x402 })`.
* Raise the payment cap: `x402.setSpendControls({ maxAmountPerPayment: "$2" })`.
* List inboxes, free: `client.inboxes.list()`.
* Create an inbox, $2.00: `client.inboxes.create({ username: "wallet-agent" })`.
* Receive events, free: `client.websockets.connect()`, then `socket.sendSubscribe({ type: "subscribe", inboxIds: [...] })`.

More SDK surface: `/integrations/sdks-and-cli`.

## Facts

* API host `https://x402.api.agentmail.to`, WebSocket host `wss://x402.ws.agentmail.to`. Paths are the regular AgentMail API paths starting with `/v0`.
* The wallet is the credential. Every request, free ones included, carries a wallet signature, and the signature determines which inboxes the caller sees and owns.
* Supported networks: Base, Polygon, Avalanche C-Chain, and X Layer with an EVM wallet, Solana with a Solana wallet. All payments are in USDC.
* Creating an inbox costs $2.00 in USDC. Listing inboxes, WebSocket connections, and unpaid price probes are free.
* The x402 client refuses to sign any payment above $1 by default. `x402.setSpendControls({ maxAmountPerPayment: "$2" })` must run before a $2.00 inbox create can succeed.
* A request with no payment attached returns `402` with a base64-encoded JSON `payment-required` header. `accepts` lists one entry per supported network, all quoting the same price.
* `amount` in a challenge is USDC atomic units with 6 decimals, so `2000000` is $2.00. Free endpoints quote `"0"`. The challenge at request time is the authoritative price.
* Challenge values on Base (`eip155:8453`): `x402Version` `2`, `scheme` `"exact"`, `asset` `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913`, `payTo` `0x6e3184C204e596dED89E8A5693B602097F4Ab687`, `maxTimeoutSeconds` `300`.
* The wallet that pays `POST /v0/inboxes` owns the inbox. Every later request on that inbox must be signed by the same private key. `GET /v0/inboxes` lists only inboxes the calling wallet created.
* Payment is verified before AgentMail processes the request. A request whose payment fails verification is never processed.
* On Solana the payment transaction is simulated on chain even at zero amount, so the wallet's USDC account must exist before the first call. An EVM wallet can make free calls before it is funded.
* `username` on `POST /v0/inboxes` is optional and first come, first served. Omitting `username` always works, AgentMail generates one.

## Not supported

* No API keys and no accounts on the x402 hosts. The regular `api.agentmail.to` host authenticates with API keys and answers wallet-paid requests with `401` instead of a payment challenge.
* Do not set `baseUrl` or `environment` when passing `{ x402 }` to `AgentMailClient`. The client picks the x402 hosts itself.
* Tempo wallets cannot pay over x402. Pay over MPP instead: `/integrations/pay-per-use/mpp`.
* The direction is agent pays AgentMail. This page defines no flow where AgentMail or an inbox pays the agent's wallet.
* The paying client is TypeScript on Node.js 18 or later. The page shows no client in any other language.

## Errors

| Error                                              | Status | Cause                                                                                                         | Fix                                                                 |
| -------------------------------------------------- | ------ | ------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
| `invalid_exact_evm_insufficient_balance`           | 402    | Payment failed verification, the wallet holds no USDC on the selected network. The request was not processed. | Fund the wallet, then retry.                                        |
| `transaction_simulation_failed`                    | -      | The Solana wallet's USDC account does not exist yet.                                                          | Create and fund the wallet's USDC account before the first call.    |
| `resource_taken`                                   | 403    | The `username` is owned by a different organization.                                                          | Pick one of the up to 3 names in `suggestions`, or omit `username`. |
| `already_exists`                                   | 403    | The `username` is already owned by the calling wallet's own organization.                                     | Pick one of the up to 3 names in `suggestions`, or omit `username`. |
| `This wallet does not own the requested resource.` | 403    | The request names an inbox owned by another wallet.                                                           | Sign with the private key that created the inbox.                   |

## Verify

Run the free list call with the paying client:

```typescript
console.log(await client.inboxes.list());
```

Expect `{ "count": 0, "inboxes": [] }` on a first run. Any successful response proves the loop: the host answered with a zero-amount `402` challenge and the client signed it and retried.

## Related

* `/integrations/pay-per-use/mpp` - pay per request from a Tempo wallet instead.
* `/core/send` - send from the inbox the wallet paid for.
* `/advanced/websockets` - every event type the socket can stream and how to subscribe to more scopes.
* `/quickstart` - authenticate with an API key and pay through a plan instead.
