Skip to content

Budgets and forecasts

Budgets (Costs → Budgets) puts an amount on AI spend for a calendar month or quarter and checks it every hour. Each budget shows what it has spent so far, where it is forecast to end the period (with a range, not a single confident number), and whether it is on track, forecast to exceed, or over. Budget alerts go to the Alerts inbox and to any channel your alert rules route them to.

The page also shows the whole project’s month: spent so far and the month-end forecast, on both bases below, with the method written out.

  • Spend to measure: the collector reporting usage, and for API-key budgets a connected vendor account (OpenAI or Anthropic) that lists keys. See AI spend.
  • For team budgets, a connected directory (Microsoft Entra ID) that gives people a department.
PermissionAllows
View budgets (budgets.read)Seeing budgets, their progress and forecasts, the project forecast, and the forecast line on the Costs chart, and asking for an override. Every shipped role that can view costs has it.
Manage budgets (budgets.manage)Creating, changing and deleting budgets, Recalculate now, setting the zone budget periods follow, enforcement rules, and approving or revoking overrides (never one’s own). Owners and administrators have it.

The server checks these on every request; hiding a button is only presentation.

ScopeWhat is measured
Whole projectEverything the project spends in this environment.
TeamA department from your directory. Shield360 has no team hierarchy of its own: teams exist only when a connected directory supplies them. Without one, team budgets cannot be created, and an existing one reports that the directory is missing.
PersonOne person, exactly as the Costs report narrowed to them shows it, with their own seats.
Tool or vendorOne tool (Claude Code, Cursor, Copilot…) from the Costs report’s tool breakdown.
ModelOne model from the Costs report’s model breakdown.
API keyWhat the vendor billed against one API key.
AppOne instrumented app, by its service name: what its traces record its model calls cost (tokens × the model’s price), the same figure as Costs → Apps. Always an estimate.

Budgets belong to the project and environment (data source) you are in, like the Costs page. Changing what a budget is on, its period or its basis starts its measurements and alerts afresh.

Each budget counts one of two things. Pick the one your finance team budgets against.

  • Charged usage — what is billed per token on top of the seats: API keys and usage-based plans. Seat subscriptions are left out, so this is the part of the bill that usage can move.
  • Total including seats — the seats on file, prorated to the period, plus charged usage: what the scope costs as things are. Seats are known in advance, so they are counted as committed for the whole period from the first day.

An API key and an app have no seats, so both bases read the same for them. What seat-covered usage would have cost at list prices is never counted: the seat is the payment.

Periods are calendar months or quarters in the organisation’s time zone: September starts at midnight on 1 September in that zone, not in UTC. A manager sets the zone at the top of the Budgets page; it applies to every budget in the organisation. Until it is set, periods follow UTC.

Vendor bills for API keys are dated by the vendor in UTC, so an API-key budget can be up to a day out at a period boundary.

The forecast is meant to be checked with a calculator, and the page shows its working.

History

The last 28 complete days of usage cost (seats excluded), in the organisation’s zone. Days before the first day with any spend are dropped, so a project that started mid-month is not averaged with days it did not exist.

Weekends

When the history holds at least five weekdays and two weekend days, and a weekend day costs at least 20% more or less than a weekday, weekends are weighted by that ratio.

Run-rate

The average weekday over the last 14 days, carried to the end of the period (including the rest of today). One-off spikes — more than three times a typical day — count as three times a typical day, so one bad day does not set the month.

Trend

With ten or more days of history, a straight line is fitted through the daily cost. When it explains most of the variation (R² of 0.6 or more), the forecast follows the line instead: spend that is climbing steadily is forecast to keep climbing.

Seats

On the total basis, the seats still to come in the period are added as committed.

Range

Low and high span the 7-, 14- and 28-day averages, the average with spikes left in, and the trend line, widened by normal day-to-day variation (1.28 standard deviations of the daily error, scaled by the square root of the days left — roughly an 80% band).

Special cases are labelled on the budget:

  • Fewer than three days of spend — a rough rate, with a range from half to twice the forecast usage.
  • API keys — vendors report a total per key rather than each day, so the forecast uses the key’s average day over four weeks, with a range of 70% to 140%.
  • Team, tool and model — the Costs report keeps daily detail for the whole project, so these use the project’s days scaled by the scope’s share of project usage over the four weeks.
  • No usage — the forecast is what is already spent plus committed seats.

On synthetic series the method lands exactly on a flat series, a mid-month start and a weekday-heavy pattern; within 1% on steady growth (following the trend); within 1% on noisy growth; and within about 5% with two one-off spikes, where the range widens to cover the uncertainty. In every case the true month-end falls inside the range.

A budget on a tool, model or team outside the largest 100 rows of the Costs report is flagged, because its spend may read low.

Every run checks each budget against its thresholds (50%, 80% and 100% unless you choose others):

