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
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:
- Client requests a service
- Agent responds with
input-required+ payment requirements - Client signs payment with their wallet
- Client submits signed payment payload
- Agent verifies and settles on-chain
- 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.2Standalone 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→ inmetadata['x402.payment.required']PaymentPayload→ inmessage.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 AP2CartMandateintask.artifactsPaymentPayload→ embedded inside an AP2PaymentMandateinmessage.parts
Flow Detection Logic
A client detects the flow by inspecting the task metadata:
- Check
task.status.message.metadataforx402.payment.status: "payment-required" - If
metadataalso containsx402.payment.required→ Standalone Flow - If
x402.payment.requiredis absent → Embedded Flow (scantask.artifacts)
Payment State Machine
The X402 extension defines 6 payment states tracked via x402.payment.status in message metadata:
| Status | Description |
|---|---|
payment-required | Agent has sent payment requirements to the client |
payment-submitted | Client has submitted a signed payment payload |
payment-rejected | Client rejected the payment requirements |
payment-verified | Agent verified the payment signature is valid |
payment-completed | Payment has been settled on-chain |
payment-failed | Payment 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:
| Key | Type | Description |
|---|---|---|
x402.payment.status | string | Required. Current payment state (one of the 6 states) |
x402.payment.required | X402PaymentRequiredResponse | Payment requirements (Standalone Flow only) |
x402.payment.payload | PaymentPayload | Signed payment authorization (Standalone Flow only) |
x402.payment.receipts | X402SettleResponse[] | Array of settlement results (on final response) |
x402.payment.error | string | Error 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:
| Property | Value |
|---|---|
| Network | base-sepolia |
| Asset (USDC) | 0x036CbD53842c5426634e7929541eC2318f3dCF7e |
| Currency | USDC (6 decimal places) |
Roadmap
USDC Amount Formatting
USDC uses 6 decimal places. Amounts in the protocol are always expressed in minimum units (micro-USDC):
| Human Amount | Protocol Value | Calculation |
|---|---|---|
| 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
BigInt comparisons internally.Error Codes
| Code | Description |
|---|---|
INSUFFICIENT_FUNDS | Client wallet has insufficient funds |
INVALID_SIGNATURE | Payment signature could not be verified |
EXPIRED_PAYMENT | Payment submitted after expiry time |
DUPLICATE_NONCE | Nonce already used (replay attack prevention) |
NETWORK_MISMATCH | Payment signed for a different blockchain network |
INVALID_AMOUNT | Payment amount doesn’t match requirements |
SETTLEMENT_FAILED | On-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.valueagainstmaxAmountRequiredusingBigIntto prevent overflow.
Further Reading
- X402 Protocol Specification
- A2A Protocol: x402 Extension v0.2
- Payment Integration Guide — Implementation patterns for your agent
- x402 npm package — Facilitator library