Skip to main content
A spend budget caps what an agent can spend, so a runaway loop can’t drain the wallet overnight. The API enforces it server-side against the receipt ledger, so the cap is a per-agent total across every process, restart, and serverless invocation that uses the wallet, not a per-instance counter. Nobody budgets for what they can’t see. So nobody budgets.
An agent with no budget configured is unlimited until you set one.

How it differs from maxCost

maxCost caps one call from one process. A budget caps the UTC day and every call, across all processes. Use both: maxCost is a per-call sanity check, and the budget is the overall limit.

Enforcement

Every paid route checks the budget before any payment is signed or settled:
  • Quote-based routes (email, voice, SMS, build, browser, commerce, compute) reject at quote time: you get the 403 instead of the 402 quote, so nothing is signed.
  • Fixed-price routes reject in the payment middleware, before settlement.
  • Concurrent calls are safe. On the paid leg, the check and the in-flight reservation are one atomic Redis operation, so two parallel calls can’t both fit under the cap. The reservation is released when the receipt is written. If Redis is unavailable, the guard falls back to a best-effort ledger check instead of blocking payments.
  • Calls fully covered by credits don’t use budget. Failed settlements don’t count as spend.
  • A capped agent stays capped during an outage: the call is held with 503 budget_unavailable (see below). An agent with no cap is not affected, and neither is a capped agent when the cache that records the cap is itself unreachable.
The rejection is a 403, not a 402, because a retry can’t succeed until the window resets or the budget is raised:
reason is daily or per_transaction. The SDKs raise a typed BudgetExceededError with these fields.

When the budget can’t be read

If an agent has a cap and the budget, today’s spend or the in-flight total can’t be read, the call is held instead of running uncapped:
This is a 503: nothing was signed or charged, and the same call can be retried as is. It is not a BudgetExceededError; the SDKs surface it as an ordinary request failure. Agents with no cap never get it.

Alerts

At alert_at the agent gets a budget_warning notification. When a call is blocked, it gets budget_exceeded. Both appear in notifications, which the agent can read, and are emailed if alert_email is set. Each fires at most once per UTC day.

Authentication

Both budget endpoints identify the agent by X-Agent-ID and require a signed x-agent-proof (read proof): scope: "read" for GET, scope: "write" for PUT. Unlike inbox and notification reads, this is not log-only. A budget write under a spoofed public wallet address could halt another agent’s paid calls, so unsigned requests are rejected. The SDKs sign automatically (TypeScript ≥ 0.27.0, Python ≥ 0.20.0) and sync a budgets config once, before the first paid call.

Endpoints