AI agents can now do real work: they call APIs, write code, analyze datasets, and make decisions on behalf of other agents or humans. The next problem is not capability. It is commerce. When Agent A needs Agent B to perform a task, how does payment actually happen? How do you handle the case where Agent B delivers garbage? What if the task costs $0.001? What if it costs $500?
In 2026, three protocols have emerged to solve agent-to-agent payments, each with different tradeoffs. This post compares them technically and honestly. No marketing, just how they work, what they are good for, and when you should use each.
x402: HTTP 402 Payment Required
x402 is an open protocol that repurposes the HTTP 402 status code for machine-to-machine payments. Originally created by Coinbase, x402 became a Linux Foundation project in April 2026 with founding members Coinbase, Stripe, Cloudflare, Shopify, and the Solana Foundation. Industry participants include AWS, Google, Microsoft, Visa, Mastercard, and American Express. The protocol has processed over 50 million transactions with annualized volume exceeding $200 million. The core idea is straightforward: if a resource costs money, the server tells you with a standard HTTP response, and your client pays.
How it works
- Agent sends
GET /weatherto a resource server. - Server responds with
402 Payment Requiredand payment requirements in headers: price, accepted networks, recipient address. - Agent selects a payment scheme (e.g., "exact" on Base), signs a payment payload with its wallet, and retries with a payment header.
- A facilitator verifies the payment and settles on-chain. The CDP facilitator (operated by Coinbase) offers a free tier of 1,000 transactions/month, then $0.001/transaction. Cloudflare runs its own facilitator with native edge integration, so any Cloudflare-proxied API can accept x402 payments with a configuration change.
- Server returns
200 OKwith the data.
The protocol is network-agnostic by design. Payment "schemes" define the mechanics (e.g., exact for fixed-price payments), and "networks" define where settlement happens. Supported networks: Base, Polygon, Arbitrum, World, and Solana. EVM networks use EIP-3009 for USDC/EURC transfers or Permit2 for any ERC-20 token.
V2, launched alongside the Linux Foundation announcement, adds several capabilities:
- Wallet-based identity: After a first successful payment, the server recognizes the wallet. Subsequent requests skip re-payment for the session, reducing overhead for repeat callers.
- Automatic API discovery: Clients can discover available paid endpoints and their pricing without prior knowledge of the server's API surface.
- Dynamic payment recipients: Servers can route payments to different addresses per request, enabling marketplace and revenue-sharing patterns.
- Plugin-driven SDK architecture: Payment schemes, networks, and facilitators are registered as plugins. Adding a new chain or payment method does not require SDK updates.
- Lifecycle hooks: Servers can attach custom logic at payment verification, settlement, and post-response stages.
Technical details
The server-side integration is a single middleware. Here is the Express.js pattern from the official examples:
import { paymentMiddleware, x402ResourceServer } from "@x402/express";
import { ExactEvmScheme } from "@x402/evm/exact/server";
import { HTTPFacilitatorClient } from "@x402/core/server";
const facilitatorClient = new HTTPFacilitatorClient({ url: FACILITATOR_URL });
app.use(
paymentMiddleware(
{
"GET /weather": {
accepts: [
{ scheme: "exact", price: "$0.001", network: "eip155:8453", payTo: ADDRESS },
],
description: "Weather data",
},
},
new x402ResourceServer(facilitatorClient)
.register("eip155:8453", new ExactEvmScheme()),
),
);
The client side wraps fetch with automatic payment handling:
import { x402Client, wrapFetchWithPayment } from "@x402/fetch";
import { ExactEvmScheme } from "@x402/evm/exact/client";
import { privateKeyToAccount } from "viem/accounts";
const signer = privateKeyToAccount(EVM_PRIVATE_KEY);
const client = new x402Client();
client.register("eip155:*", new ExactEvmScheme(signer));
const fetchWithPayment = wrapFetchWithPayment(fetch, client);
const response = await fetchWithPayment("https://api.example.com/weather");
SDKs are available for TypeScript (@x402/fetch, @x402/express), Python (x402), and Go.
What it is good for
Pay-per-call APIs, data services, and micropayments. No accounts, no API keys, no pre-registration. An agent with a wallet and USDC can start paying for resources immediately. With facilitator free tiers and $0.001/transaction beyond that, protocol-level cost overhead is negligible for most use cases.
Limitations
x402 is stateless by design. There is no escrow, no dispute mechanism, and no concept of a "job" that spans multiple interactions. Payment is instant and final. If the server returns bad data after you pay, there is no protocol-level recourse. V2's wallet-based identity reduces friction for repeat callers but does not change the fundamental model: each payment is a settled transaction, not a conditional one. This is the right tradeoff for micropayments, but the wrong one for high-value work.
ACP: Agent Commerce Protocol (Virtuals Protocol)
ACP is a full commerce framework built by Virtuals Protocol. Where x402 handles single-request payments, ACP manages the entire lifecycle of a commercial relationship between agents: discovery, negotiation, escrow, delivery, and evaluation.
How it works
ACP decomposes agent commerce into three layers: accounts (persistent relationships between two agents), jobs (individual commercial engagements with escrow), and memos (typed messages within a job: requirements, proposals, deliverables, status updates).
The core architecture, as of ACP v2.0, is built on LayerZero v2 for cross-chain support and uses a modular router pattern:
- ACPRouter: Central entry point with upgradeable modules.
- AccountManager: Agent lifecycle and relationship tracking.
- JobManager: Job creation, state transitions, and escrow.
- PaymentManager: Fee handling and settlement.
- AssetManager: Cross-chain transfers via LayerZero OApp.
Agents register in an on-chain service registry with "job offerings," which are priced services with SLAs, JSON-schema-validated requirements, and deliverable specifications. A client agent discovers a provider via the registry, initiates a job, and funds escrow in USDC.
The job state machine: open -> budget_set -> funded -> submitted -> completed | rejected | expired.
ACP v2.0 introduced a hook-based extensibility model. Instead of fixed callbacks, agents attach beforeAction and afterAction hooks at job creation. Separate deployable hook contracts (e.g., FundTransferHook for jobs that manage client funds) add capabilities without modifying core contracts.
Technical details
Based on the ACP Node.js SDK (@virtuals-protocol/acp-node):
import AcpClient, { AcpContractClientV2 } from "@virtuals-protocol/acp-node";
// Initialize with delegated signing (v2.0 uses session keys, not raw private keys)
const acpClient = new AcpClient({
acpContractClient: await AcpContractClientV2.build(
WALLET_PRIVATE_KEY,
SESSION_ENTITY_KEY_ID,
AGENT_WALLET_ADDRESS,
),
onNewTask: (job) => { /* handle incoming job requests */ },
onEvaluate: (job) => { /* handle evaluation requests */ },
});
await acpClient.init();
// Discover agents by capability
const agents = await acpClient.browseAgents({
cluster: "data-analysis",
sort: "SUCCESS_RATE",
});
// Initiate a job from a provider's offering
const offering = agents[0].offerings[0];
await offering.initiateJob({ requirement: "Analyze Q1 sales data" });
Agent identity in ACP is composite: a non-custodial EVM wallet (Base, BSC), an agent card (profile metadata), and optionally a token for on-chain representation. v2.0 implements delegated signing, where a registered signer key approves transactions on the wallet's behalf, so raw private keys stay out of application code.
Fee distribution is 95/5 (provider/protocol) for direct jobs, or 90/5/5 (provider/evaluator/protocol) when a third-party evaluator verifies deliverables.
Supported networks: Base, Ethereum, Polygon, Arbitrum, BNB Chain, with testnet variants for each.
What it is good for
ACP solves the identity and coordination layer. On-chain agent registry with reputation, cross-chain job execution, and a built-in evaluator role for third-party verification. If you are building agents that need persistent identities, discoverable service listings, and cross-chain settlement, ACP provides the infrastructure.
Limitations
ACP requires agents to register via the Virtuals service registry before they can transact. This is a deliberate design choice (it enables discovery and reputation), but it adds friction compared to x402's zero-registration model. The protocol fee is 5% minimum, which is meaningful for high-frequency, low-value transactions. The modular architecture, with LayerZero, hooks, and multiple manager contracts, introduces more integration surface area than a stateless payment header.
Escrow-based Settlement: MeshLedger
MeshLedger takes a different approach: a purpose-built escrow contract on Base L2, paired with an API layer that handles job lifecycle and a three-tier dispute resolution system. The focus is on protecting both parties in higher-value, asynchronous work where delivery is not instant.
How it works
- Customer agent creates a job via the API, specifying requirements, price, and required skills.
- The backend funds escrow on-chain: the customer deposits
listedPrice + 2% service feein USDC to the escrow contract. - A deliverer agent accepts the job (on-chain state:
FUNDED -> ACCEPTED). - Deliverer submits work (on-chain state:
ACCEPTED -> DELIVERED), which starts a 24-hour timer. - Within 24 hours, the customer either:
-
Releases payment (deliverer gets
listedPrice, treasury gets the 2% fee), or -
Files a dispute (job enters
DISPUTEDstate, AI judge evaluates).
-
Releases payment (deliverer gets
- If neither party acts within 24 hours, auto-release triggers. This is permissionless: anyone can call it once the timer expires.
The dispute resolution system has three tiers:
- Tier 1 (Automatic): Both parties agree. Payment releases normally or auto-releases after 24 hours.
-
Tier 2 (AI Judge): The judge receives the job spec, deliverable, dispute reason, and agent profiles. It evaluates requirements line by line and issues one of four rulings:
deliverer_wins,customer_wins,partial(with a split percentage, e.g., 70/30), orfraud_detected. Partial rulings distribute thelistedPriceproportionally; the 2% service fee always goes to treasury. - Tier 3 (Community Review): After an AI judge ruling, qualified reviewers (reputation >= 50, 10+ completed transactions) vote agree/disagree over 72 hours. If 70%+ disagree, the ruling is flagged for model improvement. Community review is an accountability mechanism; it does not reverse fund transfers.
Technical details
The escrow contract is deployed and verified on Base mainnet at 0x9Fc44aAC2f677D2190c4C59eEB69c3c3c1ea3dA1. The Solidity source:
// Key constants from MeshLedgerEscrow.sol
uint256 public constant FEE_BASIS_POINTS = 200; // 2%
uint256 public constant AUTO_RELEASE_DELAY = 86_400; // 24 hours
enum JobStatus {
FUNDED, ACCEPTED, DELIVERED, RELEASED, AUTO_RELEASED,
DISPUTED, RESOLVED, REFUNDED, CANCELLED
}
enum DisputeRuling { DELIVERER_WINS, CUSTOMER_WINS, PARTIAL }
// Partial ruling: splitPercentage% of listedPrice to deliverer (1-99 only)
function resolveDispute(uint256 _jobId, uint8 _ruling, uint256 _splitPercentage)
external nonReentrant onlyRole(DISPUTE_RESOLVER_ROLE)
The contract uses OpenZeppelin's ReentrancyGuard, AccessControl, and SafeERC20. Only whitelisted ERC-20 tokens are accepted. Permission rules are strict: releasePayment() is customer-only, refund() is deliverer-only, resolveDispute() requires DISPUTE_RESOLVER_ROLE, and autoRelease() is permissionless but time-gated.
The SDK (@meshledger/sdk on npm) wraps the full lifecycle:
import { MeshLedger } from "@meshledger/sdk";
const ml = new MeshLedger({ apiKey: "ml_sk_..." });
// 1. Create a job (funds escrow on-chain with USDC)
const { job_id } = await ml.jobs.create({
title: "Security audit of ERC-20 contract",
description: "Full audit with gas optimization recommendations",
price_usdc: 10,
chain: "base",
token: "USDC",
skills_required: ["solidity", "security"],
});
// 2. Deliverer accepts
await ml.jobs.accept(job_id);
// 3. Deliverer submits work
await ml.jobs.deliver(job_id, {
deliverable: { report_url: "https://...", findings: 3, severity: "medium" },
});
// 4. Customer releases payment (or disputes within 24hrs)
await ml.jobs.release(job_id);
// Alternative: file a dispute
await ml.jobs.dispute(job_id, {
reason: "Report only covers 2 of 5 contract files",
evidence: "Scope specified all files in /contracts directory...",
});
MeshLedger also implements x402 as an alternative authentication method. Routes that normally require an API key can instead accept an X-Payment-TxHash header with a verified USDC transfer. The server verifies the on-chain transaction (correct amount, correct recipient, sufficient confirmations) and marks the tx hash as used in Redis with a 7-day TTL for replay protection. This means an agent with no API key and no registration can create jobs, accept work, and file disputes by paying per-request in USDC.
What it is good for
Jobs where the deliverable needs verification before payment, higher-value work (tens to hundreds of USDC), and scenarios where disputes are a realistic possibility. The 2% fee is the lowest of the three protocols. The auto-release timer prevents funds from being locked indefinitely. The combination of AI judge rulings with on-chain partial splits handles the common case where both parties are partially right.
Limitations
Escrow adds gas overhead compared to a direct transfer. The contract is currently deployed on Base L2 only (a Solana Anchor program is compiled but not yet deployed to mainnet). Both parties need to be registered agents with API keys, unless using the x402 payment path. The system is designed for job-based work and is not optimal for sub-dollar micropayments where the gas cost of escrow creation would exceed the transaction value.
Decision Matrix
| Use Case | Best Protocol | Why |
|---|---|---|
| Pay-per-call data API (sub-$1) | x402 | Stateless, no registration, lowest overhead |
| Tokenized agent identity and discovery | ACP | On-chain registry with composite identity |
| High-value job with dispute risk | MeshLedger Escrow | Funds protected, AI judge, partial rulings |
| Sub-cent micropayments at scale | x402 | Minimal fees ($0.001/tx after free tier), HTTP-native |
| Long-running multi-step job | MeshLedger Escrow | Funds locked in escrow until delivery + review |
| Agent governance and coordination | ACP | Hook-based extensibility, evaluator roles |
| Cross-chain agent commerce | ACP | LayerZero v2 across Base, Polygon, Arbitrum, BNB |
| Quick one-off task, low stakes | x402 | Minimal integration, instant finality |
| Job requiring third-party evaluation | ACP | Built-in evaluator role with 5% evaluator fee |
| Job requiring post-delivery dispute resolution | MeshLedger Escrow | Three-tier system with split rulings |
| Zero-registration agent payments | x402 or MeshLedger (via x402) | Both support wallet-only, no-signup flows |
| Fund management tasks (yield, swaps) | ACP | FundTransferHook for principal fund handling |
Code Examples: Agent A Pays Agent B 10 USDC
x402: Pay-per-request
// x402: Agent A pays 10 USDC for a single API call to Agent B's server.
// Payment is atomic with the request. No escrow, no dispute.
import { x402Client, wrapFetchWithPayment } from "@x402/fetch";
import { ExactEvmScheme } from "@x402/evm/exact/client";
import { privateKeyToAccount } from "viem/accounts";
// Agent A's wallet
const signer = privateKeyToAccount(AGENT_A_PRIVATE_KEY);
const client = new x402Client();
client.register("eip155:*", new ExactEvmScheme(signer));
const fetchWithPayment = wrapFetchWithPayment(fetch, client);
// Request hits 402, client auto-pays 10 USDC, retries, gets data
const response = await fetchWithPayment("https://agent-b.example.com/analyze", {
method: "POST",
body: JSON.stringify({ dataset: "q1-sales" }),
});
const result = await response.json();
// Payment settled. No recourse if result is bad.
ACP: Job-based with evaluator
// ACP: Agent A hires Agent B through the Virtuals service registry.
// Funds escrowed, optional evaluator verifies deliverable.
import AcpClient, { AcpContractClientV2 } from "@virtuals-protocol/acp-node";
const acpClient = new AcpClient({
acpContractClient: await AcpContractClientV2.build(
AGENT_A_PRIVATE_KEY,
SESSION_KEY_ID,
AGENT_A_WALLET,
),
});
await acpClient.init();
// Discover Agent B by capability
const agents = await acpClient.browseAgents({ cluster: "data-analysis" });
const offering = agents[0].offerings[0]; // 10 USDC, 60-min SLA
// Initiate job (escrows 10 USDC on-chain)
await offering.initiateJob({ requirement: "Analyze Q1 sales data" });
// Agent B delivers, evaluator approves, funds release automatically
// Distribution: Agent B gets 9 USDC (90%), evaluator 0.50 (5%), protocol 0.50 (5%)
MeshLedger Escrow: Job with dispute protection
// MeshLedger: Agent A creates a job, Agent B delivers, payment releases
// from escrow. If there's a dispute, AI judge rules with partial splits.
import { MeshLedger } from "@meshledger/sdk";
// Agent A (customer)
const customer = new MeshLedger({ apiKey: "ml_sk_..." });
const { job_id } = await customer.jobs.create({
title: "Analyze Q1 sales data",
description: "Statistical analysis with trend identification",
price_usdc: 10, // 10 USDC to deliverer
chain: "base", // + 0.20 USDC service fee (2%) deposited to escrow
token: "USDC",
skills_required: ["data-analysis"],
});
// Agent B (deliverer) accepts and delivers
const deliverer = new MeshLedger({ apiKey: "ml_sk_..." });
await deliverer.jobs.accept(job_id);
await deliverer.jobs.deliver(job_id, {
deliverable: { report: "...", charts: ["..."], trends: ["..."] },
});
// Agent A has 24 hours to review. Three outcomes:
// 1. Release payment
await customer.jobs.release(job_id);
// Agent B receives 10 USDC, treasury receives 0.20 USDC
// 2. File dispute (AI judge rules)
await customer.jobs.dispute(job_id, {
reason: "Missing trend analysis section",
evidence: "Spec required trend identification, deliverable only has raw stats",
});
// AI judge might rule: partial, 60% to deliverer, 40% back to customer
// 3. Do nothing for 24 hours -> auto-release to Agent B
Conclusion
These three protocols are not competing for the same transactions. They solve different layers of the agent economy:
- x402 is the HTTP payment layer, now a Linux Foundation standard with backing from major tech and payment companies. Stateless, zero-friction, pay-per-request. It makes any API endpoint monetizable with a single middleware. The right tool when payment should be as invisible as a request header.
- ACP is the identity and coordination layer. On-chain agent registry, cross-chain job execution, extensible hooks, and an evaluator role. The right tool when agents need persistent identities, discoverable services, and multi-chain settlement.
- Escrow-based settlement is the commerce and trust layer. Funds held until delivery, structured dispute resolution, and partial rulings for the common case where both parties have a point. The right tool when the transaction value justifies protection and the deliverable needs review.
x402's move to the Linux Foundation validates the thesis that agent payments need open standards, not proprietary payment rails. MeshLedger's escrow layer is complementary: x402 handles the "how agents pay" question, MeshLedger handles the "what happens when things go wrong" question. MeshLedger already supports x402 as an alternative authentication path, so agents can use both protocols simultaneously. An agent can pay for a quick data lookup via x402 and, in the same workflow, fund a high-value escrow job through MeshLedger for work that needs delivery verification.
A mature agent economy will likely use all three. An agent might have an ACP identity, charge for quick queries via x402, and settle high-stakes work through escrow. The question is not which protocol wins. It is which one fits the transaction.
MeshLedger's escrow contract is open source and verified on BaseScan. The SDK is on npm. Source code and documentation are at github.com/MeshLedger/MeshLedger and meshledger.io.
Top comments (0)