Skip to main content

Set a Budget

Tasks

Create a rule to cap spend for an organization, team, or user, set a hard session-level cap, and configure the alerts that notify you as spend approaches those limits.

Create a budget rule​

  1. Navigate to Cost → Budgets → Rules.

  2. Click + New rule in the top-right corner and choose a starting template:

    TemplateUse for
    Org spend ceilingGlobal limit across all models for the whole organization
    Team budgetTotal cap for one team across all models
    Model capLimit one model family (e.g. claude-opus*) at any scope
    Blank ruleStart from scratch
  3. Set the rule's Scope — Global (all identities), Team (a named group), or User (a specific identity) — and its Target (the team name or user email, or * for Global). Team and group are the same thing in AIControls: a builder's team membership comes from their SSO identity provider's group mappings when SSO is configured, otherwise from the team assigned to their account in Identities.

  4. If Scope is User, you can also check Override team and global limits — see Override a shared limit below.

  5. Set the Model match — a glob pattern such as claude-opus*, gpt-4o, or * for all models. The first matching rule within each scope applies.

  6. Set the Limit (maximum spend in USD) and the Window — Hourly (1h), Daily (24h), Weekly (7d), or Monthly (30d). Each resets on a fixed calendar boundary, not a trailing period counting back from now: Hourly resets at the top of each hour, Daily at midnight UTC, Weekly every Monday, and Monthly on the 1st. A rule created partway through a period still counts the spend already recorded in that period: its usage starts at the matching spend from the start of the current window to now, so a $50 monthly cap created after $58 has been spent this month already reads as exceeded and, in Deny mode, blocks the next request. See How a new or edited rule counts spend.

  7. Choose what happens when the limit is reached:

    ModeWhat happens
    DenyRequests are blocked until the window resets.
    AuditRequests are allowed; overage is tracked and shown in the Overview "Audit-mode Overage" tile.
    WarnRequests are allowed and recorded as a warning.
    Require approvalRequests are held for a manager to approve or deny in the Approvals queue.
  8. Set Warn at — the percentage of the limit at which a budget_warning notification fires, once per rule per window, for as long as spend stays under the hard limit. Default: 80%. Requests above the threshold are still allowed and are recorded as warnings.

  9. Save the rule. Use the Effective budget inspector at the top of the Rules tab to type any identity and model and confirm which rules bind, the remaining headroom, and the decision for the next request.

note

Model match must be unique within a scope — you can't have two rules in the same scope (e.g. two Global rules) that both match claude-opus*, even with different windows. Only the first matching rule in a scope is ever enforced, so a second rule for the same match would silently do nothing, and anything that targets a rule by its match alone — raising its limit via an approval — could no longer tell the two apart. Saving a second rule for a match already present in the scope is rejected; edit the existing rule's limit or window instead. Two rules with different model-match patterns, even overlapping ones like claude-opus* and *, are both allowed and both enforce independently.

note

Rules are AND-enforced: a request must pass the first matching rule in every applicable scope (global, team, user — plus agent and token, configurable via the API; see the warning below). If multiple rules match a request, all of them apply — unless a user-scope rule has Override turned on; see Override a shared limit below.

warning

If you (or an integration) configured an agent- or token-scoped budget rule directly via the API — the Rules UI currently only exposes Global/Team/User — that rule now enforces for the first time. Previously, agent- and token-scoped rules were saved successfully but silently never matched any request; if you have one, verify its limit is still what you intend before your next request hits it. The Effective budget inspector will now show these rules binding where it previously showed nothing.

tip

To promote an audit-mode rule to Deny once you've reviewed the overage it would have caught, use the one-click promote action from the Overview tab.

How a new or edited rule counts spend​

A rule's usage in its current window is the spend recorded for the rule's scope and model match since the window opened, whether it was incurred before or after the rule existed. The Rules tab and request-time enforcement read that same figure, and the Overview totals the same recorded spend.

ChangeEffect on the current window's usage
New ruleStarts at the matching spend already recorded in the window.
Change the limit, mode, or Warn atUsage is kept.
Change the scope, target, Model match, or windowThe rule is treated as new: it starts at the matching spend already recorded for the new combination. That can be lower than the usage the old rule had counted, because the old rule's usage stays with the old combination.
Delete a rule and create it againIf the scope, target, Model match, and window are unchanged, usage is rebuilt from the recorded spend, which includes spend while no rule existed, and is never lower than what the deleted rule had counted.
Window rolls overUsage starts again at zero for the new window.

