What you will build
A CrewAI crew whose member agents pay for the services they call, settled in USDC on Base. The agent reasoning loop does not change. The crew manager does not change. You add one tool to an agent's tools list, that tool calls a small local proxy, and the proxy handles payment.
Here is the honest constraint, stated up front so you do not go hunting for a package that is not there. The x402 payment client ships for Node (@blockchain0x/x402). CrewAI is Python. So the shipped path today is a tiny Node proxy that holds the wallet and answers 402 Payment Required on the upstream, and your Python tool calls that proxy over localhost. It is about thirty lines of Node and one extra process. That is it.
This is the CrewAI-specific path through the Blockchain0x payment API. For the broader reference, see the CrewAI integration page.
Prerequisites
Have these ready:
- A working CrewAI crew with at least one
Agentand oneTool. - A Blockchain0x account, with one agent created per CrewAI agent you want to make payment-capable. The add-payments-to-agent guide covers that.
- A
sk_test_API key for this walkthrough. - Node 18+ (for the proxy; the SDK targets
>=18) and Python 3.11+ (for CrewAI).
Set the key in both shells:
export B0X_API_KEY=sk_test_... # sk_test_ -> Base Sepolia, sk_live_ -> Base mainnetRun a local x402 proxy
This is the only Node you write. It builds a fetch that answers 402s, then exposes one route the CrewAI 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 {
// fetchWithPay re-issues the request with an X-Payment: exact-usdc:<base64>
// header once the upstream 402 is settled in USDC.
const upstream = await fetchWithPay(url);
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 listening on :8787"));Start it next to your crew: node pay-proxy.mjs. One proxy per agent wallet; give each its own port and its own B0X_API_KEY.
Wrap the proxy in a CrewAI tool
A CrewAI tool is a BaseTool subclass with a name, a description, and a _run. The _run just calls the proxy. No Blockchain0x code lives in Python at all.
from crewai.tools import BaseTool
import requests
class RealtimeQuoteTool(BaseTool):
name: str = "get_quote_realtime"
description: str = "Fetch a real-time quote for a stock ticker."
def _run(self, ticker: str) -> str:
upstream = f"https://quotes.example.com/v1/quote?ticker={ticker}"
res = requests.post("http://127.0.0.1:8787", json={"url": upstream}, timeout=30)
if res.status_code != 200:
# 402 is handled by the proxy; a non-200 here means the upstream failed
# or the payment was rejected by the agent's spend limit.
return f"Quote lookup failed with status {res.status_code}."
return res.textThe tool's description is what CrewAI's planner reasons about. It does not mention price, and it does not need to. The wallet authorizes the spend at the proxy, invisibly.
Attach the tool to an agent
The payment-aware tool plugs into a CrewAI Agent exactly like an unpaid one.
from crewai import Agent, Crew, Task
researcher = Agent(
role="Market researcher",
goal="Get a real-time quote and write a one-line summary.",
backstory="An equity research analyst.",
tools=[RealtimeQuoteTool()],
verbose=True,
)
task = Task(
description="Get the real-time quote on TSLA and summarise it.",
agent=researcher,
expected_output="A one-line markdown summary with the price.",
)
crew = Crew(agents=[researcher], tasks=[task])
print(crew.kickoff())When kickoff() runs, the planner asks for the quote, the tool calls the proxy, the proxy settles the 402, and the quote comes back. From the crew's side the only observable difference is a small delay on the first paid call.
Per-agent vs per-crew billing
A decision you make in the first hour: one wallet for the whole crew, or one per agent?
Per agent wins almost every time. The crew is a logical grouping; the agent is the thing that decides to spend. Per-agent wallets give you three things:
- Isolation. A prompt injection that hijacks one agent only spends that agent's allowance. The rest of the crew keeps running.
- Attribution. When the dashboard shows the researcher spent more than the summariser, you can price the crew and tune limits per role.
- Independent limits. A researcher hitting premium data APIs needs a different ceiling than a summariser that only calls an LLM.
Share one wallet only for a small internal crew where every agent has the same risk profile and you want a single invoice. Even then the dashboard attributes spend per call, so you keep some visibility.
Set spending limits
You do not set limits from code, and you never put them in the prompt. They live in the dashboard, enforced by the wallet before settlement, which is the one layer prompt injection cannot reach. Read them back to confirm:
import os, requests
agent_id = "agt_..."
res = requests.get(
f"https://api.blockchain0x.com/v1/agents/{agent_id}/spend-permissions",
headers={"Authorization": f"Bearer {os.environ['B0X_API_KEY']}"},
timeout=30,
)
permissions = res.json()
# Each permission carries allowance_wei, per_tx_wei, and period_seconds
# (86400 daily, 604800 weekly, 2592000 monthly), plus start_at / end_at.A call that would break per_tx_wei or exhaust the period allowance_wei is refused before any USDC moves, and your proxy returns a non-200 the tool can surface. Set conservative numbers first, watch a week of real usage, then loosen. The agent-spend-controls guide covers the dashboard side.
Test on Base Sepolia
Run the whole flow on testnet before a sk_live_ key goes anywhere near it. A sk_test_ key settles on Base Sepolia (eip155:84532) with the same shapes as mainnet.
Fund the agent first: copy its wallet address from the dashboard (or its public page at https://wallet.blockchain0x.com/a/{slug}) and send it test USDC from a public Base Sepolia USDC faucet. Then start the proxy with the test key and run the crew.
One honest caveat: outbound settlement is in active rollout, and on some networks the settle path can return a 503 until the chain adapter is live. Your _run already handles a non-200, so a crew run will degrade cleanly rather than crash. The test-agent-payments guide has the full sandbox flow. Production costs are on the pricing page.
Common pitfalls
Four traps worth knowing before week one.
One agent profile for the whole crew. Simpler to set up, expensive in lost isolation and attribution. Create one Blockchain0x agent per CrewAI agent that pays, and run one proxy per wallet.
Putting the budget in the prompt. "Spend at most five dollars" in a backstory is a suggestion a crafted input can talk past. The dashboard spend limit is the real boundary. The prompt shapes behavior; the wallet controls money.
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 0.0.0.0, and do not put it behind a public ingress. Treat it like a credential, because it is one.
Skipping the verification badges. The wallet works without them, but a counterparty deciding whether to accept your agent's payment looks at its public profile. The GitHub and domain badges take about five minutes each via the verify-agent-identity guide and change how merchants treat a new agent.
What to ship today
The shortest path from "I have a CrewAI crew" to "my crew pays for its tools":
- Start one
pay-proxy.mjsper agent wallet, each with its ownsk_test_key. - Point one
BaseTool._runat its proxy. - Add the tool to the agent's
toolslist and runkickoff(). - Set a dashboard spend limit before you switch to
sk_live_.
That is the integration. If your crew is itself a paid service that charges external callers, the receive side lives in the crewai-payment-integration page. For the Node-native version of this pattern, how-to-add-payments-to-langchain-agent wires the x402 client straight into the tool with no proxy.