What securing payments means
Securing an AI agent's payments is not one setting; it is a few layers, each closing a different gap. The agent is a program driven by a model that can be manipulated, holding the ability to move money, so the goal is to make sure that even a fully compromised agent can only do bounded, auditable damage. You get there by putting the controls that matter where the agent cannot reach them.
Four layers do the work: wallet-enforced spend limits, key hygiene, verified webhooks, and keeping budgets out of the prompt. None is exotic, and together they turn agent payments from a scary idea into something you can run unattended. This page walks each layer; the secure-your-agent-wallet guide goes deeper on wallet hardening, and the spending controls product page is the broader reference.
What can go wrong
It helps to name the threats before the controls, because each layer answers a specific one. Four things can go wrong with an agent that pays.
The model gets manipulated. A prompt injection or a poisoned input convinces the agent to pay something it should not. The wallet limit bounds how much that can cost. The key leaks. A key in a log, a prompt, or source lets someone transact as the agent. Key hygiene and a least-powerful runtime key shrink what a leaked key can do, and the emergency revoke cuts it off. An event gets forged. A fake webhook tries to trigger your payment logic. Verification rejects it. And the agent runs away. A loop pays repeatedly without anyone intending it. The per-transaction cap and period allowance stop the bleed.
Notice the pattern: you cannot prevent the model from being manipulated or guarantee a key never leaks, so the design does not try to. It assumes those will happen and makes their blast radius small and visible. That is why the controls live outside the agent, and why "bounded and auditable" is the realistic goal rather than "unbreakable".
Layer 1: wallet-enforced spend limits
The foundation. Set a per-transaction cap and a per-period allowance in the dashboard, and the wallet refuses anything over them before settlement. This is the only control that bounds the cost of an incident rather than just its likelihood, because the agent cannot change it. Read it back to confirm what is enforced:
import { createClient } from "@blockchain0x/node";
const client = createClient({ apiKey: process.env.B0X_API_KEY! });
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 close to the largest legitimate single payment, so a redirected or runaway payment is capped small, and allowance_wei to a sane period budget so a stream of small bad payments still stops. The full treatment is in how-to-set-up-agent-spending-limits. If you do one thing on this page, do this.
Layer 2: key hygiene
The agent runtime should hold the least-powerful key that does its job: a sk_test_ or sk_live_ key that can transact and read, never a surface that can change the spend permission. That separation is what makes layer 1 hold; if the agent could edit its own limit, the limit would be theatre. Spend permissions are created and changed in the dashboard, by a human, on purpose.
Two habits enforce this. Keep the key in a secret manager and out of prompts, logs, and source. And check the prefix at boot so a sk_live_ key never runs in a test context or vice versa; the server rejects a mismatched caller with apikey.network_mismatch, but catching it yourself is cleaner and earlier.
const key = process.env.B0X_API_KEY!;
if (process.env.NODE_ENV === "production" && key.startsWith("sk_test_")) {
throw new Error("Test key in production. Aborting boot.");
}Layer 3: verified webhooks
Events drive your payment logic, so a forged event is an attack. Verify every delivery with webhooks.verify against the raw body and your signing secret before acting on it.
import { webhooks } from "@blockchain0x/node";
// in a raw-body webhook route:
const result = webhooks.verify({ headers: req.headers, rawBody: req.body, secret: process.env.B0X_WEBHOOK_SECRET! });
if (!result.ok) return res.status(400).json({ code: result.code });The verifier compares the HMAC in constant time and rejects deliveries more than five minutes old, which kills replays. Verify against the raw bytes, never the parsed JSON, and never act on a delivery that fails verification. An unverified handler is a door you left open.
Layer 4: prompt-injection defense
The model can be manipulated, so assume it will be, and make sure manipulation cannot move money beyond the bounds. The defense is the inversion of a common instinct: do not put the budget in the prompt as the control. A prompt budget is a suggestion a crafted input defeats. The wallet limit from layer 1 is the real boundary, and it holds precisely because it lives outside the agent.
Use the prompt for good behavior (tell the planner not to retry refused payments, to prefer cheaper options), but never as the line that stops overspend. The right mental model: a prompt injection that fully hijacks the agent can still only spend up to the per-transaction cap, and only up to the period allowance in total, and your verified webhooks will show you it happened. The injection succeeds at the model layer and fails at the money layer, which is exactly the split you want.
Audit the trail
Security is not only prevention; it is also being able to answer "what happened" afterwards. Agent payments are auditable by design, and you should use that. Your verified payment.sent and payment.received webhook events give you a real-time record of every transaction the agent made, and client.transactions.get(id) returns the canonical record for any one of them when you need to reconcile or investigate.
Keep that trail. Log every verified event keyed by its id, and reconcile it periodically against the transaction records so a payment you cannot explain surfaces quickly rather than months later. Because settlement is on chain, each payment also has a transaction hash you can confirm on a block explorer independently of your own logs, which is a stronger audit position than a closed system gives you. When something does go wrong, this trail is how you scope it: exactly which payments, for how much, in what window, bounded by the limit that was in force.
An emergency stop
Even with the layers, you want a kill switch. Two levers. Revoke the agent's API key to cut off everything immediately:
await client.apiKeys.revoke("ak_..."); // the agent's key stops working at onceOr revoke the spend permission in the dashboard to stop spending while the agent keeps reading. Use the key revoke for "stop everything now" and the permission revoke for "stop spending, keep running". Decide which one your on-call uses before you need it, and make sure they have access to do it.
Common pitfalls
Three traps that undo the layers.
Budget only in the prompt. It feels like a control and is not. The wallet limit is the guarantee; the prompt is guidance.
Admin key in the runtime. If the agent can change its own limit, the limit is meaningless. Runtime gets transact-and-read; admin stays human.
Acting on unverified events. A forged webhook can trigger real logic. Verify every delivery against the raw body before you act on it.
What to ship today
Set a wallet spend limit, move the agent's key to a secret manager with a boot-time prefix check, verify every webhook, and keep the budget out of the prompt. Confirm your on-call knows the two emergency levers. That is agent payments you can run unattended. For deeper wallet hardening see the secure-your-agent-wallet guide. Pricing is on the pricing page.