Only the first matching rule in a scope counts a model's spend, so a catch-all * rule does not also count spend that a more specific rule above it owns. Recorded spend for Team rules is matched by the team or group names stamped on each request, and spend for Agent rules by the agent ID (agents tracked per Kubernetes cluster are not reconstructed, so their rules count from the moment they are saved). Saving a rule set, including only reordering its rules, recounts every rule in that scope from the recorded spend (counters only go up, so this never lowers usage). A request still in flight when a rule is saved can occasionally be missed until the next save or restart. Spend for token-scoped rules cannot be reconstructed, because requests do not record the token, so those rules count from the moment they are saved.

Every replica applies the same rule change and reaches the same usage, so applying a rule more than once, or restarting, never counts spend twice. Another replica can take up to a minute to reflect a rule that was just created, because it caches usage briefly.

Override a shared limit​

By default, budget rules across scopes combine as described above: a request must pass every matching rule, and the lowest limit wins. Override changes that for a single user-scope rule: check Override team and global limits in the rule drawer (User scope only) and, for the models this rule matches, that user is held to this rule's limit alone — the team and organization rules for the same models no longer apply to them.

Token- and agent-scoped rules are unaffected by an override; they still enforce normally alongside it. Spend is still recorded against the team and organization rules even while they're overridden, so those rules' own dashboards and alerts stay accurate — override only changes which limit is enforced and shown as binding for that user, not what's tracked.

The Rules table marks an overriding rule with a purple override tag. Override is only available on user-scope rules; saving it on a Global or Team rule is rejected.

An approver can also turn a budget-increase request into a personal override instead of raising the shared rule — see Approve or deny a request → Approve a budget request.

Set the default session cap​

  1. Navigate to Cost → Budgets → Limits.

  2. Set Default session cap — the maximum spend (USD) for any single session. When a session hits this cap, further requests are blocked until the session ends or an admin resets it. To extend a single session, select the session on Home → Sessions and select Extend Budget in the session detail panel. Set to 0 for no cap. The page shows the cap that sessions created from now on receive: the saved value, or the configured developerSessions.defaultSessionBudgetUSD when no valid value has been saved. The cap applies to every credential type, including portal aic_ tokens and passthrough with the token in X-AIControls-Token. The default is copied onto each new session, so a change applies to sessions created afterwards and does not alter the cap of existing sessions on its own. To change sessions that already exist, see Apply the default session cap to existing sessions. See Session budgets.

To change the cap of a session that already exists, extend its budget from the session detail panel.

Apply the default session cap to existing sessions​

Sessions keep the cap they were created with. Use this to bring existing sessions to a changed default, for example after setting the default to 0 so sessions created under an old cap stop being blocked. Only admins can do this.

  1. Navigate to Cost → Budgets → Limits, change Default session cap, and select Save limits. A dialog asks Apply to existing sessions?. To apply a default you saved earlier, select Apply to existing sessions… instead and choose the old cap under Sessions currently at; the list shows each cap with its session count, and starts on the most common cap that differs from the default.

  2. Choose which sessions change. Also update active sessions is on and Also update ended sessions (expired or revoked) is off; ended sessions change only their recorded cap and nothing is enforced on them. Turn on Include sessions with a custom or extended cap to change those too; otherwise they are skipped.

  3. Read the Preview. It shows how many sessions would change, how many were skipped, and, when it matters, how many blocked sessions would be unblocked or how many active sessions would be blocked immediately because their spend is already at or above a lower cap. When you lower the cap, type the new value to confirm.

  4. Select Save and apply to N sessions (or Apply to N sessions when re-running). The change is one update: it either applies to every listed session or to none. The result shows how many sessions were updated. If the count differs from the preview because sessions changed in between, the dialog says so. If the update fails, no session changed.

Select Save without updating sessions to keep the new default for sessions created from now on and leave existing sessions as they are. Spend and counters are never changed, applying again changes nothing that already has the new cap, and each apply that changes sessions is recorded as sessions.cap_applied in Activity → Configuration. See Apply a changed default to existing sessions for the rules and the Session cap API.

Configure alerts​

Alerts notify you as spend approaches a limit — they do not, on their own, block requests. Configure them in Cost → Budgets → Alerts.

