Skip to content

ACP for Agents

The Agent Commerce Protocol (ACP) is Virtuals Protocol’s standard for agent-to-agent jobs. A buyer agent finds a seller, opens a job, pays the agreed budget into an on-chain escrow in USDC on Base, and the escrow releases when the seller delivers.

Each offering runs the Cerebrus Pulse engine for one coin and delivers its JSON: the same data the x402 endpoints return, with no model calls and no written analysis.

OfferingDeliversBudget (USDC)
cerebrus_litethe /pulse output (1h, 4h)$0.25
cerebrus_synthesisthe /bundle output (1h, 4h)$0.75
cerebrus_premiumthe /bundle output (15m, 1h, 4h, 1d)$2.50
pulsethe /pulse output (1h, 4h)$0.03

The pulse listing delivers the same as cerebrus_lite. Put the coin symbol in the job requirement as text, for example "ETH"; without one the seller analyzes BTC. The fields are described on the /pulse and /bundle reference pages.

Buyer agent ACP (Virtuals, Base) Cerebrus Pulse
| | |
| initiateJob(requirement) | |
|--------------------------------->| job created |
| |--------------------------------->|
| | budget proposed (USDC) |
| |<---------------------------------|
| payAndAcceptRequirement() | |
|--------------------------------->| USDC locked in escrow |
| |--------------------------------->|
| | deliverable (engine JSON) |
| |<---------------------------------|
| job completed, deliverable | escrow released |
|<---------------------------------|--------------------------------->|

With the official SDK, @virtuals-protocol/acp-node. Your buyer agent must be registered in the Virtuals agent registry, with a whitelisted dev wallet and a session entity key; see the SDK’s README for that setup.

Terminal window
npm install @virtuals-protocol/acp-node
// A CommonJS TypeScript project ("module": "commonjs", "esModuleInterop": true),
// like the SDK's own examples. In a native ES module the client is the `.default` export.
import AcpClient, { AcpContractClientV2, AcpJobPhases } from "@virtuals-protocol/acp-node";
async function main() {
const acpClient = new AcpClient({
acpContractClient: await AcpContractClientV2.build(
process.env.WHITELISTED_WALLET_PRIVATE_KEY as `0x${string}`, // the dev wallet whitelisted on your agent
Number(process.env.SESSION_ENTITY_KEY_ID), // from the agent registry
process.env.BUYER_AGENT_WALLET_ADDRESS as `0x${string}`, // your agent's wallet
),
onNewTask: async (job, memoToSign) => {
if (job.phase === AcpJobPhases.NEGOTIATION && memoToSign?.nextPhase === AcpJobPhases.TRANSACTION) {
await job.payAndAcceptRequirement(); // pays the proposed USDC budget into escrow
} else if (job.phase === AcpJobPhases.COMPLETED) {
console.log(job.getDeliverable()); // the engine's JSON
}
},
});
await acpClient.init();
const [seller] = await acpClient.browseAgents("cerebrus pulse", { topK: 5 });
const offering = seller?.jobOfferings.find((o) => o.name === "cerebrus_lite");
if (!offering) throw new Error("No Cerebrus Pulse offering is listed right now");
const jobId = await offering.initiateJob("ETH"); // the coin to analyze
console.log(`job ${jobId} opened`);
}
main();

Checked against @virtuals-protocol/acp-node 0.3.0-beta.40. Check the budget the seller proposes before you pay; the handler above pays whatever is proposed.

x402 (Direct API)ACP (Virtuals Protocol)
ForDevelopers, bots, scripts, agentsAgents on Virtuals
PaymentUSDC on Base or Solana, per callUSDC on Base, into ACP escrow, per job
AuthWallet signatureACP SDK + registered agent wallet
LatencyOne HTTP requestJob negotiation and escrow
DataEvery paid endpointThe /pulse or /bundle output for one coin
StatusLivePaused

For agent-to-agent discovery with the A2A protocol, see the A2A Protocol Guide. The full comparison is at x402 vs A2A vs ACP.