Skip to content

x402 Protocol

x402 is an open HTTP payment protocol that enables machine-to-machine micropayments using blockchain-based stablecoins. It extends the HTTP 402 Payment Required status code into a full request/response payment cycle.

Why x402?

Traditional payment rails require human interactions (card swipes, OAuth redirects) and are unsuitable for AI agents, microservices, or programmatic API monetisation. x402 solves this by:

  • Making payments part of the HTTP request - no redirects, no human approval.
  • Leveraging smart contract authorization for trustless settlement.
  • Supporting ERC-20 / SPL / IBC tokens - not tied to a single chain.
  • Enabling micropayments ($0.001 and up) with USDC's stable denomination.

The Payment Flow

1. Client → Resource Server      GET /api/data
2. Resource Server → Client      402 Payment Required
                                 X-Payment-Requirements: <JSON>

3. Client signs payment payload  (off-chain, locally - no gas)

4. Client → Resource Server      GET /api/data
                                 X-Payment: <signed-payload>

5. Resource Server → Facilitator POST /api/v1/verify
6. Resource Server → Facilitator POST /api/v1/settle
7. Facilitator → Blockchain      broadcast transaction
8. Facilitator → Resource Server { success: true, txHash: "0x..." }

9. Resource Server → Client      200 OK  (content returned)

Optional pre-check: The client can call POST /api/v1/verify directly before step 4 to confirm its payload is valid without risking a failed request.

Protocol Objects

The types below are defined in the x402-go library and used directly by the facilitator.

PaymentPayload

The payer constructs and signs this object, then attaches it to the request.

go
type PaymentPayload struct {
    // x402 protocol version. Currently 1.
    X402Version int `json:"x402Version"`

    // Payment scheme. Currently "exact".
    Scheme string `json:"scheme"`

    // Target network (e.g. "base-sepolia", "solana-devnet", "osmosis").
    Network string `json:"network"`

    // Base64-encoded, chain-specific signed transaction or authorization.
    Payload string `json:"payload"`
}

The Payload field encoding is chain-specific:

ChainEncoding
EVM (Base)Base64-encoded EIP-3009 transferWithAuthorization signature
SolanaBase64-encoded partially-signed SPL transfer transaction
CosmosBase64-encoded signed MsgSend transaction bytes

PaymentRequirements

The resource server advertises this in the 402 response. It fully describes what constitutes a valid payment.

go
type PaymentRequirements struct {
    // Payment scheme (e.g. "exact", "stream").
    Scheme string `json:"scheme"`

    // Blockchain network (e.g. "base-sepolia", "osmosis").
    Network string `json:"network"`

    // Maximum amount in the token's atomic unit (e.g. "1000000" = 1 USDC).
    // String because Go does not support uint256.
    MaxAmountRequired string `json:"maxAmountRequired"`

    // URL of the resource being paid for.
    Resource string `json:"resource"`

    // Human-readable description shown to payers.
    Description string `json:"description"`

    // Expected MIME type of the resource response.
    MimeType string `json:"mimeType"`

    // Output schema of the response, if applicable.
    OutputSchema map[string]interface{} `json:"outputSchema,omitempty"`

    // Recipient wallet address.
    PayTo string `json:"payTo"`

    // Seconds before the payment authorization expires.
    MaxTimeoutSeconds int `json:"maxTimeoutSeconds"`

    // Token contract address (ERC-20, SPL mint, or IBC denom).
    Asset string `json:"asset"`

    // Scheme-specific extra fields (e.g. EIP-712 domain name/version for EVM).
    Extra map[string]interface{} `json:"extra,omitempty"`
}

JSON example

json
{
  "scheme": "exact",
  "network": "base-sepolia",
  "maxAmountRequired": "1000000",
  "resource": "/api/premium-data",
  "description": "Access to premium data endpoint",
  "mimeType": "application/json",
  "payTo": "0xMerchantAddress",
  "maxTimeoutSeconds": 60,
  "asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
  "x402Version": 1
}

Schemes

exact

The only currently supported scheme. The payer authorises a transfer of exactly maxAmountRequired atomic units of the specified asset to payTo. The facilitator verifies the signature matches these parameters before settling.

Future schemes (e.g. stream, subscription) may be added in later protocol versions.

Chain-Specific Notes

EVM - Base (and other EVM chains)

  • Uses EIP-3009 transferWithAuthorization - the payer signs a structured data permit, not a transaction. No gas is required from the payer.
  • The facilitator holds the settlement wallet and broadcasts the transferWithAuthorization call, paying gas on behalf of the merchant.
  • The Extra field in PaymentRequirements may include name and version for EIP-712 domain construction.
  • USDC contract on Base Sepolia: 0x036CbD53842c5426634e7929541eC2318f3dCF7e

Solana

  • The payer creates and partially signs an SPL transfer instruction.
  • The base64-encoded serialized transaction is placed in Payload.
  • The facilitator co-signs and broadcasts.
  • Asset: USDC SPL mint.

Cosmos (Osmosis / CosmosHub / Noble)

  • The payer signs a standard MsgSend transaction.
  • The facilitator broadcasts the pre-signed transaction bytes.
  • Asset: IBC USDC (e.g. Noble USDC routed via IBC) or native Noble USDC.
  • gRPC endpoint required for Cosmos networks.

The x402-go Library

The facilitator is built on top of x402-go - a standalone Go library that handles all chain-specific verification and settlement logic.

go
import "github.com/vitwit/x402-go"

x := x402.New(cfg,
    x402.WithLogger(log),
    x402.WithMetrics(recorder),
)

// Add networks
x.AddEVMNetwork("base-sepolia", evmClient)
x.AddSolanaNetwork("solana-devnet", solanaClient)
x.AddCosmosNetwork("osmosis", cosmosClient)

// Verify a payment
result, err := x.Verify(ctx, verifyRequest)

// Settle a payment
result, err := x.Settle(ctx, verifyRequest)

The library is chain-agnostic and extensible - new networks can be added by implementing the client interface.

Verify result

go
type VerifyResult struct {
    IsValid       bool
    InvalidReason string
    Sender        string
    Timestamp     int64
    Extra         map[string]interface{}
}

Settle result

go
type SettleResult struct {
    Success   bool
    TxHash    string
    Error     string
    NetworkId string
    Block     uint64
    Sender    string
    Recipient string
    Amount    string
    Asset     string
    Fees      string
    Extra     map[string]interface{}
}

Protocol Versioning

The current protocol version is 1. All requests and payloads must include "x402Version": 1. Future versions will be announced in the x402-go repository.

Further Reading

Released under the MIT License.