Documentation Index Fetch the complete documentation index at: https://mintlify.com/Crossmint/crossmint-agentic-finance/llms.txt
Use this file to discover all available pages before exploring further.
Overview
Agent-to-Agent (A2A) payments enable AI agents to autonomously pay each other for services and tool access. No user intervention required after initial approval—agents handle signature generation, payment submission, and settlement verification.
Key insight: Agents treat payments like API authentication—just another HTTP header to include.
Why A2A Matters
Traditional payment flows break agent autonomy:
Manual Flows User must approve every transaction ❌ Breaks automation
Private Keys Agents need direct key access ❌ Security nightmare
Custom Infrastructure Build payment systems from scratch ❌ High development cost
User Intervention Manual signing for each payment ❌ Poor UX
A2A payments solve this:
User: "RSVP to event abc-123"
↓
Guest Agent: Detects paid tool, checks price ($0.05)
↓
Guest Agent: Signs USDC payment autonomously
↓
Host Agent: Verifies signature, settles on Base
↓
Guest Agent: "You're registered! TX: 0xabc...def"
Zero user intervention after initial confirmation.
A2A Architecture
A2A payments combine three technologies:
1. x402 Protocol (Payment Layer)
HTTP-native payment protocol using 402 Payment Required status codes.
// Server returns payment requirement
HTTP / 1.1 402 Payment Required
{
"payment" : {
"amount" : "50000" , // 0.05 USDC
"currency" : "USDC" ,
"to" : "0xHostWallet" ,
"chainId" : 84532
}
}
See x402 Protocol for details.
Model Context Protocol defines how agents call tools and functions.
// Define a paid tool
server . paidTool (
"rsvpToEvent" ,
"RSVP to a paid event" ,
0.05 , // Price in USD
{ eventId: z . string () },
async ({ eventId }) => {
// Business logic
return { success: true , eventId };
}
);
See events-concierge/src/agents/host.ts:158-202
3. Smart Wallets (Identity Layer)
Crossmint smart wallets enable signing without private key management.
// Create wallet from API key
const wallet = await crossmintWallets . createWallet ({
chain: "base-sepolia" ,
signer: { type: "api-key" }
});
// Sign payment
const signature = await wallet . signTypedData ( paymentMessage );
See Smart Wallets for details.
Payment Flow
Let’s walk through a complete A2A payment:
User Initiates Action
User asks their agent to perform a paid action User: "RSVP to event abc-123"
Guest Agent Calls Paid Tool
Agent makes MCP tool call to Host await mcpClient . callTool ( "rsvpToEvent" , {
eventId: "abc-123" ,
walletAddress: guestWallet . address
});
Host Returns 402 Payment Required
Host MCP server detects missing payment, returns requirement {
"statusCode" : 402 ,
"payment" : {
"amount" : "50000" ,
"currency" : "USDC" ,
"to" : "0xHostWallet" ,
"chainId" : 84532
}
}
Guest Agent Confirms with User
Agent shows payment details, gets approval const approved = await askUser (
`Pay ${ requirement . amount } USDC to RSVP?`
);
if ( ! approved ) throw new Error ( "Payment declined" );
Guest Agent Signs Payment
Agent creates EIP-712 signature const signature = await guestWallet . signTypedData ({
domain: { chainId: 84532 , ... },
types: { Payment: [ ... ] },
message: {
amount: "50000" ,
to: "0xHostWallet" ,
currency: "0xUSDC" ,
nonce: Date . now ()
}
});
Guest Agent Retries with Payment
Agent retries MCP call with X-PAYMENT header await mcpClient . callTool ( "rsvpToEvent" , args , {
headers: { "X-PAYMENT" : signature }
});
Host Verifies & Settles
Host verifies signature and settles payment on-chain // Verify signature
const valid = await x402 . verifySignature ( signature , message );
// Settle on Base Sepolia
const tx = await facilitator . settle ( signature );
// Execute business logic
await recordRsvp ( eventId , guestWallet );
Host Returns Success + TX Hash
Host returns tool result with transaction proof {
"success" : true ,
"eventId" : "abc-123" ,
"transactionHash" : "0xabc...def" ,
"message" : "RSVP confirmed!"
}
Guest Agent Confirms to User
Agent displays confirmation with blockchain proof Agent: "You're registered for 'Tech Meetup'!"
Agent: "Transaction: 0xabc...def"
Agent: "View on explorer: https://sepolia.basescan.org/tx/0xabc...def"
Code Example: Guest Agent
Here’s how the Guest Agent handles automatic payments:
events-concierge/src/agents/guest.ts
export class Guest extends McpAgent {
private wallet : Wallet ;
private mcpClient : McpClient ;
async init () {
// Create guest wallet
this . wallet = await crossmintWallets . createWallet ({
chain: "base-sepolia" ,
signer: { type: "api-key" }
});
// Setup MCP client with x402 auto-payment
this . mcpClient = new McpClient ({ url: hostMcpUrl })
. withX402Client ({
wallet: this . wallet ,
onPaymentRequired : async ( requirement , retryFn ) => {
// 1. Show payment modal to user
const approved = await this . confirmPayment ( requirement );
if ( ! approved ) throw new Error ( "Payment declined" );
// 2. Sign payment
const signature = await this . wallet . signPayment ( requirement );
// 3. Retry with signature
return retryFn ( signature );
}
});
}
async rsvpToEvent ( eventId : string ) {
// This automatically handles 402 responses!
return this . mcpClient . callTool ( "rsvpToEvent" , {
eventId ,
walletAddress: this . wallet . address
});
}
}
See events-concierge/src/agents/guest.ts
Code Example: Host Agent
Here’s how the Host Agent defines paid tools:
events-concierge/src/agents/host.ts
export class Host extends McpAgent {
private wallet : Wallet ;
private server : McpServer ;
async init () {
// Create host wallet for receiving payments
this . wallet = await crossmintWallets . createWallet ({
chain: "base-sepolia" ,
signer: { type: "api-key" }
});
// Setup MCP server with x402 support
this . server = new McpServer ({ name: "Event RSVP" })
. withX402 ({
network: "base-sepolia" ,
recipient: this . wallet . address ,
facilitator: { url: FACILITATOR_URL }
});
// Register paid tool
this . server . paidTool (
"rsvpToEvent" ,
"RSVP to an event (requires payment)" ,
0.05 , // Price in USD
{ eventId: z . string (), walletAddress: z . string () },
async ({ eventId , walletAddress }) => {
// This only executes AFTER payment is verified!
const event = await getEvent ( eventId );
await recordRsvp ( eventId , walletAddress );
return {
success: true ,
eventId ,
eventTitle: event . title ,
message: `RSVP successful! Paid ${ event . price } USDC.`
};
}
);
}
}
See events-concierge/src/agents/host.ts:158-202
Multi-Tenant Architecture
For production A2A systems, use Cloudflare Durable Objects for per-user isolation:
// Each user gets their own Host instance
User A → / mcp / users / hash - a → Host DO ( name : "hash-a" )
├─ wallet : 0xAAA ...
├─ events : [ ... ]
└─ revenue : $12 . 50
User B → / mcp / users / hash - b → Host DO ( name : "hash-b" )
├─ wallet : 0xBBB ...
├─ events : [ ... ]
└─ revenue : $8 . 00
Benefits:
Stateful In-memory state persists across requests
Isolated Each user has their own wallet and data
Single-threaded No race conditions or concurrency bugs
Auto-scaling Created on-demand, hibernate when idle
See events-concierge/README.md for full implementation.
Security Considerations
Always verify signatures match the payment message: const valid = await verifySignature ({
signature ,
message: paymentRequirement ,
signer: guestWalletAddress
});
if ( ! valid ) throw new Error ( "Invalid signature" );
Use nonces to prevent replay attacks: const message = {
amount: "50000" ,
to: hostWallet ,
currency: USDC_ADDRESS ,
nonce: Date . now () // or UUID
};
// Store used nonces
await kv . put ( `nonce: ${ nonce } ` , "used" );
Verify payer has sufficient funds before executing business logic: const balance = await getUSDCBalance ( guestWallet );
if ( balance < requirement . amount ) {
throw new Error ( "Insufficient USDC balance" );
}
Confirm on-chain settlement before marking payment complete: const tx = await publicClient . getTransaction ({
hash: txHash
});
if ( tx . status !== "success" ) {
throw new Error ( "Transaction failed" );
}
Prevent abuse with rate limits: const attempts = await kv . get ( `ratelimit: ${ walletAddress } ` );
if ( attempts > 10 ) {
throw new Error ( "Rate limit exceeded" );
}
Error Handling
Agents must gracefully handle payment failures:
try {
const result = await mcpClient . callTool ( "rsvpToEvent" , args );
console . log ( "✅ RSVP successful:" , result );
} catch ( error ) {
if ( error . code === 402 ) {
// Payment required but user declined
console . log ( "⚠️ Payment required but not approved" );
} else if ( error . message . includes ( "Insufficient" )) {
// Insufficient USDC balance
console . log ( "❌ Please add USDC to your wallet" );
} else if ( error . message . includes ( "signature" )) {
// Invalid signature
console . log ( "❌ Signature verification failed" );
} else {
// Other error
console . log ( "❌ RSVP failed:" , error . message );
}
}
Real-World Examples
Event RSVP MCP-based event booking with autonomous payments Stack: Cloudflare Durable Objects, MCP, x402
Tweet Agent Pay to post tweets via AI agent Stack: Next.js, Express, x402
Worldstore Agent Amazon purchases via XMTP chat with gasless USDC Stack: XMTP, Base, Crossmint
Ad Bidding Claude agents competing for ad space with payments Stack: Claude, x402, autonomous bidding
Best Practices
Use Base Sepolia and Circle’s USDC faucet for development:
Always get user approval before signing payments: const approved = await showPaymentModal ({
amount: "0.05 USDC" ,
recipient: "Event Host" ,
action: "RSVP to Tech Meetup"
});
Display Transaction Proofs
Show users blockchain proof of payment: console . log ( `✅ Payment confirmed!` );
console . log ( `TX: ${ txHash } ` );
console . log ( `Explorer: https://sepolia.basescan.org/tx/ ${ txHash } ` );
Track all payment attempts for debugging: await logPayment ({
timestamp: Date . now (),
from: guestWallet ,
to: hostWallet ,
amount: "50000" ,
txHash ,
status: "success"
});
Track earnings in real-time: const revenue = await kv . get ( ` ${ userId } :revenue` );
console . log ( `Total revenue: $ ${ revenue / 1000000 } ` );
Next Steps
x402 Protocol Deep dive into HTTP payment protocol
Smart Wallets Learn about Crossmint smart wallet features
Payment Flow See detailed payment flow diagrams
Build Event RSVP Build your first A2A payment system
Resources