x402: HTTP-Native Payments
How the x402 protocol turns HTTP 402 into a concrete payment flow for AI agents.
HTTP status code 402 ("Payment Required") has existed since 1997. The x402 protocol finally gives it a concrete implementation: agents pay per-request in USDC, with no accounts, API keys, or invoicing.
The Flow
- Agent sends
GET /api/datato a merchant - Merchant returns
402 Payment Requiredwith a JSON body:
{
"x402Version": 2,
"accepts": [
{
"scheme": "exact",
"network": "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",
"amount": "10000",
"resource": "https://merchant.com/api/data",
"payTo": "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
"extra": {
"name": "Premium weather data",
"facilitatorUrl": "https://facilitator.pincerpay.com"
}
}
]
}
- Agent signs a USDC transfer and POSTs it to the facilitator's
/v1/settleendpoint - Facilitator verifies the transaction, broadcasts it on-chain
- Agent retries the original request with the receipt in the
X-PAYMENTheader - Merchant verifies the receipt and serves the resource
See this flow animated step by step in the interactive demo.
Why This Matters
- No API keys to manage -- agents pay per-request, no account creation needed
- No rate limits -- every request that pays gets served
- No invoicing -- settlement is instant, on-chain, final
- Any HTTP endpoint -- works with REST, GraphQL, file downloads, anything over HTTP
The Facilitator
The PincerPay Facilitator is the intermediary that verifies and broadcasts transactions. It ensures merchants don't need to run their own on-chain infrastructure.
- Verify -- checks that the signed transaction matches the payment requirements (amount, recipient, chain)
- Broadcast -- submits the transaction on-chain
- Confirm -- monitors confirmation status and notifies merchants via webhooks
Merchants interact with the facilitator through the @pincerpay/merchant middleware. Agents interact through @pincerpay/agent. Neither needs direct facilitator API knowledge for basic usage. See the API Reference for direct integration.
Describing x402 in OpenAPI
If you publish an OpenAPI spec for an x402-paywalled API, model the payment receipt as an apiKey security scheme carried in the X-PAYMENT request header. This is the canonical representation; every downstream spec should describe it the same way so tools and agents recognize it.
The exact wire contract set by the PincerPay middleware:
- Payment receipt header (request):
X-PAYMENT(the middleware also acceptspayment-signaturefor compatibility, butX-PAYMENTis canonical). - Unpaid response: HTTP
402with the requirements JSON in the body and a base64-encoded copy in thepayment-requiredresponse header. - Settled response: the original
2xx, plus a base64-encoded receipt in thepayment-responseresponse header. - Price: advertised in the 402 body at
accepts[].amount, as USDC base units (6 decimals -"10000"= $0.01), perscheme/network/asset/payTo. There is noX-PRICE-USDheader; price is not a header at all.
components:
securitySchemes:
x402Payment:
type: apiKey
in: header
name: X-PAYMENT
description: >
Base64-encoded x402 payment payload. Obtain the requirements by
calling the endpoint without this header and reading the 402 response
body (`accepts[]`), then settle and retry with the receipt here.
responses:
PaymentRequired:
description: Payment required (x402). Body lists accepted payment options.
headers:
payment-required:
schema: { type: string }
description: Base64-encoded copy of the requirements body.
content:
application/json:
schema:
type: object
properties:
x402Version: { type: integer, example: 2 }
accepts:
type: array
items:
type: object
properties:
scheme: { type: string, example: exact }
network: { type: string, example: "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1" }
amount: { type: string, description: "USDC base units (6 decimals)", example: "10000" }
asset: { type: string, description: "USDC token address on the network" }
payTo: { type: string }
paths:
/api/data:
get:
security:
- x402Payment: []
# Recommended vendor extension for static price advertisement.
# The authoritative price is always the live 402 `accepts[].amount`.
x-pincerpay-price:
usdc: "0.01" # human-readable USDC
chains: ["solana"] # chain shorthands that can settle
responses:
"200": { description: Resource delivered after payment }
"402": { $ref: "#/components/responses/PaymentRequired" }
Prefer
x-pincerpay-price(human-readable USDC + chains, mirroringPincerPayConfig["routes"]) over an ad-hocx-price-usd, and treat it as advisory: the live 402accepts[].amount(base units) is the source of truth.