AlertWhenSeverity
budget.thresholdSpend reaches a threshold below 100%Warning
budget.exceededSpend reaches 100% (or a threshold above it)Critical
budget.threshold, marked ForecastThe forecast reaches 100% before the period endsWarning

Each is raised once per budget per period; the next period starts afresh. If spend passes several thresholds between two runs, one alert is raised for the highest. Dedupe keys are stable — budget:<id>:<period>:<threshold>, and budget:<id>:<period>:forecast — so a repeat is counted on the open alert rather than sent again.

Forecast alerts say so in their title (“Forecast: …”) and body, and carry variant=forecast in their attributes, so a rule or webhook can treat them differently from real spend.

Alerts never name a person: a per-person budget is described as “a per-person budget”, and its link opens the budget for those allowed to see it.

To send budget alerts to Slack, Teams, email, PagerDuty or a webhook, add an Event rule on Alerts → Rules that matches the budget.threshold and budget.exceeded kinds.

Alerts tell people; enforcement acts on new AI requests. Each budget can add rules at percentages of its amount:

ActionWhat happens to a new request
WarnIt goes through, and the person sees a notice with the budget, its percentage and who to ask.
ThrottleEach person may make the set number of requests a minute; beyond that the request is refused and they are asked to wait.
BlockIt is refused, with who to ask and a one-click link to request an override.

A budget with no rules is alert-only, as every budget was before. The grace margin (percentage points) makes throttle and block start that much later; in between, people are warned and told when the hard action will start.

Enforcement pointWhat it checksHow a refusal looks
AI proxyEvery request, for the person it is for (X-Shield360-User or the enrolled user), the vendor and the model, with the prompt’s estimated input costBlock: 403 in the provider’s error format (budget_exceeded). Throttle: 429 (budget_throttled). Warn: the response carries X-Shield360-Budget-Warning.
Coding-agent hooksThe start of each turn (prompt submit), for the developer and the agentClaude Code, Codex, Cursor, Gemini CLI, Windsurf and OpenCode stop the turn with the reason; Claude Code, Codex and OpenCode show warnings. Copilot CLI cannot stop a prompt from a hook.
Browser extensionVendor API consoles billed per use (OpenAI Platform, Anthropic Console, Google AI Studio), for the browser’s signed-in personA page saying why, with Request an override; a warning has Continue for the rest of the browser session. Chat sites paid by the seat are not affected.

All three ask the same decision, GET /api/budgets/decision, with their own collector token, so they always agree. A decision is reused for 60 seconds.

An app instrumented with the Shield360 SDK can ask the same decision for itself, with the environment’s API key and its service name. App budgets and whole-project budgets apply; budgets on people, tools and vendor keys do not, since an app cannot vouch for who is using it.

import Shield360 from 'shield360';
Shield360.init({ applicationName: 'checkout-api', shield360Url: 'https://app.shield360.ai', shield360ApiKey: process.env.SHIELD360_API_KEY });
const check = await Shield360.budgets.check({ model: 'gpt-4o', estimatedCostUsd: 0.02 });
if (!check.allowed) throw new Error(check.reason);
import os
import shield360
shield360.init(application_name="checkout-api", shield360_url="https://app.shield360.ai", shield360_api_key=os.environ["SHIELD360_API_KEY"])
check = shield360.budgets.check(model="gpt-4o", estimated_cost_usd=0.02)
if not check.allowed:
raise RuntimeError(check.reason)

check() never throws. It reuses a decision for its 60 seconds while the calls checked with it fit in its headroom, as the proxy does, so it does not reach Shield360 on every call. It fails open when Shield360 cannot be reached, unless you pass failClosed / fail_closed, set SHIELD360_BUDGET_FAIL_CLOSED=1, or the budget asks to fail closed. To check before every provider call without changing your code, pass budgetCheck: true (budget_check=True) to init, or set SHIELD360_BUDGET_CHECK=1: a call a budget blocks then throws BudgetExceededError.

Vendor bills arrive a day or more late, so enforcement cannot wait for them. It uses Shield360’s freshest estimate: the budget’s last measurement (hourly, on the budget’s own basis), plus the forecast’s run rate for the time since, plus what the request itself may cost when the enforcement point can price it. Every decision is labelled an estimate and carries this working; the budget’s own figures on this page are unchanged.

A budget that has never been measured is not enforced.

Enforcement points reuse a decision for up to 60 seconds, but only while the requests made since then fit in its headroom: how much more can be spent before any budget on that person reaches its next stricter action (warn, then throttle, then block, after any grace margin). The AI proxy and the coding-agent hooks add up the estimated cost of each request decided with the cached answer. When the next request would take the total past the headroom, they ask again with that request’s cost. So a large prompt straight after a small one is checked on its own cost, not let through on the earlier answer. When no stricter action is left to reach, the decision is reused for the full 60 seconds.

