What you will build
An agent whose purpose is to spend: it decides what it needs, pays for it in USDC on Base, and does so without a human in the loop, inside a budget a human set once. Think of an agent that procures data, buys tools, or pays other agents to get its job done. The spending is not a side effect; it is the core behavior you are building. By the end the agent picks what to pay for, pays it, and cannot exceed its limit.
This is the full build of a spender. To add payment to an agent built for something else, see how-to-let-ai-agent-pay-autonomously; to build an agent that gets paid rather than pays, see how-to-build-a-paid-ai-agent. The payment API product page is the broader reference.
The three parts of a spender
An autonomous payment agent is three parts, and the design effort is mostly in the third.
The first is a wallet, funded, the source of the money. The second is a pay capability, a fetch that settles 402s, so the agent can actually pay. The third, and the one that makes it an agent rather than a script, is a decision loop: the logic that decides what to pay for and when. A script pays for a fixed thing; an agent evaluates options, picks, and pays. Most of building a good autonomous spender is building a decision loop that spends wisely, with the wallet and the pay capability as plumbing underneath. And wrapped around all three is the limit, the thing that makes autonomous spending safe to turn on at all.
Prerequisites
- A Blockchain0x account with an agent created and its wallet funded with test USDC.
- A
sk_test_API key for this walkthrough. - Node 18 or newer. The SDK targets
>=18.
export B0X_API_KEY=sk_test_... # sk_test_ -> Base SepoliaGive it a wallet and a pay capability
Build the client and a fetch that pays. This is the plumbing the decision loop calls.
import { createClient } from "@blockchain0x/node";
import { createX402Client } from "@blockchain0x/x402/client";
const sdk = createClient({ apiKey: process.env.B0X_API_KEY! });
const fetchWithPay = createX402Client({ sdk });
// A helper the decision loop uses to actually buy something.
async function buy(url: string, init?: RequestInit) {
const res = await fetchWithPay(url, init);
if (!res.ok) return { ok: false, status: res.status };
return { ok: true, body: await res.text() };
}buy is the agent's hand: give it a URL and it pays for and returns the result, or reports a clean failure. The decision loop decides which URLs to hand it.
Give it a decision loop
The decision loop is the agent. A simple shape: gather options, score them against the goal and cost, pick, pay, and repeat until done or out of budget.
async function run(goal: string) {
while (!satisfied(goal)) {
const options = await discoverPaidOptions(goal); // each has a url and a quoted or known price
const choice = chooseBest(options, goal); // your scoring: value per cost toward the goal
if (!choice) break; // nothing worth buying
const result = await buy(choice.url, choice.init);
if (!result.ok) {
// payment refused (limit), insufficient funds, or upstream failure
recordAndAdapt(choice, result.status); // do not retry the same buy blindly
continue;
}
incorporate(result.body, goal);
}
}The important discipline is in the failure branch: when buy fails, the agent adapts rather than retrying the same purchase. A refused payment is information, the agent is at a limit or the option is too expensive, so it should pick a different option or stop, not loop on the same call. An autonomous spender that retries a refused buy is the most common way these agents waste cycles and look like runaways.
Bound it with a limit
Autonomy plus money requires a hard ceiling, and it must live where the agent cannot reach it. Set a per-transaction cap and a per-period allowance in the dashboard; the wallet enforces both before settlement. Read them back to confirm:
const res = await fetch(
`https://api.blockchain0x.com/v1/agents/${agentId}/spend-permissions`,
{ headers: { Authorization: `Bearer ${process.env.B0X_API_KEY!}` } },
);
const permissions = await res.json(); // per_tx_wei, allowance_wei, period_secondsSet per_tx_wei to the most a single sensible purchase should cost, and allowance_wei to the budget for the whole run or period. Now the decision loop is free to spend however it judges best, and the worst case is bounded: a single bad decision is capped at one transaction, and a string of them stops at the period allowance. The full treatment is in how-to-set-up-agent-spending-limits. This is the step that turns an autonomous spender from a risk into something you can leave running.
Log every spending decision
An autonomous spender makes choices with real money while you are not watching, so log the choices, not just the payments. For each buy, record what the agent considered, what it picked, why, and what it cost. The payment side already gives you a payment.sent event per settlement; pair that with your own decision log and you can answer "why did the agent spend this" after the fact.
function recordDecision(options, choice, result) {
log.info({ goal, considered: options.map(o => o.url), chose: choice?.url, cost: choice?.price, outcome: result.ok ? "paid" : result.status });
}This matters more for a spender than for most agents, because a bad decision costs money rather than just a wrong answer. When you review a run and see the agent paid for something it should not have, the decision log tells you whether the scoring was wrong or the options were bad, which are different fixes. Reconcile the decision log against the payment.sent stream periodically; they should match, and a gap means a payment you cannot explain, which is exactly what you want to catch early.
Test the spending behavior
Test on Base Sepolia, and test the decisions, not just the plumbing. Fund the agent with test USDC and run it against a real goal, watching what it chooses to buy. Three things to confirm: it buys things that move it toward the goal, it stops or adapts when buy fails rather than looping, and it respects the limit, set per_tx_wei below a tempting option and confirm the agent cannot purchase it. The decision quality is the part that needs the most iteration; the payment is reliable, but whether the agent spends wisely is on your scoring logic. When it spends sensibly and stays within bounds on testnet, move to a sk_live_ key.
Common pitfalls
Three traps building an autonomous spender.
No limit, or a limit only in the prompt. A prompt budget is guidance a crafted input can defeat. The wallet limit is the guarantee. Set it before you run the agent unattended.
Retrying refused buys. When a purchase is refused, adapt or stop. Retrying the same buy turns one blocked decision into a loop that burns turns and looks like a runaway.
Optimizing for capability before judgment. A spender that can pay for anything but chooses badly wastes real money. Spend your effort on the decision loop, not on the pay capability, which is a few lines.
What to ship today
Wire buy, write the simplest decision loop that picks and pays toward a goal, and set a dashboard limit. Run it on Base Sepolia against a real goal and watch what it buys and how it handles a refusal. Tune the scoring until it spends sensibly, then go live. For adding autonomy to an existing agent see how-to-let-ai-agent-pay-autonomously. Pricing is on the pricing page.