A2A Transport (Agent-to-Agent)
T402 payment flows over the Agent-to-Agent (A2A) protocol, enabling AI agents to pay each other for services.
Overview
The A2A transport adapts the T402 payment protocol for Google’s Agent-to-Agent protocol, enabling autonomous AI agents to monetize their services through on-chain payments.
Instead of HTTP status codes and headers, A2A uses task state and message metadata to signal payment requirements and submit payments.
Key Differences from HTTP
| Aspect | HTTP Transport | A2A Transport |
|---|---|---|
| Payment signal | 402 status code | Task state input-required |
| Requirements location | Response body | Task metadata |
| Payment submission | Payment-Signature header | Message metadata |
| Settlement receipt | Payment-Response header | Task metadata |
| State management | Stateless | Task lifecycle |
| Error signaling | HTTP status codes | Task state failed |
Payment Flow
Client sends task
The client agent sends a message/send request to the server agent.
Server signals payment required
Instead of HTTP 402, the server returns a task with state: "input-required" and payment requirements in metadata.
Client submits payment
The client signs the payment authorization and sends it back as a message with t402.payment.payload metadata.
Server verifies and settles
The server verifies the payment, settles on-chain, and completes the task with the response and settlement receipt.
Payment Required Signal
When a server agent requires payment, it returns a task in input-required state:
{
"jsonrpc": "2.0",
"id": "req-001",
"result": {
"kind": "task",
"id": "task-123",
"status": {
"state": "input-required",
"message": {
"role": "agent",
"parts": [{ "text": "Payment required to access this service." }],
"metadata": {
"t402.payment.status": "payment-required",
"t402.payment.required": {
"t402Version": 2,
"error": "Payment required",
"resource": {
"url": "a2a://agent/weather-tool",
"description": "Weather data lookup",
"mimeType": "application/json"
},
"accepts": [{
"scheme": "exact",
"network": "eip155:8453",
"amount": "1000",
"asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"payTo": "0xAgentAddress...",
"maxTimeoutSeconds": 300
}]
}
}
}
}
}
}Payment Submission
The client signs the payment and sends it back with the task ID for correlation:
{
"jsonrpc": "2.0",
"method": "message/send",
"id": "req-002",
"params": {
"message": {
"taskId": "task-123",
"role": "user",
"parts": [],
"metadata": {
"t402.payment.status": "payment-submitted",
"t402.payment.payload": {
"t402Version": 2,
"scheme": "exact",
"network": "eip155:8453",
"payload": {
"authorization": {
"from": "0xClientAddress...",
"to": "0xAgentAddress...",
"value": "1000",
"validAfter": 1700000000,
"validBefore": 1700000300,
"nonce": "0xabc..."
},
"signature": "0xdef..."
}
}
}
}
}
}Settlement Response
After successful verification and settlement, the task completes with the receipt:
{
"kind": "task",
"id": "task-123",
"status": {
"state": "completed",
"message": {
"role": "agent",
"parts": [{ "text": "Weather in Tokyo: 22°C, sunny." }],
"metadata": {
"t402.payment.status": "payment-completed",
"t402.payment.receipts": [{
"success": true,
"transaction": "0x1234abcd...",
"network": "eip155:8453",
"payer": "0xClientAddress..."
}]
}
}
}
}AP2 Embedded Flow
The Agent Payments Protocol (AP2) extends A2A with a structured, mandate-based payment flow. Instead of carrying x402 requirements in message metadata, AP2 wraps them inside CartMandate artifacts using the W3C Payment Request format.
Embedded vs Standalone
| Aspect | Standalone (A2A) | Embedded (AP2) |
|---|---|---|
| Requirements location | t402.payment.required metadata | CartMandate artifact DataPart |
| Payment submission | t402.payment.payload metadata | PaymentMandate message DataPart |
| Payment method format | t402 PaymentRequirements | W3C PaymentMethodData wrapping t402 |
| Cart support | No | Yes (PaymentDetailsInit) |
| Mandate tracking | No | Yes (mandate IDs, receipts) |
| Detection | isStandaloneFlow(task) | isEmbeddedFlow(task) |
How It Works
In the embedded flow, the server creates a CartMandate with x402 requirements embedded in the PaymentMethodData:
{
"artifacts": [{
"kind": "ap2.cart",
"name": "Cart Mandate",
"parts": [{
"kind": "data",
"data": {
"ap2.mandates.CartMandate": {
"contents": {
"payment_request": {
"method_data": [{
"supported_methods": "https://www.x402.org/",
"data": { "requirements": [{ "scheme": "exact", "network": "eip155:8453", "..." }] }
}]
}
}
}
}
}]
}]
}The client extracts the requirements, signs the payment, and wraps it in a PaymentMandate DataPart in a message.
See the AP2 Integration guide for the full embedded flow with code examples.
Payment Status Lifecycle
| Status | Task State | Description |
|---|---|---|
payment-required | input-required | Server needs payment |
payment-submitted | working | Client sent payment payload |
payment-verified | working | Payload verified |
payment-completed | completed | Settled on-chain |
payment-rejected | failed | Client declined to pay |
payment-failed | failed | Verification or settlement failed |
Extension Declaration
Agent Card
Server agents advertise T402 support in their Agent Card:
{
"name": "Weather Agent",
"capabilities": {
"extensions": [
{
"uri": "https://github.com/google-a2a/a2a-t402/v0.1",
"description": "Accepts T402 USDT payments",
"required": true
},
{
"uri": "https://github.com/google-agentic-commerce/a2a-x402/blob/main/spec/v0.2",
"description": "x402 v0.2 compatibility",
"required": false
}
]
}
}Client Activation
Clients signal T402 support via an HTTP header on the A2A connection:
X-A2A-Extensions: https://github.com/google-agentic-commerce/a2a-x402/blob/main/spec/v0.2Error Handling
When payment fails, the task transitions to failed state with error details:
{
"kind": "task",
"id": "task-123",
"status": {
"state": "failed",
"message": {
"role": "agent",
"parts": [{ "text": "Payment verification failed." }],
"metadata": {
"t402.payment.status": "payment-failed",
"t402.payment.error": "EXPIRED_PAYMENT",
"t402.payment.receipts": [{
"success": false,
"errorReason": "Payment authorization has expired",
"network": "eip155:8453"
}]
}
}
}
}Error Codes
| Error Code | Description | Recovery |
|---|---|---|
EXPIRED_PAYMENT | Authorization deadline passed | Retry with new signature |
INSUFFICIENT_AMOUNT | Amount less than required | Retry with correct amount |
INVALID_SIGNATURE | Signature verification failed | Check signer address |
NETWORK_MISMATCH | Wrong network | Use correct network from accepts |
SETTLEMENT_FAILED | On-chain settlement failed | Retry or try different network |
Metadata Fields Reference
| Field | Location | Description |
|---|---|---|
t402.payment.status | Message metadata | Current payment lifecycle state |
t402.payment.required | Message metadata | PaymentRequired object (same as HTTP 402 body) |
t402.payment.payload | Message metadata | Signed PaymentPayload |
t402.payment.receipts | Message metadata | Array of settlement receipts |
t402.payment.error | Message metadata | Error code string |
x402.payment.status | Message metadata | x402 compat alias for t402.payment.status |
x402.payment.required | Message metadata | x402 compat alias for t402.payment.required |
x402.payment.payload | Message metadata | x402 compat alias for t402.payment.payload |
x402.payment.receipts | Message metadata | x402 compat alias for t402.payment.receipts |
x402.payment.error | Message metadata | x402 compat alias for t402.payment.error |
Implementation Example
import { A2AServer } from '@google/a2a'
const server = new A2AServer({
agentCard: {
name: 'Premium Data Agent',
capabilities: {
extensions: [{
uri: 'https://github.com/google-a2a/a2a-t402/v0.1',
required: true
}]
}
}
})
server.onMessage(async (message, task) => {
// Check for payment
const paymentPayload = message.metadata?.['t402.payment.payload']
if (!paymentPayload) {
// Return payment required
return task.requireInput({
metadata: {
't402.payment.status': 'payment-required',
't402.payment.required': {
t402Version: 2,
accepts: [{
scheme: 'exact',
network: 'eip155:8453',
amount: '1000',
asset: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
payTo: agentAddress,
maxTimeoutSeconds: 300
}]
}
}
})
}
// Verify and settle payment
const receipt = await facilitator.settle(paymentPayload)
// Return result with receipt
return task.complete({
parts: [{ text: 'Here is your data...' }],
metadata: {
't402.payment.status': 'payment-completed',
't402.payment.receipts': [receipt]
}
})
})Comparison with MCP Transport
| Feature | A2A Transport | MCP Transport |
|---|---|---|
| Use case | Agent-to-agent services | Tool access for AI models |
| Protocol | JSON-RPC + tasks | JSON-RPC + tool calls |
| Payment signal | Task input-required | Tool result with 402 |
| State | Task lifecycle | Stateless per call |
| Discovery | Agent Card | MCP server manifest |
For AI agents accessing tools (like weather APIs), use the MCP transport. For agents providing services to other agents, use A2A.
Related
- AP2 Integration — Embedded payment flow with AP2 mandates
- MCP Transport — T402 over Model Context Protocol
- Agent-to-Agent Use Case — AI agent payment patterns
- T402 Specification — Full A2A transport spec