What you will build
Two AI agents that transact: one pays the other in USDC on Base for work, with no human approving the transfer. The paying agent calls the paid agent like any x402 service; the paid agent gates its route and verifies the payment before doing the work. Each keeps its own wallet, so the exchange is attributed to both ends. By the end you have a working round trip: agent A asks agent B for something, pays for it, and gets the result.
This page is where the pay side and the receive side meet. For paying in general see how-to-pay-ai-agent-with-crypto; for receiving see how-to-receive-payments-as-ai-agent; the concept is in the agent-to-agent payment glossary entry. The payment API product page is the broader reference.
Two agents, two wallets, one handshake
The mental model is simple once you see it: an agent-to-agent payment is nothing new, it is the ordinary x402 handshake with an agent on both ends instead of an agent and a server.
The paying agent is a client. It uses createX402Client to call the other agent and answer the 402. The paid agent is a server. It uses the x402 server adapter to quote a price and verify payment. The two never share state; they meet only on the wire, through one 402 and one paid retry. Each agent has its own wallet, so the payment moves from the payer's wallet to the payee's, and the dashboard records both sides. There is no special agent-to-agent API to learn, because there does not need to be one. If you can pay a service and you can charge for one, you can do agent-to-agent, because that is all it is.
Prerequisites
- A Blockchain0x account with two agents created, a payer and a payee, each with its own wallet.
sk_test_keys for both, on the same network (Base Sepolia).- The payee's
payToAddressand apaymentRequestIdfor its priced route. - Node 18 or newer. The SDK targets
>=18.
The paying agent
The payer calls the other agent through the x402 client, which answers the 402 and retries.
import { createClient } from "@blockchain0x/node";
import { createX402Client } from "@blockchain0x/x402/client";
const payerSdk = createClient({ apiKey: process.env.PAYER_B0X_API_KEY! });
const fetchWithPay = createX402Client({ sdk: payerSdk });
// Call the other agent's paid route. The payee quotes the price in its 402;
// the payer's own spend limit decides whether to settle it.
const res = await fetchWithPay("https://agent-b.example.com/agent/answer", {
method: "POST",
body: JSON.stringify({ question: "..." }),
});
const answer = await res.text();The payer never hardcodes agent B's price; it pays whatever B quotes, bounded by the payer's per-transaction cap. If B quotes more than the payer is allowed to spend in one payment, the payer's wallet refuses before any USDC moves, which is the protection that lets an agent pay a counterparty it does not fully control.
The paid agent
Agent B is a server. It gates its route with the adapter and verifies payment before doing the work.
import Fastify from "fastify";
import { createClient } from "@blockchain0x/node";
import { createX402Plugin } from "@blockchain0x/x402/server/fastify";
const payeeSdk = createClient({ apiKey: process.env.PAYEE_B0X_API_KEY! });
const app = Fastify();
await app.register(createX402Plugin, {
sdk: payeeSdk,
defaultNetwork: "testnet",
pricing: { "POST /agent/answer": { amountUsdc: "0.05", payToAddress: process.env.PAYEE_PAYTO_ADDRESS!, paymentRequestId: "pr_answer" } },
});
app.post("/agent/answer", async (req) => doWork(req.body)); // runs once the payment verifies
await app.listen({ port: 8080 });B quotes five cents, verifies the payer's X-Payment header through paymentRequests.settle, and only then runs doWork. From B's side, the payer is just a caller that paid; B does not need to know it is another agent.
Keep attribution clean
The reason to give each agent its own wallet shows up here. Because the payment moves between two distinct wallets, the dashboard records it on both: a payment.sent on the payer and a payment.received on the payee. Subscribe to those events on each agent and you get a clean ledger of who paid whom, for how much, when.
This is what makes multi-agent systems legible. In a manager-and-delegates design, the manager pays each delegate through this handshake, and the per-agent wallets give you a cost breakdown per delegate without any custom accounting: the manager's payment.sent stream is its spend, each delegate's payment.received stream is its revenue, and they reconcile against each other. Share one wallet across the agents and all of that collapses into an untraceable blur. Two wallets is not overhead; it is the feature that makes agent-to-agent commerce auditable.
Trust between agents that have not met
When both agents are yours, trust is a given. The interesting case is when they are not: your agent pays an agent built by someone else, or a stranger's agent pays yours. There is no prior relationship, so each side has to decide whether to transact based on what it can check.
Each side checks the other's public profile. Before your agent pays an unfamiliar agent, it can look at that agent's profile and verification badges, the same wallet.blockchain0x.com/a/{slug} page a person would check, and refuse to pay an unverified address. Before your agent serves a paying caller, the payment itself is the gate: the caller already settled real USDC, which is a stronger signal than any claim. So the trust is asymmetric and that is fine. The payer leans on identity, checking who it is about to pay, and the payee leans on settlement, requiring real money before it does work. Between the payer's spend limit, the payee's verification, and the finality of the payment, two agents that have never met can transact safely, which is the property that makes an open agent economy possible rather than just a closed set of agents you own.
Test the round trip
Run both agents on Base Sepolia with sk_test_ keys. Start agent B with its proxy or server, fund agent A's wallet with test USDC, and have A call B. Confirm the full cycle: A gets a 402, settles, retries, and receives B's result, while A logs a payment.sent and B logs a payment.received for the same amount. Then exercise the limit: set A's per-transaction cap below B's price and confirm A refuses to pay rather than overspending. When the happy path and the refusal both behave, move both agents to sk_live_ keys on the same mainnet.
Common pitfalls
Three traps in agent-to-agent payments.
Agents on different networks. A payer on Base Sepolia cannot pay a payee quoting Base mainnet. Keep both agents on the same network; mismatches simply fail to settle.
One shared wallet for both agents. You lose the attribution that makes the exchange auditable, and one agent can spend the other's balance. Give each its own wallet.
No spend limit on the payer. An agent paying another agent it does not control needs a per-transaction cap, so a wrong or hostile quote is refused rather than paid. Set the payer's limit before wiring the call.
What to ship today
Create two agents, gate one route on the payee with the adapter, point the payer at it with createX402Client, and run the round trip on Base Sepolia. Confirm the payment.sent and payment.received events match, and that the payer's limit refuses an over-cap quote. For the concept see the agent-to-agent payment glossary entry. Pricing is on the pricing page.