What you will build
An AutoGen agent that pays for the tools it calls, settled in USDC on Base. The agent reasoning loop does not change. You add one function tool that calls a small local proxy, and the proxy handles payment. To the agent it is a normal tool that returns a result; underneath, a paid call settles through the agent's wallet.
The honest constraint first: the x402 payment client ships for Node, and AutoGen is Python. So the shipped path is a tiny Node proxy that holds the wallet and answers 402 Payment Required, with your AutoGen tool calling it over localhost. This is the same pattern the CrewAI guide uses, because both are Python frameworks. The AutoGen integration page is the broader reference, and the payment API product page covers the wider surface.
Why a Node proxy
It is worth a sentence on why the proxy, rather than reaching for a Python SDK call, is the right move and not a workaround.
The payment layer that answers a 402 and settles in USDC, @blockchain0x/x402, is a Node package. There is no Python equivalent yet, and inventing an import that does not exist would just produce code that fails to install. The proxy is the honest bridge: a real, running Node process that does the real x402 work, exposed to your Python agent over a localhost call it already knows how to make. It keeps every payment identifier on the verified Node surface, and it costs you one extra process per wallet, which any team already running sidecars knows how to operate. When a Python x402 client ships, this collapses to a direct call; until then, the proxy is the path that actually works.
Prerequisites
- A Blockchain0x account with one agent created, so it has a wallet.
- A
sk_test_API key for this walkthrough. - Node 18+ for the proxy (the SDK targets
>=18) and Python 3.10+ for AutoGen.
export B0X_API_KEY=sk_test_... # sk_test_ -> Base SepoliaRun the x402 proxy
This is the only Node you write. It builds a fetch that answers 402s and exposes one route your AutoGen tool can post a URL to.
// pay-proxy.mjs -> npm i @blockchain0x/node @blockchain0x/x402 && node pay-proxy.mjs
import http from "node:http";
import { createClient } from "@blockchain0x/node";
import { createX402Client } from "@blockchain0x/x402/client";
const sdk = createClient({ apiKey: process.env.B0X_API_KEY }); // sk_test_ pins it to Base Sepolia
const fetchWithPay = createX402Client({ sdk });
http
.createServer(async (req, res) => {
const chunks = [];
for await (const c of req) chunks.push(c);
const { url } = JSON.parse(Buffer.concat(chunks).toString() || "{}");
try {
const upstream = await fetchWithPay(url); // settles the 402 in USDC, retries
res.writeHead(upstream.status, { "content-type": upstream.headers.get("content-type") ?? "text/plain" });
res.end(await upstream.text());
} catch (err) {
res.writeHead(502).end(String(err));
}
})
.listen(8787, () => console.log("pay-proxy on :8787"));Start it next to your agent: node pay-proxy.mjs. One proxy per agent wallet, each on its own port with its own B0X_API_KEY.
Wrap it in an AutoGen tool
In AutoGen, a tool is a Python function the agent can call. Write one that calls the proxy and register it with the agent. No Blockchain0x code lives in Python.
import requests
from autogen_agentchat.agents import AssistantAgent
def get_quote_realtime(ticker: str) -> str:
"""Fetch a real-time quote for a stock ticker."""
upstream = f"https://quotes.example.com/v1/quote?ticker={ticker}"
res = requests.post("http://127.0.0.1:8787", json={"url": upstream}, timeout=30)
return res.text if res.status_code == 200 else f"Lookup failed: {res.status_code}"
researcher = AssistantAgent(
name="researcher",
model_client=your_model_client,
tools=[get_quote_realtime],
system_message="You are an equity research analyst.",
)The agent calls get_quote_realtime like any other tool. The proxy settles the upstream 402 in USDC, and the result comes back. The agent never sees the payment; it just gets the quote.
Give each agent its own wallet
AutoGen shines in multi-agent setups, and that is exactly where per-agent wallets pay off. Run one proxy per agent that pays, each pointed at that agent's key and port, and give each agent a tool that calls its own proxy.
That buys you the same three things per-agent wallets always do: isolation, so a prompt-injected agent only spends its own allowance; attribution, so the dashboard shows which agent spent what; and per-role limits, so a research agent that calls premium data can have a different ceiling than a summarizer that mostly calls an LLM. In a group chat where several agents act, this is what keeps cost legible instead of a single shared blur.
A two-agent example
Make it concrete with two agents that pay independently. A researcher calls a paid data API, and a writer calls a paid grammar service. Each gets its own proxy on its own port, each pointed at its own agent's key.
def research_lookup(query: str) -> str:
"""Look up market data (paid)."""
r = requests.post("http://127.0.0.1:8787", json={"url": f"https://data.example.com/q?x={query}"}, timeout=30)
return r.text if r.status_code == 200 else f"failed: {r.status_code}"
def polish_text(draft: str) -> str:
"""Polish a draft (paid)."""
r = requests.post("http://127.0.0.1:8788", json={"url": "https://grammar.example.com/polish"}, timeout=30)
return r.text if r.status_code == 200 else f"failed: {r.status_code}"
researcher = AssistantAgent(name="researcher", model_client=mc, tools=[research_lookup])
writer = AssistantAgent(name="writer", model_client=mc, tools=[polish_text])Two proxies run, one on :8787 with the researcher's key and one on :8788 with the writer's key. Now a group chat between them produces two clean spend streams in the dashboard, one per agent, each bounded by its own limit. If the writer is ever compromised, it cannot touch the researcher's balance, because they are different wallets behind different proxies. That isolation is the whole reason to run one proxy per agent rather than sharing.
Set spend limits and test
Set the limit in the dashboard, where the wallet enforces it before any payment, and read it back to confirm:
import os, requests
res = requests.get(
f"https://api.blockchain0x.com/v1/agents/{os.environ['RESEARCHER_AGENT_ID']}/spend-permissions",
headers={"Authorization": f"Bearer {os.environ['B0X_API_KEY']}"},
timeout=30,
)
permissions = res.json() # per_tx_wei, allowance_wei, period_seconds, ...Run the whole loop on Base Sepolia first with a sk_test_ key: fund the agent's wallet with test USDC from a public faucet, start the proxy, and watch a paid tool call settle. Exercise a refused payment too, by setting a tight per_tx_wei and confirming an over-limit call comes back as a non-2xx the tool handles. When the happy path and the refused path both behave, swap to a sk_live_ key for Base mainnet.
Common pitfalls
Three traps.
Exposing the proxy beyond localhost. The proxy holds a wallet key and pays whatever URL it is handed. Bind it to 127.0.0.1, never a public interface, and treat it like the credential it is.
One wallet for a whole group chat. You lose attribution and isolation. Run a proxy per agent that pays, so each settles on its own wallet.
Putting the budget in the system message. A budget in an agent's system message is a suggestion a crafted input can talk past. The dashboard spend limit is the real boundary; the system message is for behavior.
What to ship today
Start one pay-proxy.mjs with a sk_test_ key, register one AutoGen function tool that calls it, and watch a paid call settle on Base Sepolia. Set a dashboard spend limit before you switch to a sk_live_ key. The CrewAI version of this exact pattern is at how-to-add-payments-to-crewai-agent; the Node-native, no-proxy version is how-to-add-payments-to-langchain-agent. Pricing is on the pricing page.