Get started
Quickstart
Run the complete local stack — chain, mock USDC, merchant API, gateway and dashboard — and watch an agent buy something.
The quickstart runs everything locally and disposably: a private Anvil chain, a mock USDC token, a demo merchant API, the gateway and a dashboard. There are no API keys, no real money and no manual blockchain setup.
Run the demo
- Clone and install
bash git clone https://github.com/devlab-group/agent-commerce.git cd agent-commerce npm install - Start the stack
bash docker compose upCompose starts five services. Every published port binds to 127.0.0.1 only.
Service Port What it is anvil8545 private EVM chain, Anvil's default chain id 31337 chain-deploy— deploys MockUSDC, funds the buyer, writes .deploy/local.json merchant-api3000 the “existing” merchant backend gateway8080 Agent Commerce Gateway with config-demo.yaml dashboard5173 read-only live view of requests Linux onlyIf your user is not UID/GID 1000 (check withid -u && id -g), exportDOCKER_UID=$(id -u) DOCKER_GID=$(id -g)beforedocker compose up, so the deployment manifest stays host-writable. Docker Desktop on macOS and Windows does not need this. - Verify the whole stack
In a second terminal:
console $ npm run agent-commerce -- doctor --config config-demo.yaml PASS Config valid: 2 resource(s), merchant "Demo Data Store" (using local chain manifest .deploy/local.json for X402_ASSET, X402_ASSET_NAME, X402_ASSET_VERSION, X402_ASSET_DECIMALS, MERCHANT_WALLET, X402_FACILITATOR_PRIVATE_KEY) PASS Gateway healthy and ready at http://127.0.0.1:8080 PASS Backend 2/2 backend host(s) reachable PASS Protocols http=on mcp=on (/mcp) a2a=off acp=off INFO A2A disabled INFO ACP disabled INFO AP2 disabled PASS Payments x402 v2 (scheme=exact) enabled - LOCAL dev chain (eip155:31337), destination=0x7099…79C8, facilitator=local (in-process), paymentFlow=authorization INFO Payments (MPP) MPP not configured PASS Storage sqlite schema v5 writable; receipts=2 PASS Transactions none unresolved PASS Protocol versions reported by gateway /.well-known/agent-commerce Score: 8/8 checks passedThat is real output from a gateway running on the host. Run on the host against this Docker stack, the Storage check warns instead, because the compose gateway keeps its receipts in the
receipts:/workspace/datavolume, not in./dataon the host:console WARN Storage no store at "./data/receipts.sqlite" and its directory does not exist. Both are created when the gateway starts, provided the path is writable. If the gateway is already running, this path does not match the one it uses. Score: 6/7 checks passeddoctorexits non-zero only when a check fails; aWARNdoes not. - Watch an agent buy something
console $ npm run demo:agent [agent] Agent Commerce Gateway: deterministic buyer demo [buyer] balance before 100 MockUSDC [agent] merchant balance before 0 MockUSDC [gateway] connected to http://localhost:8080/mcp [agent] discovered 2 tool(s): weather_basic, market_report [agent] paid tool: "market_report", free tool: "weather_basic" [gateway] 402 payment required: 0.01 USDC, 1 offer(s) [buyer] challenge checked before signing: pays 10000 units of 0x5fbd…0aa3 to 0x7099…79C8 [buyer] signed x402 payment proof (LOCAL DEVELOPMENT ONLY - DO NOT FUND) [gateway] delivered "market_report" [receipt] id=receipt_484b…1eba requestId=mcp_4292…c930 status=settled settlementTx=0x0b19…1c7f [buyer] balance after 99.99 MockUSDC (Δ -0.01) [agent] merchant balance after 0.01 MockUSDC (Δ 0.01) [agent] on-chain balance changes match the expected payment amount [gateway] delivered free tool "weather_basic": {"city":"Paris","temperatureC":-4,…} [agent] demo complete: free and paid delivery both verifiedThe deterministic buyer discovers the tools over MCP, checks the payment challenge before signing and signs an x402 authorization. The gateway calls the merchant backend, then settles the payment on the local chain before it returns the result. The balance change confirms settlement — an HTTP status alone does not. This output is shortened; balances, IDs and hashes vary by run.
- Open the dashboard
http://localhost:5173polls the gateway'sGET /api/eventsroute and shows the same request within a few seconds.
Stop and reset
docker compose down -vThis removes every container, which resets the chain, and deletes the receipts volume. .deploy/local.json is a bind mount, so it stays on the host until the next chain-deploy rewrites it.
What just happened
A successful paid request produces this event sequence, all sharing one requestId:
resource.requested → payment.verified → backend.called → payment.settled
→ resource.deliveredThe unpaid call that fetched the challenge is a separate request with its own requestId: resource.requested → payment.required.