x402 · USDC on Base + Solana

x402-print-mail

Give an agent an address and a paragraph and it puts paper in someone's mailbox — the proof PDF comes back in the same response.

git clone https://github.com/nirholas/x402-print-mail
cd x402-print-mail && npm install
npm run dev   # http://localhost:4024

Pricing

RoutePriceWhat you get back
POST /letters$0.05`{mailId, previewPdfBase64, expectedDelivery}` plus page count, carrier, mail class, and the addresses as printed
POST /postcards$0.03`{mailId, previewPdfBase64, expectedDelivery}` for a two-page proof: front artwork, then back with address block and postage box
GET /healthfreeLiveness, data source, configured rails
GET /.well-known/x402freeMachine-readable discovery manifest

Every paid route returns the purchased artifact in the 200 body — nothing is deferred to a webhook or a second fetch.

How a paid call works

1 · RequestAgent calls POST /letters
2 · 402Server quotes the price on both rails
3 · PayClient signs on its rail, retries with X-PAYMENT
4 · 200Artifact in the body + settlement receipt header

Two rails, one challenge

Pay in USDC on Base or Solana — your client picks. The 402 body always lists both.

EVM · Base

USDC on base-sepolia (or base). The wallet signs an EIP-3009 authorization locally — no server round-trip.

0x40252CFDF8B20Ed757D61ff157719F33Ec332402

Solana

USDC SPL transfer on solana. A facilitator sponsor pays the SOL network fee, so buyers need only USDC. Helpers at /api/x402-checkout build and wrap the transaction.

WwwuGbqHrwF5RG89KhUbmRWEvjnRH9k5kVM5p7T3WwW

Quickstart for agents

import { writeFileSync } from "node:fs";
import { privateKeyToAccount } from "viem/accounts";
import { wrapFetchWithPayment } from "x402-fetch";

const payFetch = wrapFetchWithPayment(fetch, privateKeyToAccount(process.env.PRIVATE_KEY));

// Send a letter — $0.05
const letter = await (await payFetch("http://localhost:4024/letters", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ to, from, body: "Dear Dana,\n\nYour order shipped today.\n\nSincerely,\nAcme" }),
})).json();

writeFileSync("letter.pdf", Buffer.from(letter.previewPdfBase64, "base64"));
console.log(letter.mailId, "arrives by", letter.expectedDelivery);

Live printing runs through [Lob](https://lob.com) when `LOB_API_KEY` is set. Without it, the service typesets and renders the proof itself — a genuine, paginated PDF with the address block, body copy and a footer marking it a preview — and labels every response `"source": "fixture"` with `"testMode": true`. Expected delivery dates use the same USPS First-Class business-day math Lob quotes. Nothing is printed or mailed in either mode unless you supply a **live** Lob key.

Links