The x402 protocol
x402 is an open HTTP-native micropayment protocol. The name comes from
the HTTP status code 402 Payment Required, which was reserved in the
original HTTP/1.1 spec but left unspecified for 25 years.
The wire protocol in one page
An x402 payment is four HTTP messages with a specific header dance.
Request 1 — plain GET
GET /article/42 HTTP/1.1
Host: news.example.comResponse 1 — 402 with a quote
HTTP/1.1 402 Payment Required
PAYMENT-REQUIRED: <base64(body)>
Content-Type: application/json
{
"x402Version": 2,
"accepts": [{
"scheme": "exact",
"network": "solana-devnet",
"asset": "<spl-mint>",
"amount": "10000",
"payTo": "<merchant-eta-address>",
"resource": "https://news.example.com/article/42",
"maxTimeoutSeconds": 300,
"mimeType": "application/json"
}]
}Request 2 — retry with a signed payment
GET /article/42 HTTP/1.1
Host: news.example.com
PAYMENT-SIGNATURE: <base64(envelope)>Response 2 — 200 + data + settlement receipt
HTTP/1.1 200 OK
X-Payment-Response: <base64(settlement)>
Content-Type: application/json
{ "id": 42, "body": "..." }That's the whole protocol on the wire. Four messages. Three headers:
PAYMENT-REQUIRED (quote), PAYMENT-SIGNATURE (proof-of-payment),
X-Payment-Response (settlement receipt).
What makes x402 different
- HTTP-native. Not a sidecar service, not a webhook callback — plain
request/response.
curl+jqcan debug it. - Chain-agnostic at the spec level. The
accepts[].networkfield picks the chain. Solana is one of several adapters. - No-data-no-pay invariant. A signed payment is bound to a specific resource URL + amount + recipient. If the merchant never serves data, the merchant also doesn't get a verifiable payment to claim.
How Obscura extends x402 with confidentiality
Obscura's flavour of x402 swaps the classic "facilitator settles a signed SPL transfer" pattern for a confidential mixer transfer:
- The agent never holds a Solana key — Obscura's backend signs on
its behalf inside
/api/x402/sign. - The transfer is a deposit + commitment in the on-chain Umbra mixer tree, not a direct SPL transfer. The amount is encrypted, and the on-chain link between sender ETA and recipient ETA is broken by the mixer.
- The payment header is an
umbra-mixer-v1envelope carrying the queue + callback signatures. The merchant verifies these on-chain via standard Solana RPC — no facilitator involvement.
Why Solana
- Sub-cent transaction fees (a fraction of a lamport per transfer) — the only L1 where a $0.001 API call doesn't get eaten by gas.
- Fast confirmations. The merchant's verification is two
getTransactionreads adding ~150–400ms; on Ethereum L1 the same flow would add 12+ seconds per call. - Fee-payer decoupling. Solana lets a third party pay the network fee, which is what lets Obscura's backend fund the queue + callback txns from a treasury — your agent only ever needs the configured stablecoin.
Replay protection
Two defences in the Obscura flow:
- Backend-side fingerprint dedupe.
/api/x402/signrejects duplicate spend intents (same agent + URL + amount + recipient ETA- asset) while the original is in flight, returning
conflict(409).
- asset) while the original is in flight, returning
- Merchant-side replay window. The merchant SDK caches each
queueSignaturein-memory and rejects re-presentation of the same signature withinreplayWindowMs(default 5 minutes).
Beyond that, the on-chain Umbra program enforces nullifier uniqueness: the same UTXO can't be claimed twice, and the same encrypted-balance deduction can't be replayed at the program level.
Further reading
x402-solanaon npm — the core x402-on-Solana types Obscura's backend re-uses for thePaymentRequirementsshape.- How it works (agent) — the full flow from the agent SDK's perspective.
- How it works (merchant) — the verification swimlane on the merchant side.