When a limit is actually reached and a rule in Deny or Require approval mode starts blocking or holding requests, AIControls sends a budget_exceeded notification to administrators without any further configuration: once per rule per window (not once per blocked request, and only once across all replicas), naming the scope (organization, team, user, agent, or token), the model match, the window, the spend against the limit, when the window resets, and which identity tripped it. It is delivered to the in-app inbox and, when configured, email, Slack, Teams, and webhooks — turn channels on or off under Settings → Notifications → Budget Exceeded. The same applies to per-agent token budgets on hourly/daily/weekly/monthly windows — except the 1-minute tokens-per-minute (TPM) rate limit, which fires rate_limit_exceeded instead, not budget_exceeded. Rules in Audit or Warn mode never block, so they never send budget_exceeded; use Warn at for those.

  1. Under Budget warning thresholds, add one or more percentage values (e.g. 75, 90). Each threshold fires a budget_warning notification at most once per window per rule. (Per-model rules use their own Warn at percentage instead, as described in Create a budget rule, with the delivery noted below.) budget_warning is an administrator-facing notification — it names the developer, team, or agent whose spend tripped it, but is delivered to:

    • Email, Slack, or Teams, if configured (to the admin-configured recipients — not the affected developer's own inbox or address)
    • Administrators' in-app inbox — for per-model Warn at only; the thresholds set here do not create an in-app entry, so configure at least one external channel if you rely on them
  2. Under Session spend alert, set a USD threshold at which an in-app notification fires the first time a session's spend crosses that value. Set to 0 to disable. This is independent of the session cap configured in Limits — it only notifies, it does not block requests.

note

For conditional enforcement near a budget threshold — for example, requiring a ticket ID once spend exceeds 80% — use a CEL policy with object.modelBudget.team.pctUsed.

What users see when a budget is reached​

Generation-endpoint budget denials are delivered in the format the developer's tool already decodes, with one readable message that starts Blocked by AIControls: and names the budget, the spend against its limit, and how to get more headroom. The /v1/messages/count_tokens endpoint is an exception: it keeps an Anthropic error envelope because it has no assistant-turn response shape.

ClientWhat the developer sees
Claude Code (Anthropic Messages API)An assistant message in the conversation beginning Blocked by AIControls: — for example Blocked by AIControls: model budget exceeded: spent $52.10 / limit $50.00 for * (user scope "dev@example.com") in window 30d. It is a normal turn, not an API Error banner, and Claude Code does not retry it.
Codex (Responses API)The same message printed as an error line in the transcript (■ Blocked by AIControls: …). Codex treats it as final and does not retry; typing another prompt sends a new request, which is denied the same way until the window resets or more budget is granted.
Cursor and other OpenAI-compatible tools (Chat Completions API)An HTTP error whose message is Blocked by AIControls: …, shown however the tool reports a rejected API call — except a per-model rule with exceptions enabled (see below), which arrives as a normal 200 assistant turn offering the exception, the same as Claude Code.
Claude-on-Bedrock (via InvokeModel) and Vertex passthroughClaude-on-Bedrock's InvokeModel API and Claude-on-Vertex's rawPredict both speak the Anthropic envelope, so a denial matches the request's own shape; native Gemini requests use the Google API envelope. Bedrock's Converse API and non-Claude Bedrock model families (Nova, Titan, Llama, Mistral, Cohere) are not covered by this wire-format guarantee — a denial there is still sent in the Anthropic envelope regardless of the request's actual shape.

Which budget produced the message determines its wording:

BudgetMessage
Per-model rule (Deny mode)model budget exceeded: spent $X / limit $Y for <model pattern> (<scope> scope) in window <window> — and, when exception requests are enabled, the message is an assistant turn that also offers a time-bounded exception (see Exceptions). Reply request exception: <reason> to file it.
Session capSession budget reached ($X of $Y used), with the option to request a session top-up.
Virtual key spend capvirtual key budget exhausted: … naming the key's cap.
Token budget (hourly or longer window)token budget exceeded: used N / limit M in window <window>.

Per-minute rate limits (requests or tokens per minute) are the one exception: they are transient, so they are returned as a 429 with a Retry-After header and the developer's tool retries automatically once the minute rolls over.

A rule in Require approval mode does not deny at all — the request is held for a reviewer, and the developer's tool waits; see What the developer sees under approval workflows.

Exceptions​

A developer denied by a model-cap rule can be granted a time-bounded exception: an approved exception targeting controller budget.model-cap, scoped to the same team/user/agent/token tier as the rule, with the model pattern (e.g. claude-opus*) as its subject, adds the exception's USD amount to that rule's limit for the developer's next request. When the exception expires (or is revoked), the added amount is dropped automatically — no cleanup needed.

Policies can read the resolved, exception-adjusted limits as object.defaults.budget.limits. See Request a config-target exception for how a developer requests one and how you approve it.

A developer doesn't have to wait for a denial to ask for more — the Budgets that apply to you list on their Builder Portal Overview shows every rule that applies to them with a Request more button; see Builder Portal → Budgets. Either way, an approver decides whether the result is a personal override, a raise of the shared rule, or a temporary exception — see Approve a budget request.

note

This is a durable, rolling-cap exception — separate from a one-time, single-session top-up. If a developer just needs a bit more headroom to finish their current session, see Grant a session budget top-up instead; approving one never affects the other.

See also​