x402 Suite · infrastructure

x402-receipts

Receipts and accounting for agent spending. Turn a bare settlement id — a Base transaction hash or a Solana transaction signature — into a signed receipt an accountant will accept, and export the whole book as CSV.

Pay in USDC on Base or USDC on Solana Resolves both chains
npm install x402-receipts

The problem

An x402 payment succeeds and your agent gets this back:

X-PAYMENT-RESPONSE: { "success": true, "network": "base-sepolia", "transaction": "0x33b7dd08…" }

That is not a receipt. No amount, no counterparty, no timestamp, no idea what was bought. At month end you have a few thousand of them across two chains and a finance function that would like a CSV.

Pricing

RoutePriceWhat you get back
GET /receipt/:tx$0.001Enriched, signed receipt: payer, payee, amount, asset, block time, fee, every transfer, your metadata
GET /export$0.01The full ledger as CSV and JSON in one response, with per-rail and per-merchant totals
POST /annotate/:txfree*Attach resource / merchant / label / category
GET /summaryfreeTotals without the rows — decide whether the export is worth buying
POST /verifyfreeSignature check on any receipt or export

*requires X-Admin-Key. The export returns both formats deliberately: CSV is what accounting software imports, JSON is what the next agent parses. Charging twice for the same rows would be a worse product.

Two chains, one lookup

The identifier's shape picks the chain. No flag, no guessing:

# Base: 0x + 64 hex
curl "localhost:4033/receipt/0x33b7dd081487769518a62d6da79f1e9f2872c02253b0981ac6317fc0c51c65e2"

# Solana: base58 signature
curl "localhost:4033/receipt/qMvBwBSdfWTAPyDZi1BDk3Nu68HEVAsz9aMexw3XT7rLobYmpLSg1BBW9Bgvpt1e7A8NUjcuSjAZ5aoYF8cARjj"

Both come back in the same shape. Getting there takes genuinely different work:

Base

Read the transaction receipt and decode every Transfer(address,address,uint256) log. The event states sender, recipient and amount directly.

Solana

No transfer event exists. Diff preTokenBalances against postTokenBalances per (owner, mint) — negative deltas are senders, positive ones receivers. Survives transferChecked, CPI wrappers and multi-hop routes, where instruction parsing does not.

One Solana subtlety: on x402 payments the fee payer is the facilitator's sponsor, not the buyer — that is what lets a buyer pay with USDC and no SOL. Read payer from the transfer, never the fee payer.

Use it as a library

import { resolveTransaction, ReceiptLedger, toCsv } from "x402-receipts";

const ledger = new ReceiptLedger({ file: "data/receipts.json" });

const receipt = await resolveTransaction("0x33b7dd08…");     // Base
const solana  = await resolveTransaction("qMvBwBSd…");       // Solana

ledger.record(receipt, { resource: "https://api.example.com/report", category: "data" });
ledger.record(solana,  { resource: "https://api.example.com/report", category: "data" });

ledger.summary();
// { count: 2, totalUsd: 0.02, byRail: { evm: {…}, solana: {…} }, … }

toCsv(ledger.list({ since: "2026-08-01" }));

The flow

agent GET /receipt/… 402 accepts[2] + X-PAYMENT verify+settle then resolve Base RPC Solana RPC receipt

Pleasingly circular: the transaction in your X-PAYMENT-RESPONSE is exactly what you would feed back into GET /receipt/:tx.

No keys, no fixtures

Every receipt comes from a live chain read through public, keyless RPC — Base via viem, Solana via api.mainnet-beta.solana.com. Set BASE_RPC_URL / SOLANA_RPC_URL to a dedicated provider before you depend on it; the public endpoints are rate-limited.

Docs

Tutorial →

Run it, resolve a real Base tx and a real Solana signature, buy the export.

API reference →

Every route and every library export, with schemas and error cases.

For AI agents →

Discovery, both rails, MCP integration, and where to get listed.

skill.md →

The agent-facing contract in one file.

.well-known/x402 →

Machine-readable manifest for x402 indexes.

OpenAPI 3.1 →

Including the 402 response and PaymentRequirements schema.