Cost Controls API
The cost controls surface has two layers:
- community-visible routes for pricing and usage summary
- non-community routes for budget creation, enforcement, alerts, and deeper usage analytics
That split is important for engineers assessing AxonFlow. You can start by observing LLM usage and pricing, then move to a non-community deployment mode when you need budget enforcement, alerting, and operational guardrails around spend.
All Cost Controls endpoints are available through the official SDKs:
Base URL: http://localhost:8080 via Agent proxy, or http://localhost:8081 if you expose the Orchestrator directly.
Authentication
When called through the Agent gateway (port 8080, the recommended path), all endpoints require:
Authorization: Basic base64(clientId:clientSecret)headerContent-Type: application/jsonheader for POST and PUT requests
The gateway validates the credentials and stamps three identity headers before forwarding to the Orchestrator — any client-supplied values are overwritten:
X-Org-ID— the customer organization from the validated license; scopes budgets and usageX-Tenant-ID— the authenticated client identity (deprecated alias ofX-Client-ID); also scopes budgets and usageX-Client-ID— the successor ofX-Tenant-IDunder the v9 identity model (emitted since platform v8.0.0)
X-User-ID (when set) is recorded on budget writes as created_by / updated_by for audit attribution. On read endpoints, org_id and tenant_id can alternatively be passed as query parameters; the header value wins when both are present.
All Cost Controls handlers also answer OPTIONS preflight requests and set Access-Control-Allow-Origin: * with allowed headers Content-Type, X-Org-ID, X-Tenant-ID, X-User-ID, Authorization on every response.
Overview
Verified route families:
| Method | Path | Availability |
|---|---|---|
GET | /api/v1/pricing | Community-visible |
GET | /api/v1/usage | Community-visible |
POST | /api/v1/budgets | Non-community |
GET | /api/v1/budgets | Non-community |
POST | /api/v1/budgets/check | Non-community |
GET | /api/v1/budgets/{id} | Non-community |
PUT | /api/v1/budgets/{id} | Non-community |
DELETE | /api/v1/budgets/{id} | Non-community |
GET | /api/v1/budgets/{id}/status | Non-community |
GET | /api/v1/budgets/{id}/alerts | Non-community |
GET | /api/v1/usage/breakdown | Non-community |
GET | /api/v1/usage/records | Non-community |
The community-visible routes are always registered; the budget and usage-analytics routes are registered only when the deployment is not running in community mode.
Budget limits can be applied at these verified scopes:
| Scope | Description |
|---|---|
organization | Organization-wide budget |
team | Budget for a specific team |
agent | Budget for an individual agent |
workflow | Budget for a workflow |
user | Budget for a specific user |
The pre-flight check (POST /api/v1/budgets/check) evaluates budgets in the order agent → team → organization → user, skipping any scope whose ID is not supplied. workflow-scoped budgets can be created and tracked but are not consulted by the pre-flight check.
Budget Management
Create Budget
Create a new budget with spending limits. The handler applies defaults when omitted:
scopedefaults toorganizationon_exceeddefaults towarnalert_thresholdsdefaults to[50, 80, 100]
POST /api/v1/budgets
Headers:
Content-Type: application/json
Authorization: Basic base64(clientId:clientSecret)
X-User-ID: [email protected]
(X-Org-ID and X-Tenant-ID are stamped by the Agent gateway from your credentials and stored on the budget row.)
Request Body:
{
"id": "monthly-budget",
"name": "Monthly Production Budget",
"scope": "organization",
"limit_usd": 1000.00,
"period": "monthly",
"on_exceed": "warn",
"alert_thresholds": [50, 80, 100]
}
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
id | string | Yes | - | Unique budget identifier |
name | string | Yes | - | Human-readable budget name |
description | string | No | - | Free-text description |
scope | string | No | organization | Budget scope |
scope_id | string | No | null | ID of the scoped entity when needed |
limit_usd | number | Yes | - | Budget limit in USD (must be > 0) |
period | string | Yes | - | daily, weekly, monthly, quarterly, or yearly |
on_exceed | string | No | warn | warn, block, or downgrade |
alert_thresholds | number[] | No | [50,80,100] | Percentage thresholds |
Responses:
| Status | Meaning |
|---|---|
| 201 | Created — body is the full budget object (including enabled: true, org_id, tenant_id, created_by, timestamps) |
| 400 | Invalid body or validation failure (missing id/name, non-positive limit_usd, unknown scope/period/on_exceed) |
| 409 | A budget with this id already exists |
List Budgets
GET /api/v1/budgets
List budgets with filters. Supported query parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
scope | string | (all) | Filter by budget scope |
scope_id | string | (all) | Filter by scoped entity ID |
enabled | boolean | (all) | true or false |
org_id | string | header value | Fallback when X-Org-ID is absent |
tenant_id | string | header value | Fallback when X-Tenant-ID is absent |
limit | integer | 50 | Values ≤ 0 or > 1000 fall back to 50 |
offset | integer | 0 | Pagination offset |
Response (200 OK):
{
"budgets": [ ... ],
"total": 3,
"limit": 50,
"offset": 0
}
Get Budget
GET /api/v1/budgets/{id}
Returns the budget object. 404 if the budget does not exist.
Update Budget
PUT /api/v1/budgets/{id}
Partial update — only non-zero fields in the body are applied. Updatable fields: name, limit_usd (values > 0), on_exceed, alert_thresholds (non-empty array). X-User-ID is recorded as updated_by. Other fields (scope, period, id) cannot be changed in place.
Responses: 200 with the updated budget object; 400 invalid body or validation failure; 404 budget not found.
Delete Budget
DELETE /api/v1/budgets/{id}
Responses: 204 No Content on success; 404 budget not found.
Budget Status
GET /api/v1/budgets/{id}/status
Returns current spend against the budget for the active period:
{
"budget": { ... },
"used_usd": 412.55,
"remaining_usd": 587.45,
"percentage": 41.3,
"period_start": "2026-07-01T00:00:00Z",
"period_end": "2026-08-01T00:00:00Z",
"is_exceeded": false,
"is_blocked": false
}
is_blocked is true only when the budget is exceeded and on_exceed is block. 404 if the budget does not exist.
Budget Alerts
GET /api/v1/budgets/{id}/alerts?limit=20
Returns recent threshold alerts (default limit 20):
{
"alerts": [
{
"budget_id": "monthly-budget",
"threshold": 80,
"percentage_reached": 81.2,
"amount_usd": 812.00,
"alert_type": "threshold",
"acknowledged": false,
"created_at": "2026-07-08T10:00:00Z"
}
],
"count": 1
}
Check Budget (Pre-flight)
Check if a request should be allowed based on budget constraints. Use this before making LLM requests.
POST /api/v1/budgets/check
Request Body:
{
"org_id": "your-org-id",
"team_id": "engineering",
"agent_id": "support-bot",
"user_id": "42",
"tenant_id": "your-client-id"
}
All fields are optional strings; scopes without an ID are skipped. org_id and tenant_id default to the X-Org-ID / X-Tenant-ID header values when omitted.
Response (Allowed, 200):
{
"allowed": true
}
When a matching budget is exceeded but its action is warn or downgrade, the response still has allowed: true and additionally carries action, budget_id, budget_name, used_usd, limit_usd, percentage, and message.
Response (Blocked, 200):
{
"allowed": false,
"action": "block",
"budget_id": "monthly-budget",
"budget_name": "Monthly Production Budget",
"used_usd": 1100,
"limit_usd": 1000,
"percentage": 110,
"message": "Budget 'Monthly Production Budget' exceeded - requests blocked"
}
A blocked decision is returned with HTTP 200 — the block is expressed in the body, not the status code.
Runtime Enforcement
Budgets are enforced on the Agent gateway's /api/request path before the request is forwarded to an LLM provider:
- If the budget decision is blocked (
on_exceed: blockand the limit is exceeded), the gateway rejects the request with HTTP 402 Payment Required. The response body hassuccess: false,blocked: true, the block reason, and abudget_infoobject (budget_id,budget_name,used_usd,limit_usd,percentage,exceeded,action). - If the budget is exceeded with
on_exceed: warn, the request proceeds and the response carries anX-Budget-Warningheader. - If the budget check itself errors (for example, the cost service is unreachable), the request is allowed — budget enforcement fails open.
Usage & Pricing
Usage Summary
GET /api/v1/usage
Community-visible. Query parameters: org_id, tenant_id (default to header values), team_id, agent_id, provider, model, period (daily, weekly, monthly), start_time / end_time (RFC3339). When neither period nor start_time is given, the current month is used.
Response (200 OK):
{
"total_cost_usd": 123.45,
"total_tokens_in": 1200000,
"total_tokens_out": 340000,
"total_requests": 5230,
"period": "monthly",
"period_start": "2026-07-01T00:00:00Z",
"period_end": "2026-07-31T23:59:59Z",
"average_cost_per_request": 0.0236
}
Usage Breakdown
GET /api/v1/usage/breakdown?group_by=provider
Non-community. group_by accepts provider (default), model, agent, team, user, or workflow; any other value returns 400. Time filters match the usage summary.
Response (200 OK):
{
"group_by": "provider",
"total_cost_usd": 123.45,
"items": [
{
"group_by": "provider",
"group_value": "openai",
"cost_usd": 80.10,
"tokens_in": 800000,
"tokens_out": 220000,
"request_count": 3400,
"percentage": 64.9
}
]
}
Usage Records
GET /api/v1/usage/records
Non-community. Raw per-request usage rows. Query parameters: org_id, tenant_id, team_id, agent_id, provider, model, start_time, end_time, limit (default 100; values ≤ 0 or > 1000 fall back to 100), offset.
Response (200 OK): {"records": [...], "total": n, "limit": 100, "offset": 0} where each record carries request_id, timestamp, scope IDs, provider, model, tokens_in, tokens_out, cost_usd, request_type, and cached.
Pricing
GET /api/v1/pricing
GET /api/v1/pricing?provider=openai
GET /api/v1/pricing?provider=openai&model=gpt-4o
Community-visible. With no parameters, returns {"providers": {...}} (the full pricing table). With provider, returns {"provider": "...", "models": {...}}; 404 if the provider is unknown. With provider and model, returns {"provider": "...", "model": "...", "pricing": {...}}; 404 if that model has no pricing entry.
Why Teams Use This API
- Engineering leads use pricing and usage endpoints to understand real provider cost patterns before production rollout.
- Platform teams use budgets to prevent one workflow, one user group, or one agent from exhausting shared spend.
- Procurement and architecture reviewers often care about this surface because cost governance is part of the production-readiness story, not just an ops afterthought.
Related Docs
Operational Readiness Checklist
Before relying on this page in a production rollout, pair it with the core operations docs:
- Deployment Mode Matrix for self-hosted, Evaluation, Enterprise, SaaS, and In-VPC fit
- Failure Modes And Recovery for degraded-provider, connector, approval, and runtime behavior
- Capacity Planning for sizing and growth signals
- Community vs Evaluation vs Enterprise for limits, support surfaces, and upgrade triggers