Enforcement points fail open by default: if Shield360 cannot be reached, requests go through. Turn on Block when the decision cannot be read on a budget with a block rule to fail closed instead, as the AI guard’s fail-closed setting does. A machine can also be forced to fail closed with SHIELD360_BUDGET_FAIL_CLOSED=1, or excluded from budget checks with SHIELD360_BUDGET_ENFORCEMENT=off.

When a budget allows overrides, the link in a refusal opens Request a budget override with the budget and the person already filled in. The person gives a reason and a duration (one hour to seven days) and sends it. Approvers — anyone with Manage budgets, other than the requester — are alerted (budget.override_requested) and decide under Costs → Budgets → Override requests. An approval lifts warn, throttle and block for that person (or, if the approver chooses, everyone the budget covers) until it expires; it can be revoked early. Every request, decision and revocation is recorded in the audit log.

Per-person budgets name the person they are on; enforcement trusts the person an enforcement point reports, as attribution does. A device that lies about who is using it can be seen in the activity it reports.

Many vendors let an administrator cap spend in their own console. For example, a company on Claude Enterprise might give everyone $500 a month, then raise one person to $750. Costs → Budgets → Limits set in vendors reads those caps through the vendor’s connector. You don’t re-enter them in Shield360: when an administrator changes a limit in the vendor, Shield360 sees it on the next sync and records the change.

VendorWhat is readConnector
Claude EnterpriseEach member’s monthly limit, whether it was set for them or comes from their seat tier, group or the organisation default, and their spend this monthClaude Enterprise spend limits and costs
CursorEach member’s effective monthly limit, whether it was set for them or is the team’s, and their spend this cycleCursor
GitHubThe enterprise’s billing budgets (enterprise, organisation, repository, cost centre and per-user), with what each has consumedGitHub Copilot, with an enterprise set
LiteLLMThe max budget on each virtual key, user and team that has one, with LiteLLM’s spend against itLiteLLM budgets
OpenRouterThe credit limit on each API key, with its usage in the reset periodOpenRouter key limits
AzureEach subscription’s cost budgets, with their current spendAzure OpenAI or Azure Cost Management
AWSThe account’s cost budgets from AWS Budgets, with their actual spendAmazon Bedrock or AWS Cost Explorer, with an account id set

OpenAI, ChatGPT Enterprise and Google Cloud aren’t read yet:

  • OpenAI: its spend-limit API is ambiguous about units.
  • ChatGPT Enterprise: OpenAI hasn’t published the endpoint for its spend controls.
  • Google Cloud: budgets don’t report spend. Use Budgets in Shield360 for these.

Each limit shows:

  • The amount and period. No limit means unlimited, and $0 means nothing beyond what the plan includes.
  • Where the limit comes from.
  • What the vendor counts against it this period.
  • The last change seen.

Limits are sorted so the people closest to their limit come first.

How a change is noticed. Vendors don’t keep a history of limits or send a notice when one changes, so each sync compares what the vendor reports with the previous sync. A change is dated between the two syncs that saw it, and the vendor doesn’t say who made it. When Shield360 finds a change, it records it under Changes in the last 90 days, adds an audit log entry (vendor_spend_limit.changed, .added or .removed), and raises one vendor_limit.changed alert for the sync. A connector’s first sync records the limits as they are, without listing them as changes. To notice changes sooner, set the connector’s Sync every to an hour.

Alerts. A limit whose spend reaches 80% raises vendor_limit.threshold (Info), and one that reaches 100% raises vendor_limit.reached (Warning). Each is raised once per limit per period. A new amount starts afresh, because 80% of $750 isn’t 80% of $500. Like budget alerts, these never name the person.

Shield360 never changes a vendor’s limit. To give someone more, change the limit in the vendor’s console, and the next sync picks it up. Vendor limits are separate from the budgets above. A budget measures spend Shield360 sees across every tool, while a vendor limit caps only that vendor’s spend, as that vendor counts it.

  • Home — the Cost & coverage card says how many budgets are over or forecast to exceed, and lists them under needs attention.
  • Costs → Analytics — the cost-over-time chart continues charged usage as a dashed forecast line to the end of the month, when the window ends today and the report is not narrowed to a person or machine.

Budgets are measured hourly by a scheduled job (BUDGETS_EVALUATE_SCHEDULE, default 17 * * * *). A new or changed budget is measured straight away, and Recalculate now measures every budget in the project on demand. The job reads the same figures as the Costs report, so a budget never disagrees with the page it links to.

Budgets are included in an organisation export (postgres/budgets.ndjson) with their last measurement. They are deleted with their project or organisation. Erasing a person deletes any per-person budget on them.

Vendor limits and their changes are exported as postgres/vendor_spend_limits.ndjson and postgres/vendor_spend_limit_changes.ndjson. They are deleted with their project, organisation or connector. Erasing a person deletes their mirrored limits and the change history for those limits. The vendor still holds the limit, so remove the person in the vendor too, or the next sync reads their limit again.