Skip to main content
LearnGuidesAgent spend controls
GUIDE

Set up agent spend controls that survive prompt injection.

8 minutes
SHORT ANSWER

Configure caps and allowlists at the wallet API layer - not in the agent's code or prompt. The API checks every payment intent against the policy before settlement, so prompt injection that bypasses the agent's planner still cannot bypass the cap. The baseline policy is a daily cap plus a per-payment ceiling; allowlists and time windows tighten it further.

PREREQUISITES

Before you start.

  • An existing agent profile (see the add-payments-to-agent guide).
  • An API key with policy-write scope.
  • A rough idea of expected daily spend - the cap should be 2-3x normal usage, not 100x, so it is actually load-bearing.
  • Optional: a list of known-good counterparty wallet addresses if your agent's payee set is finite.
  • Familiarity with the agent spend policy concept - this guide is its operational counterpart.
STEP 1 OF 4

Apply a baseline cap.

Start with the two most useful rules: a daily total cap and a per-payment ceiling. The daily cap bounds the worst-case 24-hour exposure. The per-payment ceiling stops a single anomalous request (a buggy retry, a maliciously-quoted price) from emptying the day's budget in one shot.

TypeScript
import { Blockchain0x } from "@blockchain0x/sdk";

const client = new Blockchain0x({ apiKey: process.env.BLOCKCHAIN0X_API_KEY! });

await client.agents.updateSpendPolicy("agt_01J9QKE...", {
  daily_cap_usdc: "20.00",
  per_payment_cap_usdc: "2.00",
});
Python
from blockchain0x import Client
import os

client = Client(api_key=os.environ["BLOCKCHAIN0X_API_KEY"])

client.agents.update_spend_policy(
    "agt_01J9QKE...",
    daily_cap_usdc="20.00",
    per_payment_cap_usdc="2.00",
)
STEP 2 OF 4

Restrict to known counterparties.

If the agent's legitimate payees are a small known set, allow only those wallet addresses. This is the strongest single defense against prompt-injection-driven payment redirection: even if the agent is convinced to pay an attacker, the wallet refuses because the attacker's address is not on the list. Combine with default_action: "deny" so anything not explicitly allowed is rejected.

TypeScript
await client.agents.updateSpendPolicy("agt_01J9QKE...", {
  daily_cap_usdc: "20.00",
  per_payment_cap_usdc: "2.00",
  allowed_counterparties: [
    "0xAccountingSaaS...",
    "0xTaxFilingTool...",
    "0xPayrollProvider...",
  ],
  // Optional: deny everything else explicitly.
  default_action: "deny",
});
Python
client.agents.update_spend_policy(
    "agt_01J9QKE...",
    daily_cap_usdc="20.00",
    per_payment_cap_usdc="2.00",
    allowed_counterparties=[
        "0xAccountingSaaS...",
        "0xTaxFilingTool...",
        "0xPayrollProvider...",
    ],
    default_action="deny",
)
STEP 3 OF 4

Add time-window restrictions.

For predictable workloads (a nightly pipeline, a scheduled batch job), restrict spending to the hours the agent is expected to be active. A payment intent outside the window fails at the policy layer, regardless of amount or counterparty. This catches both runaway loops at the wrong time and out-of-schedule exploit attempts.

TypeScript
await client.agents.updateSpendPolicy("agt_01J9QKE...", {
  daily_cap_usdc: "50.00",
  per_payment_cap_usdc: "2.00",
  // Local time, agent-workspace timezone.
  time_windows: [{ start: "01:00", end: "05:00" }],
});
Python
client.agents.update_spend_policy(
    "agt_01J9QKE...",
    daily_cap_usdc="50.00",
    per_payment_cap_usdc="2.00",
    time_windows=[{"start": "01:00", "end": "05:00"}],
)
STEP 4 OF 4

Subscribe to policy events.

Every rejected payment intent generates a policy_violation event with the rule that failed and the requested amount. Subscribe to it in your webhook handler so you have visibility when the policy actually kicks in. A reasonable alert threshold: more than 5 rejections per hour from a single agent, or any rejection on a fresh agent (which usually means a misconfiguration, not an attack).

A typical rejection payload looks like this:

// What you get back when the policy refuses a payment.
{
  "error": {
    "code": "policy_violation",
    "message": "Payment would exceed daily cap",
    "rule": "daily_cap_usdc",
    "limit_usdc": "20.00",
    "spent_today_usdc": "19.50",
    "requested_usdc": "2.00"
  }
}

The rule, the limit, and the spent-today number make it straightforward to triage: if spent_today_usdc is near the limit, the policy is doing its job; if it is far below the limit but another rule fired, that is the more interesting signal.

COMMON PITFALLS

Five mistakes that defeat the policy.

Putting the spend cap in agent code instead of the wallet API

Spend rules in the agent's planner or system prompt are not security boundaries. A prompt-injection attack that overrides the planner also overrides the cap. The whole point of an agent spend policy is that it lives outside the agent's manipulable scope - in the wallet API layer. Configure it through the SDK call, not through 'tell the agent not to spend more than $X'.

Forgetting that policies stack

If your workspace has a daily cap of $100 and the agent inside it has a daily cap of $20, the effective cap is $20 (the tighter one wins per call). What gets evaluated is every applicable rule simultaneously. A common confusion is to set a generous per-agent cap and a tight workspace cap, expect the per-agent cap to apply, and then discover that the workspace cap is the actual ceiling.

Allowlists that are too small to work

Counterparty allowlists are the strongest single control, but they only work when the agent has a knowable set of legitimate payees. For an agent that pays arbitrary paid MCP tools across the web, the allowlist is infeasible. For an agent that integrates with three specific vendors, the allowlist eliminates injection risk entirely. Use them where they fit; do not force them where they do not.

No alert on policy_violation events

Every rejected payment is logged with the rule that failed. If you do not subscribe to policy_violation events or surface them in your dashboard, an under-the-radar attack (or a legitimate runaway loop) just looks like the agent silently producing no work. Wire up an alert on more-than-N rejections-per-hour - it is the cheapest early warning system you can build.

Letting the agent ask the user to raise its own limit

If your agent's UX includes 'tell me if you need more budget' and you have a flow that the user routinely approves, you have effectively given the agent unbounded budget through social engineering. Limits are set by humans through admin UI, with audit logging. The agent should never have a path to its own limit adjustment, even with a human nominally in the loop.

NEXT STEPS

After the policy is in place.

With the policy enforced, the highest-leverage follow-ups are webhook robustness (so policy events actually reach your handler), identity verification (so counterparties trust the agent's profile), and a pre-launch security review (so you have not left another door open).

Full reference at docs.blockchain0x.com. Product surface: Spending controls.

Last reviewed: 2026-05-15. Published under CC BY 4.0.

Give your agent guardrails the prompt cannot lift.

Daily caps, per-call ceilings, counterparty allowlists, time windows. Free to start.