Verify. Settle. Issue receipts.

The x402 payment protocol turns any HTTP endpoint into a paid resource. Users sign once. The facilitator settles on Base. Receipts enable repeat access without re-payment.

Run quickstart API reference

How x402 works

Three-call settlement

01

POST /verify

Validate the EIP-3009 authorization. Checks signature, amount match, nonce freshness, expiry, and gas price. Returns valid: true or a structured error code.

Reference →

02

POST /settle

Execute the on-chain transfer. Facilitator calls transferWithAuthorization on USDC. Returns txHash, payer, and requestId. Gas is paid by the facilitator — not the user.

Reference →

03

Issue receipt

Bind the txHash to a receipt with a TTL. The receipt ID is presented on repeat requests to skip re-settlement. Reduces cost for high-frequency access.

Reference →

Receipt reuse

Pay once. Access many.

After settlement, issue a receipt with a TTL. Present the receipt ID on subsequent requests — the server verifies it without triggering a new settlement.

  • TTL: Set per-receipt. Controls reuse window.
  • Reuse count: Tracked. Visible in dashboard and on the receipt object.
  • Revoke: Instant. Revoked receipts return RECEIPT_REVOKED immediately.
  • Linked endpoint: Receipt is scoped to a specific resource URL. Cannot be used cross-endpoint.

Receipt verify call

GET /api/v1/receipts/rcpt_01HX.../verify
Authorization: Bearer $P402_API_KEY

# Response (valid):
{
  "valid":      true,
  "reuseCount": 4,
  "expiresAt":  "2025-01-01T01:00:00Z",
  "resource":   "https://your-api.com/endpoint"
}

# Response (expired):
{
  "valid":  false,
  "reason": "RECEIPT_EXPIRED",
  "requestId": "req_..."
}

Error recovery

Common errors and fixes

  • AMOUNT_MISMATCH

    • value in authorization ≠ maxAmountRequired
    • Ensure both fields use atomic USDC units. $1.00 = 1000000.
  • REPLAY_DETECTED

    • Nonce already used in a previous settlement
    • Generate a fresh random bytes32 nonce and re-sign the authorization.
  • AUTHORIZATION_EXPIRED

    • validBefore is in the past
    • Set validBefore to at least 30 seconds in the future at signing time.
  • GAS_PRICE_TOO_HIGH

    • Base network gas > configured limit (50 gwei default)
    • Retry after a few seconds. Gas spikes on Base are typically short.
  • INVALID_SIGNATURE

    • EIP-712 signature does not match from address
    • Verify the EIP-712 domain (chainId: 8453, USDC verifyingContract) and signing wallet.

All errors include a requestId field. Include it when contacting support.

SDK

Drop-in integration

The @p402/sdk handles verify, settle, retry, and receipt reuse automatically. For custom flows, use the REST API directly.

SDK docs Run quickstart

SDK — one line

import { p402Fetch } from '@p402/sdk';

// Drop-in fetch replacement.
// Handles 402 → sign → settle → retry automatically.
const res = await p402Fetch('https://your-api.com/endpoint', {
  wallet: yourWallet,
  maxAmount: 1_000_000n, // $1.00 USDC
});

// res is the successful endpoint response.
// Receipt is stored automatically for reuse.