Budget Controls
AI agents consume LLM tokens on every request, and token consumption translates directly to cost. Without budget controls, a single agent caught in a reasoning loop or processing unexpectedly large inputs can exhaust thousands of dollars in minutes. VeraID provides multi-tier budget enforcement that checks spend limits before tokens are consumed, preventing runaway costs at the source.
Budget Types
Every AI agent identity supports four budget controls, each independently configurable.
| Budget Type | Description | Example |
|---|---|---|
| Daily limit | Maximum spend per UTC day. Resets at midnight UTC. | $50.00/day |
| Monthly limit | Maximum spend per calendar month. Resets on the 1st of each month at midnight UTC. | $1,000.00/month |
| Per-request limit | Maximum spend for a single LLM call. Prevents individual requests from consuming disproportionate budget. | $2.00/request |
| Warning threshold | Percentage of any limit at which a warning is raised (an organization setting). Does not block requests. | 80% |
Action on Exceed
What happens when a request would pass a budget is set per organization, in Settings → AI budget (or PUT /api/v1/settings/ai-budget), and can be made stricter for one agent with actionOnExceed in its configuration (an agent’s own action never loosens the organization’s, and past an organization-wide limit the organization’s action always applies). The same settings hold the organization-wide daily and monthly limits (all agents together), the default limits for an agent that has no costBudget of its own, and the warning, critical and block thresholds. A budget counts as exceeded once a request would take spend past the block threshold (100% by default, so the limit itself). In these settings, a limit of 0 means no limit. On an agent, a costBudget limit that is unset (or null) means the organization’s default agent limit; the per-request limit defaults to $5 when the agent sets none.
The check runs before the model is called, when the agent asks POST /api/v1/ai-monitoring/validate.
Block
The request is refused: the validate call answers 403 with errorCode: "BUDGET_EXCEEDED" and a reason naming the budget that was exceeded (Daily budget exceeded, Monthly budget exceeded, Organization daily budget exceeded, Organization monthly budget exceeded, or the per-request limit).
Throttle
The first request past the budget is allowed, and the agent is then refused for one minute (throttleDelay: 60000). A request over the per-request limit is refused.
Alert
The request is allowed and flagged in the answer’s warnings (Daily budget exceeded - flagged, not refused). This is the default for an organization that has not changed its AI budget settings, so enforcement does not start refusing requests until an administrator chooses Block or Throttle: whether to refuse is the organization’s decision.
Budget alerts
Whatever the action, the organization is told. When usage is recorded (POST /api/v1/ai-monitoring/log), VeraID raises an AI_AGENT_QUOTA_EXCEEDED alert against the same limits enforcement uses:
- Per agent: its own
costBudget, or the organization’s default agent limits when it has none. - Organization-wide: the organization’s daily and monthly limits, all agents together. These alerts name no agent.
The alert is Medium at the warning threshold, High at the critical threshold, and Critical once spend passes the block threshold (the budget is exceeded). A warning or critical threshold of 0 is ignored for alerts (80% and 90% apply instead), since it would alert on every request. There is one alert per agent (or for the organization) and period: a daily and a monthly crossing are separate alerts, and a repeat updates or escalates the open alert rather than opening another, and is checked at most once a minute per severity. Alerts appear in the alerts list (live, on an open alerts page) and are forwarded to the incident integrations (PagerDuty, Jira Service Management) whose minimum severity and alert types they match. They are not posted to Slack or Teams, and do not fire the alert.triggered webhook event.
Rate Limiting
In addition to cost-based budgets, VeraID enforces request rate limits to prevent agents from overwhelming LLM providers or consuming budget through high-frequency low-cost calls.
| Rate Limit | Description | Default |
|---|---|---|
| Requests per minute | Maximum LLM API calls per 60-second sliding window | 60 |
| Requests per hour | Maximum LLM API calls per 60-minute sliding window | 1,000 |
Rate limits are enforced independently of budget limits. A request can be within budget but still rate-limited, or within rate limits but over budget.
{ "agentConfig": { "costBudget": { "daily": 50.00, "monthly": 1000.00, "perRequest": 2.00 }, "rateLimits": { "requestsPerMinute": 30, "requestsPerHour": 500 } }}Creating an Agent with Budget Controls
API
curl -X POST https://app.veraid.io/api/v1/identities \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "research-assistant", "type": "AI_AGENT", "description": "Summarizes research papers and answers questions from the knowledge base", "tags": ["research", "production"], "agentConfig": { "provider": "anthropic", "modelIdentifier": "claude-sonnet-4", "capabilities": ["chat-completion", "retrieval"], "dataAccess": { "allowed": ["knowledge-base", "public-papers"], "denied": ["financial-records", "hr-data"] }, "monitoring": { "logAllRequests": true, "logResponses": false, "requireApproval": false }, "costBudget": { "daily": 25.00, "monthly": 500.00, "perRequest": 1.50 }, "actionOnExceed": "block", "rateLimits": { "requestsPerMinute": 20, "requestsPerHour": 300 } } }'SDK
import { VeraIDClient } from '@veraid/sdk';
const client = new VeraIDClient({ apiKey: process.env.VERAID_API_KEY, agentId: process.env.VERAID_AGENT_ID,});
const agent = await client.identities.create({ name: 'research-assistant', type: 'AI_AGENT', description: 'Summarizes research papers and answers questions from the knowledge base', tags: ['research', 'production'], agentConfig: { provider: 'anthropic', modelIdentifier: 'claude-sonnet-4', capabilities: ['chat-completion', 'retrieval'], dataAccess: { allowed: ['knowledge-base', 'public-papers'], denied: ['financial-records', 'hr-data'], }, monitoring: { logAllRequests: true, logResponses: false, requireApproval: false, }, costBudget: { daily: 25.0, monthly: 500.0, perRequest: 1.5, }, actionOnExceed: 'block', rateLimits: { requestsPerMinute: 20, requestsPerHour: 300, }, },});Checking Budget at Runtime
Check Before Spending
Ask the server whether a request of a given estimated cost may go ahead. This is the same check enforcement makes before a model call (POST /api/v1/ai-monitoring/validate): the agent’s and the organization’s limits, the block threshold and the action on exceed.
curl -X POST https://app.veraid.io/api/v1/agents/$AGENT_ID/budget \ -H "Authorization: Bearer $VERAID_API_KEY" -H "Content-Type: application/json" \ -d '{"estimatedCost": 0.03}'{ "allowed": false, "reason": "DAILY_LIMIT_EXCEEDED", "message": "Daily budget exceeded", "warnings": [], "dailyRemaining": 0.02, "monthlyRemaining": 312.77}dailyRemaining and monthlyRemaining mean what they mean in the budget status below (what may still be spent before a refusal): less this request’s cost when it is allowed, still 9007199254740991 where nothing would be refused, and on a refusal the headroom before this request.
reason is one of PER_REQUEST_LIMIT_EXCEEDED, DAILY_LIMIT_EXCEEDED, MONTHLY_LIMIT_EXCEEDED, ORG_DAILY_LIMIT_EXCEEDED, ORG_MONTHLY_LIMIT_EXCEEDED or THROTTLED. Under the Alert action an over-budget request answers allowed: true with the breach in warnings.
The SDKs’ checkBudget(estimatedCost) (TypeScript, Python, Go) decides from GET /api/v1/agents/:id/budget: it returns true when dailyRemaining and monthlyRemaining both cover the cost. Those figures are what enforcement would still allow before refusing, the organization-wide limits included: unlimited under Alert, and up to the block threshold under Block, where the SDK agrees with the server exactly. Under Throttle the SDK refuses at the threshold, where the server would let one more request through before throttling. It does not check the per-request limit; the server’s own check does.
Get Budget Status
const status = await client.getBudgetStatus(); // GET /api/v1/agents/:id/budget
console.log(`Daily: $${status.dailySpend} / $${status.dailyLimit}`);console.log(`Monthly: $${status.monthlySpend} / $${status.monthlyLimit}`);{ "dailyLimit": 25.0, "monthlyLimit": 500.0, "dailySpend": 12.47, "monthlySpend": 187.23, "dailyUsagePercent": 49.88, "monthlyUsagePercent": 37.45, "isExceeded": false, "dailyRemaining": 12.53, "monthlyRemaining": 312.77, "perRequestLimit": 1.5, "warningThreshold": 0.8, "isWarning": false, "actionOnExceed": "block"}warningThreshold is a fraction of the limit. dailyRemaining and monthlyRemaining are how much may still be spent before a request is refused: what is left up to the block threshold of the agent’s limit and of the organization-wide limit, under an action that refuses (Block or Throttle). When nothing would be refused (the Alert action, or a limit of 0) they read 9007199254740991, so a comparison with a request’s cost never reads them as spent. The spend and limit fields show the plain figures.
Cost Tracking
The gateway automatically tracks cost data for every LLM call made through a governed agent identity.
Per-Request Tracking
Each request records:
| Field | Description |
|---|---|
inputTokens | Number of tokens in the prompt |
outputTokens | Number of tokens in the response |
totalTokens | Sum of input and output tokens |
model | Model identifier used for the request |
costUsd | Calculated cost in USD based on model pricing |
timestamp | ISO 8601 timestamp of the request |
operation | Operation type (e.g., chat-completion, embedding, image-generation) |
Cumulative Tracking
VeraID maintains running totals at multiple granularities:
- Per-request — Individual cost for each LLM call
- Hourly — Aggregated spend per clock hour (for trend analysis)
- Daily — Aggregated spend per UTC day (for daily budget enforcement)
- Monthly — Aggregated spend per calendar month (for monthly budget enforcement)
- Lifetime — Total spend since the agent identity was created
Who may record spend
The spend counters decide whether requests are refused under Block and Throttle, so recording spend for an agent (POST /api/v1/ai-monitoring/log, or POST /api/v1/budget with recordUsage) is limited:
- Only the agent itself, with its own key issued by an organization admin (
Authorization: Bearer kd_live_..., a credential of the agent identity: what the SDKs send), or an organization admin from a signed-in session may record spend for an agent. Anyone else gets403, an agent’s owner included: any member can create an agent, and what it records also counts toward the organization-wide limits. A key never records for another agent. - One call may record at most $500, far above any real model call; a larger one is refused with
400. Log calls one at a time. - An organization may record up to 1,000 calls per clock minute, and each agent up to 500. Past that, the answer is
429withRetry-After(the seconds left in the minute).
A budget check (POST /api/v1/ai-monitoring/validate, POST /api/v1/budget) by the agent’s own key, its owner or an admin acts for the agent: under Throttle it can start the throttle. A check by anyone else gives the same answer as a dry run and never starts one.
Where the budget is reported
Everything that shows or judges an agent’s budget reads the same limits and counters as enforcement: the agent’s costBudget, else the organization’s default agent limits, plus the organization-wide limits. A budget counts as exceeded past the organization’s block threshold, and as a warning from its warning threshold.
- The agent’s analytics (
GET /api/v1/ai-monitoring/{agentId}) show the Budget Exceeded or Budget Warning banner from itsbudgetfield.- That field holds
exceeded, plusdailyandmonthlyfor the agent and for theorganization. Each period haslimit,used,percentUsed,exceededandwarning. - It also holds
perRequestLimit,actionOnExceedand thethresholds. - The same response’s
tokenUsageholds the agent’s tokens againsttokenBudget.
- That field holds
- A logged call (
POST /api/v1/ai-monitoring/log) answers withbudget:exceeded,daily,monthlyandorganization, judged the same way and including the call just logged. - The AI Spend Budget policy condition (
token_budget) is judged by the limits it sets, in USD: daily, weekly, monthly or per request. A per-request limit alone still counts as one of its own. A condition that sets none holds the agent to its budget as above, including the agent’s own per-request limit. A per-request limit applies only when the request’s model and tokens are known. The condition’sonExceeded(denyorwarn) decides what going over does, whatever the budget’s action on exceed. A weekly limit exists only on this condition, not in an agent’s budget. Weekly spend counts UTC weeks from Monday, and only from this release onwards: the first week undercounts.
Resetting the counters
If the counters were pushed up by mistake, an organization admin can reset them for the current UTC day, month, or both, from a signed-in session (API keys cannot). Each reset is audited (ai.budget_reset).
# One agent's own counters (and any throttle on it)curl -X POST https://app.veraid.io/api/v1/budget/reset \ -H "Content-Type: application/json" \ -d '{"scope": "agent", "agentId": "'$AGENT_ID'", "period": "daily"}'
# The organization-wide counters (all agents together)curl -X POST https://app.veraid.io/api/v1/budget/reset \ -H "Content-Type: application/json" \ -d '{"scope": "organization", "period": "both"}'Resetting an agent’s counters for both periods also clears its weekly spend, which only a policy’s weekly limit reads. Resetting an agent’s counters does not lower the organization-wide ones, which still include what it spent. Resetting the organization-wide counters clears all of the period’s spend, every agent’s real spend included, so the organization-wide limits start again from zero.
Cost Analytics API
# Get cost breakdown for an agent over a time rangecurl -G https://app.veraid.io/api/v1/identities/{agentId}/costs \ -H "Authorization: Bearer $API_KEY" \ -d "startDate=2026-03-01" \ -d "endDate=2026-03-19" \ -d "granularity=daily"{ "agentId": "idt_9c3a8b7e-4f21-4d6a-b8e1-a2c5d9f07e3b", "period": { "start": "2026-03-01T00:00:00Z", "end": "2026-03-19T23:59:59Z" }, "totalCost": 187.23, "totalRequests": 4218, "totalTokens": 2847392, "byDay": [ { "date": "2026-03-01", "cost": 8.42, "requests": 198, "inputTokens": 52340, "outputTokens": 31204 } ], "byModel": [ { "model": "claude-sonnet-4", "cost": 142.18, "requests": 3102, "percentage": 75.9 }, { "model": "gpt-4o", "cost": 45.05, "requests": 1116, "percentage": 24.1 } ]}What’s Next
- Prompt Injection Detection — Protect agents from adversarial inputs
- Approval Workflows — Require human review for high-cost operations
- Agent Gateway — Understand the full request pipeline