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.
Negotiation Protocol
Coming Soon: @t402/a2a-negotiation is not yet released. This page describes planned functionality and APIs are subject to change. Follow the GitHub repository for release announcements.
The A2A Negotiation Protocol enables AI agents to negotiate payment terms, discover services, and settle payments atomically. It implements the Agent-to-Agent (A2A) protocol with T402 payment extensions.
Features
- Negotiation Protocol - State machine for multi-round price negotiations
- Pricing Strategies - Pluggable strategies for automated negotiation
- Agent Discovery - Service registry with capability and pricing discovery
- Escrow System - Conditional payments with multi-sig and time-lock support
- Atomic Settlement - All-or-nothing payment settlement
- Dispute Resolution - Evidence-based dispute handling
Installation
npm install @t402/a2a-negotiationQuick Start
Basic Negotiation
import {
NegotiationProtocol,
StrategyFactory,
} from '@t402/a2a-negotiation';
// Create a negotiation session
const negotiation = NegotiationProtocol.create({
initiator: 'client-agent',
counterparty: 'service-agent',
resource: {
url: 'https://api.example.com/generate-image',
description: 'AI Image Generation Service',
},
maxRounds: 5,
timeoutMs: 300000, // 5 minutes
});
// Submit a proposal
const proposal = negotiation.propose({
terms: {
scheme: 'exact',
network: 'eip155:8453', // Base
asset: '0x833589fCD6eDb6E08f4c7C32D4f71b54bda02913', // USDC
amount: '1000000', // 1 USDC (6 decimals)
payTo: '0xServiceWallet',
},
slaTerms: {
maxResponseTimeMs: 5000,
},
});
// Use a pricing strategy to evaluate
const strategy = StrategyFactory.priceRange(
'500000', // min: 0.5 USDC
'2000000', // max: 2 USDC
['eip155:8453'],
['0x833589fCD6eDb6E08f4c7C32D4f71b54bda02913'],
);
const evaluation = strategy.evaluate({
proposal,
currentRound: negotiation.getSession().currentRound,
maxRounds: 5,
previousOffers: [],
});
if (evaluation.accept) {
negotiation.accept(proposal.proposedTerms);
} else if (evaluation.counterTerms) {
negotiation.counter({
proposalId: proposal.id,
counterTerms: evaluation.counterTerms,
reason: evaluation.reason,
});
}Negotiation Flow
Initiate Negotiation
Client agent creates a negotiation session with the service agent.
Submit Proposal
Client proposes payment terms (amount, network, asset, SLA requirements).
Evaluate with Strategy
Service agent uses a pricing strategy to evaluate the proposal.
Counter or Accept
If terms are acceptable, accept. Otherwise, send a counter-offer.
Multiple Rounds
Negotiation continues until agreement or max rounds reached.
Settlement
Once accepted, proceed to atomic payment settlement.
Agent Discovery
Register an Agent
import {
AgentRegistry,
PriceDiscovery,
CommonCapabilities,
} from '@t402/a2a-negotiation';
const registry = new AgentRegistry();
// Register an agent with capabilities
registry.register({
id: 'image-gen-agent',
name: 'AI Image Generator',
version: '1.0.0',
endpoints: [{
url: 'https://api.imagegen.ai',
protocol: 'https',
transport: 'a2a',
}],
capabilities: [
CommonCapabilities.imageGeneration('Generate images from text prompts'),
],
pricing: [{
capabilityType: 'image-generation',
config: {
model: 'per-request',
basePrice: '1000000', // 1 USDC
currency: {
network: 'eip155:8453',
asset: '0x833589fCD6eDb6E08f4c7C32D4f71b54bda02913',
decimals: 6,
},
negotiable: true,
},
}],
paymentAddress: '0xAgentWallet',
supportedNetworks: ['eip155:8453'],
supportedAssets: ['0x833589fCD6eDb6E08f4c7C32D4f71b54bda02913'],
});Discover Agents
// Find agents by capability
const result = registry.discover({
capabilityType: 'image-generation',
networks: ['eip155:8453'],
maxPrice: '2000000',
});
// Get price quotes from discovered agents
const quotes = result.agents.map(agent =>
PriceDiscovery.getQuote(agent, {
agentId: agent.id,
capabilityType: 'image-generation',
network: 'eip155:8453',
asset: '0x833589fCD6eDb6E08f4c7C32D4f71b54bda02913',
}),
);
// Find the cheapest option
const cheapest = PriceDiscovery.findCheapest(quotes);Pricing Strategies
Built-in Strategies
| Strategy | Description |
|---|---|
AcceptAllStrategy | Accepts any proposal (for testing) |
FixedPriceStrategy | Only accepts at fixed price |
RangePricingStrategy | Accepts within price range |
DynamicPricingStrategy | Adjusts based on demand |
GraduatedDiscountStrategy | Offers discounts per round |
SLAAwareStrategy | Prices based on SLA requirements |
CompositeStrategy | Combines multiple strategies |
Using Strategy Factory
import { StrategyFactory } from '@t402/a2a-negotiation';
// Fixed price strategy
const fixed = StrategyFactory.fixedPrice('1000000');
// Price range strategy
const range = StrategyFactory.priceRange(
'500000', // min
'2000000', // max
['eip155:8453'],
['0xUSDC'],
);
// Graduated discount (10% per round, max 30%)
const discount = StrategyFactory.graduatedDiscount(
'1000000', // base price
0.1, // discount per round
0.3, // max discount
);
// SLA-aware pricing
const sla = StrategyFactory.slaAware('1000000', {
priorityMultiplier: 1.5,
urgencyMultiplier: 2.0,
});Custom Strategy
import { NegotiationStrategy, StrategyContext, StrategyEvaluation } from '@t402/a2a-negotiation';
class MyStrategy implements NegotiationStrategy {
name = 'my-strategy';
evaluate(context: StrategyContext): StrategyEvaluation {
const { proposal, currentRound, maxRounds } = context;
// Accept if price >= 1 USDC
if (BigInt(proposal.proposedTerms.amount) >= BigInt('1000000')) {
return { accept: true };
}
// Counter with minimum price
return {
accept: false,
counterTerms: {
...proposal.proposedTerms,
amount: '1000000',
},
reason: 'Price below minimum',
};
}
}Escrow System
Create Escrow with Confirmation Release
import {
EscrowManager,
createConfirmationConditions,
} from '@t402/a2a-negotiation';
const escrow = new EscrowManager();
// Create escrow
const record = escrow.createEscrow({
negotiationId: 'neg-123',
payer: '0xClientWallet',
payee: '0xServiceWallet',
paymentTerms: {
scheme: 'exact',
network: 'eip155:8453',
asset: '0xUSDC',
amount: '1000000',
payTo: '0xServiceWallet',
maxTimeoutSeconds: 600,
},
releaseConditions: createConfirmationConditions('0xServiceWallet'),
expirationMs: 7 * 24 * 60 * 60 * 1000, // 7 days
});
// Fund the escrow
escrow.fund(record.id, '0xFundingTxHash');
// Service confirms delivery
escrow.confirm(record.id, '0xServiceWallet', '0xSignature');
// Release funds
if (escrow.checkReleaseConditions(record.id)) {
escrow.release(record.id, '0xReleaseTxHash');
}Multi-Sig Escrow
import { createMultiSigConditions } from '@t402/a2a-negotiation';
const record = escrow.createEscrow({
// ... other config
releaseConditions: createMultiSigConditions(
['0xSigner1', '0xSigner2', '0xSigner3'],
2, // Required signatures
),
});Time-Locked Escrow
import { createTimeLockConditions } from '@t402/a2a-negotiation';
const record = escrow.createEscrow({
// ... other config
releaseConditions: createTimeLockConditions(
new Date('2026-02-01T00:00:00Z'),
),
});Atomic Settlement
import { AtomicSettlement } from '@t402/a2a-negotiation';
const settlement = new AtomicSettlement();
// Register a settlement handler
settlement.registerHandler({
network: 'eip155:8453',
async submit(request) {
// Submit transaction to blockchain
return { txHash: '0x...' };
},
async verify(txHash) {
// Check transaction confirmation
return { confirmed: true, confirmations: 3 };
},
async refund(request) {
// Process refund
return { txHash: '0x...' };
},
});
// Create settlement from negotiation
const request = settlement.createFromNegotiation(
negotiation.getSession(),
paymentPayload,
);
// Execute settlement
const result = await settlement.execute(request.id);
if (result.state === 'completed') {
console.log('Payment settled:', result.transactionHash);
}A2A Integration
Building AgentCard with T402 Extension
import {
AgentCardBuilder,
CommonCapabilities,
} from '@t402/a2a-negotiation';
const agentCard = new AgentCardBuilder()
.setInfo(
'My AI Agent',
'https://api.myagent.ai',
'AI-powered service agent',
'1.0.0',
)
.setA2ACapabilities(true, true) // streaming, pushNotifications
.addT402Extension()
.withT402Capabilities()
.addCapability(
CommonCapabilities.textGeneration(),
{
model: 'per-token',
pricePerUnit: '10', // 0.00001 USDC per token
currency: {
network: 'eip155:8453',
asset: '0xUSDC',
decimals: 6,
},
negotiable: true,
},
{ maxResponseTimeMs: 5000 },
)
.setPaymentAddress('0xMyWallet')
.addNetwork('eip155:8453')
.addAsset('0xUSDC')
.build();Processing A2A Messages
import {
isNegotiationMessage,
extractNegotiationMetadata,
} from '@t402/a2a-negotiation';
function handleA2AMessage(message: any) {
const metadata = message.metadata ?? {};
if (isNegotiationMessage(metadata)) {
const negotiationMeta = extractNegotiationMetadata(metadata);
switch (negotiationMeta?.['t402.negotiation.status']) {
case 'negotiate-request':
// Handle negotiation request
break;
case 'negotiate-counter':
// Handle counter-offer
break;
case 'negotiate-accept':
// Handle acceptance
break;
}
}
}Common Capabilities
The package provides pre-defined capability types:
import { CommonCapabilities } from '@t402/a2a-negotiation';
// AI/ML Capabilities
CommonCapabilities.textGeneration()
CommonCapabilities.imageGeneration(description)
CommonCapabilities.audioTranscription(description)
CommonCapabilities.codeGeneration(description)
// Data Capabilities
CommonCapabilities.dataQuery(description)
CommonCapabilities.dataStorage(description)
// API Capabilities
CommonCapabilities.apiAccess(description)
CommonCapabilities.webhookDelivery(description)Negotiation API Reference
Negotiation Module
| Export | Description |
|---|---|
NegotiationProtocol | State machine for negotiations |
NegotiationSession | Session state type |
NegotiationProposal | Proposal schema |
CounterOffer | Counter-offer schema |
PaymentTerms | Payment terms schema |
SLATerms | SLA terms schema |
Discovery Module
| Export | Description |
|---|---|
AgentRegistry | Agent service registry |
PriceDiscovery | Price quote and comparison |
DynamicPricingManager | Market-based pricing |
AgentCardBuilder | Build T402-enabled AgentCards |
Settlement Module
| Export | Description |
|---|---|
AtomicSettlement | Atomic payment settlement |
ConditionalPaymentManager | Conditional payments |
EscrowManager | Escrow management |
DisputeResolver | Dispute resolution |
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
- Agent Policy — AI agent spending policies
- T402 Specification — Full A2A transport spec