All articles
solanax402paymentsusdc

x402: HTTP 402 reborn — internet-native payments, on Solana

x402 makes the unused HTTP 402 status code into an actual payment protocol. Here's the request/response flow, the headers, and how Solana settles the payment.

Share
devrels.xyz/a/77short link

HTTP 402 has existed in the spec since 1997 with the description "reserved for future use." 28 years later, x402 (a Coinbase-published open spec) finally gives it a use: internet-native, on-chain settlement for API access.

It's simple, payment-agnostic at the spec level, and works cleanly on Solana with USDC. Here's the wire-level flow.

The flow

text
Client → Server   GET /paid-endpoint
Server → Client   HTTP/1.1 402 Payment Required
                  X-Payment-Required: {
                    "scheme":  "exact",
                    "network": "solana-mainnet",
                    "asset":   "USDC",
                    "amount":  "0.01",
                    "payTo":   "<merchant-solana-address>",
                    "resource":"/paid-endpoint",
                    "nonce":   "<server-issued>",
                    "expires": "<unix-ts>"
                  }

Client signs + submits a payment tx to Solana
Client → Server   GET /paid-endpoint
                  X-Payment: {
                    "scheme":  "exact",
                    "txid":    "<solana-signature>",
                    "nonce":   "<from-above>"
                  }

Server verifies on Solana that the tx matches the requirement
Server → Client   HTTP/1.1 200 OK
                  <the actual content>

Two HTTP round-trips, one Solana tx. The endpoint becomes permissionless — no API keys, no signup, just "pay $0.01 and you get the response."

The headers spec

Two headers do all the work:

  • X-Payment-Required — sent by the server in the 402 response. JSON-encoded payment requirement (scheme, network, asset, amount, recipient, nonce, expiry).
  • X-Payment — sent by the client in the retry request. JSON-encoded payment proof (scheme, txid, nonce).

Schemes are the extension point. "exact" means "a transaction transferring exactly this asset and amount to this address." Future schemes could be upTo (subscription-style), streaming (per-second metering), refundable (escrow-released), etc.

Solana settlement, concrete

For the scheme: "exact" on Solana with USDC, the client builds a normal SPL Token transfer with the nonce attached as a memo:

typescript
import { Connection, Transaction, PublicKey } from "@solana/web3.js"
import {
  getOrCreateAssociatedTokenAccount, createTransferCheckedInstruction,
} from "@solana/spl-token"
import { createMemoInstruction } from "@solana/spl-memo"

const conn = new Connection("https://api.mainnet-beta.solana.com")
const USDC = new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v")

// From the X-Payment-Required header:
const { payTo, amount, nonce } = paymentRequired

const fromAta = await getOrCreateAssociatedTokenAccount(
  conn, payer, USDC, payer.publicKey,
)
const toAta = await getOrCreateAssociatedTokenAccount(
  conn, payer, USDC, new PublicKey(payTo),
)

const tx = new Transaction()
  .add(createTransferCheckedInstruction(
    fromAta.address, USDC, toAta.address, payer.publicKey,
    Math.floor(parseFloat(amount) * 1_000_000),  // USDC has 6 decimals
    6, [],
  ))
  .add(createMemoInstruction(nonce, [payer.publicKey]))

const sig = await conn.sendTransaction(tx, [payer])

// Now retry the request:
const resp = await fetch("/paid-endpoint", {
  headers: {
    "X-Payment": JSON.stringify({ scheme: "exact", txid: sig, nonce }),
  },
})

Server-side verification

typescript
import { Connection, PublicKey, ParsedAccountData } from "@solana/web3.js"

async function verifyPayment(
  conn: Connection,
  xPayment: { txid: string; nonce: string },
  requirement: { payTo: string; amount: string; nonce: string },
): Promise<boolean> {
  if (xPayment.nonce !== requirement.nonce) return false

  // Fetch the parsed tx
  const tx = await conn.getParsedTransaction(xPayment.txid, "confirmed")
  if (!tx || tx.meta?.err) return false

  // Find the SPL transfer to our merchant address
  const instructions = tx.transaction.message.instructions
  const transfer = instructions.find((ix) => {
    if (!("parsed" in ix)) return false
    return ix.program === "spl-token" && ix.parsed.type === "transferChecked"
  }) as any
  if (!transfer) return false

  const info = transfer.parsed.info
  if (info.destination !== merchantUsdcAta) return false  // pre-computed
  const expected = Math.floor(parseFloat(requirement.amount) * 1_000_000)
  if (parseInt(info.tokenAmount.amount) !== expected) return false

  // Verify the memo contains the nonce
  const memo = instructions.find((ix) => "parsed" in ix && ix.program === "spl-memo") as any
  if (!memo || memo.parsed !== requirement.nonce) return false

  return true
}

Writing this verification yourself means running an RPC connection and handling settlement edge cases. In practice most merchants delegate it to a facilitator — a service that exposes /verify and /settle endpoints so the resource server stays chain-agnostic. PayAI (payai.network) runs the largest Solana facilitator after Coinbase and even fronts the gas for both sides.

Why this matters

Three concrete unlocks:

  • API monetisation without signups. A scraper, an LLM agent, or a script can pay $0.001 per request without credit card forms or developer accounts. The endpoint remains stateless from an auth perspective.
  • Agent payments by default. An AI agent with a wallet can call any x402-enabled API on the open internet without needing pre-arranged credentials. This is the payment layer most agentic web demos have been missing.
  • Micropayments that actually work. Solana's sub-cent fees make sub-cent API charges viable for the first time. $0.0001 per request is real, not theoretical.

The honest read

x402 won't replace API keys for B2B. Enterprise APIs need contracts, SLAs, abuse rate-limiting, identity. x402 fits the opposite niche: permissionless, agent-friendly, low-stakes API access where the cost of signup > the value of the call.

It also adds a Solana tx to every API call — ~400ms minimum, even with optimistic confirmation. For latency-sensitive paths you'd batch payments (pay $1.00 upfront for 1000 calls). For occasional or one-off calls, the 400ms is acceptable.

References

HTTP 402 was waiting 28 years for the right substrate. With sub-cent USDC transfers on Solana, it finally has one.

Keep reading

Get new articles in your inbox

Technical deep-dives on Solana tooling, infrastructure, and ecosystem. No noise.

x402: HTTP 402 reborn — internet-native payments, on Solana | devrels.xyz