X402 Payments

The X402 Payments Extension enables A2A agents to monetize their services through on-chain cryptocurrency payments. It revives the HTTP 402 "Payment Required" status code for the world of decentralized agents.

Source

This document is based on the A2A Protocol: x402 Payments Extension v0.2 specification.

Overview

X402 adds a payment layer to the A2A protocol. When a client requests a paid service, the agent responds with payment requirements. The client signs a payment authorization, submits it, and the agent verifies and settles the payment on-chain before executing the service.

The flow in a nutshell:

  1. Client requests a service
  2. Agent responds with input-required + payment requirements
  3. Client signs payment with their wallet
  4. Client submits signed payment payload
  5. Agent verifies and settles on-chain
  6. Agent executes the service and returns results with a payment receipt

Extension Declaration

Agents that support X402 payments must declare the extension in their Agent Card:

{
  "capabilities": {
    "extensions": [
      {
        "uri": "https://github.com/google-agentic-commerce/a2a-x402/blob/main/spec/v0.2",
        "description": "Supports payments using the x402 protocol for on-chain settlement.",
        "required": true
      }
    ]
  }
}

Setting required: true signals to clients that they must implement the X402 protocol to interact with the agent. Clients activate the extension by including the URI in the HTTP header:

X-A2A-Extensions: https://github.com/google-agentic-commerce/a2a-x402/blob/main/spec/v0.2

Standalone vs Embedded Flows

The extension supports two modes of operation:

Standalone Flow

The simplest flow, used for direct monetization. Payment data is transported in the task.status.message.metadata.

  • x402PaymentRequiredResponse → in metadata['x402.payment.required']
  • PaymentPayload → in message.metadata['x402.payment.payload']

This is the flow used by all A2X reference agents.

Embedded Flow

Used when X402 acts as a Form of Payment for a higher-level protocol like AP2 (Agent Payment Protocol). Payment data is embedded inside higher-level objects in task.artifacts and message.parts.

  • x402PaymentRequiredResponse → embedded inside an AP2 CartMandate in task.artifacts
  • PaymentPayload → embedded inside an AP2 PaymentMandate in message.parts

Flow Detection Logic

A client detects the flow by inspecting the task metadata:

  1. Check task.status.message.metadata for x402.payment.status: "payment-required"
  2. If metadata also contains x402.payment.required Standalone Flow
  3. If x402.payment.required is absent → Embedded Flow (scan task.artifacts)

Payment State Machine

The X402 extension defines 6 payment states tracked via x402.payment.status in message metadata:

StatusDescription
payment-requiredAgent has sent payment requirements to the client
payment-submittedClient has submitted a signed payment payload
payment-rejectedClient rejected the payment requirements
payment-verifiedAgent verified the payment signature is valid
payment-completedPayment has been settled on-chain
payment-failedPayment verification or settlement failed

Data Structures

PaymentRequirements

Describes a single accepted payment option. Sent by the agent to tell the client what payment it accepts.

interface PaymentRequirements {
  scheme: 'exact';               // Payment scheme
  network: string;               // Blockchain network (e.g., "base-sepolia")
  maxAmountRequired: string;     // Maximum amount in minimum units
  asset: string;                 // Token contract address
  payTo: string;                 // Recipient wallet address
  resource: string;              // Resource identifier
  mimeType: string;              // Response MIME type
  maxTimeoutSeconds: number;     // Payment timeout
  extra: {
    name: string;                // EIP-712 token name (e.g., "USDC")
    version: string;             // EIP-712 version (e.g., "2")
  };
}

X402PaymentRequiredResponse

Wraps one or more PaymentRequirements options:

interface X402PaymentRequiredResponse {
  x402Version: 1;
  accepts: PaymentRequirements[];  // Array of accepted payment options
}

PaymentPayload

The client's signed payment authorization:

interface PaymentPayload {
  x402Version: 1;
  network: string;
  scheme: string;
  payload: {
    signature: string;
    authorization: {
      from: string;          // Payer wallet address
      to: string;            // Recipient wallet address
      value: string;         // Amount in minimum units
      validAfter: string;    // Earliest valid timestamp
      validBefore: string;   // Expiry timestamp
      nonce: string;         // Unique nonce for replay protection
    };
  };
}

X402SettleResponse

Returned after payment settlement:

interface X402SettleResponse {
  success: boolean;
  transaction: string;    // On-chain transaction hash
  network: string;        // Blockchain network
  errorReason?: string;   // Error description if failed
}

Metadata Keys

Payment state and data are transported via specific metadata keys on A2A messages:

KeyTypeDescription
x402.payment.statusstringRequired. Current payment state (one of the 6 states)
x402.payment.requiredX402PaymentRequiredResponsePayment requirements (Standalone Flow only)
x402.payment.payloadPaymentPayloadSigned payment authorization (Standalone Flow only)
x402.payment.receiptsX402SettleResponse[]Array of settlement results (on final response)
x402.payment.errorstringError code on failure

Example: Payment Required Response

{
  "kind": "task",
  "id": "task-123",
  "status": {
    "state": "input-required",
    "message": {
      "kind": "message",
      "role": "agent",
      "parts": [{ "kind": "text", "text": "Payment is required." }],
      "metadata": {
        "x402.payment.status": "payment-required",
        "x402.payment.required": {
          "x402Version": 1,
          "accepts": [
            {
              "scheme": "exact",
              "network": "base-sepolia",
              "maxAmountRequired": "1000",
              "asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
              "payTo": "0xYourWalletAddress",
              "resource": "baseFee",
              "mimeType": "application/json",
              "maxTimeoutSeconds": 300,
              "extra": { "name": "USDC", "version": "2" }
            }
          ]
        }
      }
    }
  }
}

Example: Payment Completed Response

{
  "kind": "task",
  "id": "task-123",
  "status": {
    "state": "completed",
    "message": {
      "kind": "message",
      "role": "agent",
      "parts": [{ "kind": "text", "text": "Analysis complete." }],
      "metadata": {
        "x402.payment.status": "payment-completed",
        "x402.payment.receipts": [
          {
            "success": true,
            "transaction": "0xabc123def456...",
            "network": "base-sepolia"
          }
        ]
      }
    }
  }
}

Facilitator Integration

The Facilitator is a service that handles on-chain payment verification and settlement. In the A2X ecosystem, the x402 npm package provides a built-in facilitator.

import { useFacilitator } from 'x402/verify';

const { verify: verifyPayment, settle: settlePayment } = useFacilitator();

Verification

The facilitator cryptographically verifies the payment signature:

const verifyResult = await verifyPayment(paymentPayload, acceptedRequirements);
if (!verifyResult.isValid) {
  // Handle invalid payment: verifyResult.invalidReason
}

Settlement

After verification, the facilitator settles the payment on-chain:

const settleResult = await settlePayment(paymentPayload, acceptedRequirements);
if (settleResult.success) {
  // settleResult.transaction contains the on-chain tx hash
} else {
  // settleResult.errorReason contains the failure description
}

Payment Network

Currently, A2X agents operate on Base Sepolia testnet using USDC:

PropertyValue
Networkbase-sepolia
Asset (USDC)0x036CbD53842c5426634e7929541eC2318f3dCF7e
CurrencyUSDC (6 decimal places)

Roadmap

The payment network will expand to Base Mainnet USDC, then to a custom token alongside USDC.

USDC Amount Formatting

USDC uses 6 decimal places. Amounts in the protocol are always expressed in minimum units (micro-USDC):

Human AmountProtocol ValueCalculation
0.001 USDC"1000"0.001 × 10⁶
0.01 USDC"10000"0.01 × 10⁶
0.10 USDC"100000"0.10 × 10⁶
1.00 USDC"1000000"1.00 × 10⁶

Warning

Always use string representation for amounts to avoid floating-point precision errors. The protocol uses BigInt comparisons internally.

Error Codes

CodeDescription
INSUFFICIENT_FUNDSClient wallet has insufficient funds
INVALID_SIGNATUREPayment signature could not be verified
EXPIRED_PAYMENTPayment submitted after expiry time
DUPLICATE_NONCENonce already used (replay attack prevention)
NETWORK_MISMATCHPayment signed for a different blockchain network
INVALID_AMOUNTPayment amount doesn’t match requirements
SETTLEMENT_FAILEDOn-chain transaction failed

Error Response Example

{
  "kind": "task",
  "id": "task-123",
  "status": {
    "state": "failed",
    "message": {
      "kind": "message",
      "role": "agent",
      "parts": [{ "kind": "text", "text": "Payment verification failed: The signature has expired." }],
      "metadata": {
        "x402.payment.status": "payment-failed",
        "x402.payment.error": "EXPIRED_PAYMENT",
        "x402.payment.receipts": [
          {
            "success": false,
            "errorReason": "Payment authorization was submitted after its 'validBefore' timestamp.",
            "network": "base-sepolia",
            "transaction": ""
          }
        ]
      }
    }
  }
}

Security Considerations

  • Private Key Security: Private keys must only be handled by trusted signing services or wallets. Never expose keys in agent code.
  • Signature Verification: Always cryptographically verify every payment signature before executing services. Use useFacilitator().verify().
  • Input Validation:Rigorously validate all payment-related data — amounts, addresses, network, scheme.
  • Replay Protection: Track used nonces to prevent replay attacks. The facilitator handles this automatically.
  • Transport Security: All A2A communication must use HTTPS/TLS in production.
  • Amount Validation: Compare authorization.value against maxAmountRequired using BigInt to prevent overflow.

Further Reading