Configure and read LLM spend caps for an organization. A spend cap is an admin-imposed maximum LLM cost enforced at one of three scopes:
- tenant — the whole org (one cap per platform).
- agent — one agent/mentor (one cap per agent).
- user_agent — one user on one agent (an explicit per-user cap).
Each cap has a rolling interval (day/week/month/year, calendar
aligned), a dollar limit (max_cost_usd), an enforcement mode
(block refuses further chats once exceeded; alert_only only notifies), and
alert thresholds (fractions of the limit that fire a near-limit alert). When
a chat is blocked, the request is refused with HTTP 429 and a body naming the
interval that was hit (dollar amounts are deliberately omitted). Spend counters
are reconciled from ClickHouse on a schedule and surfaced read-only so a UI
can render a progress bar.
Auth & conventions
- Base URL:
https://api.iblai.app/dm— these are Data Manager (DM) endpoints, so the/dmprefix is required; the/api/ai-mentor/...paths below are appended to it (e.g.https://api.iblai.app/dm/api/ai-mentor/orgs/$IBLAI_ORG/spend-caps/tenant/). Omitting/dmwill not resolve. - Header:
Authorization: Api-Token $IBLAI_API_KEYon every request. - Path vars:
{org}=$IBLAI_ORG(the org key).{mentor}= an agent'sunique_id(a UUID).{user_id}/{username}= the target learner. - Endpoint prefix twin: every path under
api/ai-mentor/also resolves underapi/ai-agent/(alias); either works, pick one and be consistent. - Permission tiers: all cap configuration endpoints (tenant / agent /
user-agent read+write) require a platform admin (RBAC action prefix
Ibl.Mentor/SpendCaps/*). The status endpoint is learner-safe: any authenticated member may read their own status, and admins may read any user's — it returns only a coarse zone + percent, never raw dollars. - PUT is upsert: writing a cap that doesn't exist yet returns 201 Created; writing over an existing one returns 200 OK.
- DELETE and PUT (destructive / budget-changing) calls say "Confirm with the user first."
- Not connected yet? Run
/iblai-api-loginfirst to populateIBLAI_ORG,IBLAI_USERNAME, andIBLAI_API_KEY.
Concepts
- scope —
tenant|agent|user_agent. Set by the endpoint you call, never sent in the body. Narrower scopes carrymentor_unique_id(agent, user_agent) andusername(user_agent). - interval_type —
day|week|month|year. The rolling window is calendar aligned (day = midnight, week = Monday, month = 1st, year = Jan 1). - enforcement —
block(default): oncecurrent_spend_usd >= max_cost_usdthe next chat is refused with 429.alert_only: never blocks; only drives near-limit alerts. - alert_thresholds — a list of fractions in
(0, 1](default[0.8, 0.95]= 80% and 95%). Each fires an admin alert once per period. - Read-only counters —
current_spend_usd,remaining_usd,is_exceeded,period_started_at,last_reconciled_atare reconciled from ClickHouse and ignored if sent in a write body. - Applicability — for a given chat, the tenant cap always applies; the agent cap applies when a mentor is in play; the user's own per-agent cap applies when both mentor and user are known. When several hard-block caps are exceeded, the most specific one (user > agent > tenant) is reported.
Reads
Tenant cap
- GET
/api/ai-mentor/orgs/{org}/spend-caps/tenant/— the org-wide cap, or404if none is set. Returns the full cap object (see Schema).
Agent caps
- GET
/api/ai-mentor/orgs/{org}/spend-caps/agents/[?mentor={mentor}]— list agent-scoped caps for the org; passmentorto filter to one agent. Returns a list of cap objects. - GET
/api/ai-mentor/orgs/{org}/mentors/{mentor}/spend-cap/— the single cap for one agent, or404if none. The mentor is resolved tenant-scoped, so an agent from another org returns404.
User-per-agent caps
- GET
/api/ai-mentor/orgs/{org}/mentors/{mentor}/spend-caps/users/[?username=&email=]— list explicit per-user caps on one agent; optionalusername/emailfilters. - GET
/api/ai-mentor/orgs/{org}/mentors/{mentor}/spend-caps/users/{username}/— the one cap for a specific user on that agent, or404.
User spend status (learner-safe)
-
GET
/api/ai-mentor/orgs/{org}/spend-caps/status/{user_id}/[?mentor={mentor}]— the coarse status across every cap that applies to{user_id}(tenant cap always; agent + that user's own per-agent cap whenmentoris given). Any member may read their own; admins may read anyone's. Returns only a zone and percentage — never raw spend/limit dollars — so it is safe to surface to learners. Returns400ifmentoris not a valid UUID.{ "status": "warning", "blocked": false, "caps": [ { "scope": "tenant", "interval_type": "month", "enforcement": "block", "status": "warning", "percent_used": 82.5, "warning_threshold": 0.8, "mentor_unique_id": null, "username": null } ] }statusis the worst zone across the applicable caps (ok→warning→exceeded);blockedistruewhen a hard-block cap is already exceeded (the next chat would be refused).
Writes (platform admin — confirm with the user first)
All writes accept the same body fields: max_cost_usd (required on create,
0),
interval_type(required),enforcement(defaultblock),alert_thresholds(default[0.8, 0.95]),enabled(defaulttrue).scope, the mentor, and the user are inferred from the URL — never the body. Counter fields sent in the body are ignored.
Tenant cap
- PUT
/api/ai-mentor/orgs/{org}/spend-caps/tenant/— create (201) or update (200) the org-wide cap. - DELETE
/api/ai-mentor/orgs/{org}/spend-caps/tenant/— remove it (204).
Agent cap
- PUT
/api/ai-mentor/orgs/{org}/mentors/{mentor}/spend-cap/— create/update the cap for one agent. - DELETE
/api/ai-mentor/orgs/{org}/mentors/{mentor}/spend-cap/— remove it.
User-per-agent cap
- PUT
/api/ai-mentor/orgs/{org}/mentors/{mentor}/spend-caps/users/{username}/— create/update the cap for one user on one agent (username comes from the URL, not the body). - DELETE
/api/ai-mentor/orgs/{org}/mentors/{mentor}/spend-caps/users/{username}/— remove it.
Example
dm=https://api.iblai.app/dm
auth="Authorization: Api-Token $IBLAI_API_KEY"
# Set a $200/month hard-block tenant cap that alerts at 75% and 90%.
curl -X PUT "$dm/api/ai-mentor/orgs/$IBLAI_ORG/spend-caps/tenant/" \
-H "$auth" -H 'Content-Type: application/json' \
-d '{"max_cost_usd": "200.00", "interval_type": "month",
"enforcement": "block", "alert_thresholds": [0.75, 0.9]}'
# Cap one agent at $50/week, alert-only (never blocks chats).
curl -X PUT "$dm/api/ai-mentor/orgs/$IBLAI_ORG/mentors/$MENTOR/spend-cap/" \
-H "$auth" -H 'Content-Type: application/json' \
-d '{"max_cost_usd": "50.00", "interval_type": "week", "enforcement": "alert_only"}'
# How close is a user to their limits? (learner-safe, dollars hidden)
curl "$dm/api/ai-mentor/orgs/$IBLAI_ORG/spend-caps/status/$IBLAI_USERNAME/?mentor=$MENTOR" \
-H "$auth"
# Remove the tenant cap.
curl -X DELETE "$dm/api/ai-mentor/orgs/$IBLAI_ORG/spend-caps/tenant/" -H "$auth"
Notes
- Blocking is 429, not 402. A spend cap that blocks a chat returns HTTP
429 with
{"error_code": "spend_cap_exceeded", ...}; this is distinct from the 402 returned when a user runs out of purchased credits (seeiblai-api-billing). A cap ceilings admin-set LLM cost; credits are a per-user prepaid balance. - Counters are eventually consistent.
current_spend_usdis refreshed by a scheduled reconcile task from ClickHouse, so it can lag real-time spend slightly. Enforcement reads the cached value to stay off the hot path. - Status hides dollars by design. The status endpoint only exposes
percent_usedand a zone so a non-admin cannot read tenant/agent-wide spend totals; use the admin read endpoints for the dollar figures. - One cap per (scope, agent, user). Unique constraints allow a single tenant cap, one cap per agent, and one cap per (agent, user); PUT upserts that single row.
- Disable vs delete. Set
enabled: falseto pause a cap without losing its configuration; DELETE removes it entirely.
Schema
Cap object (read; write accepts the non-read-only fields):
| Field | Type | Notes |
|---|---|---|
id |
int | read-only |
scope |
str | tenant | agent | user_agent; read-only (from URL) |
platform_key |
str | read-only |
mentor_unique_id |
str | null | read-only; set for agent/user_agent |
mentor_name |
str | null | read-only |
username |
str | null | read-only; set for user_agent |
email |
str | null | read-only; set for user_agent |
interval_type |
str | write, required: day|week|month|year |
max_cost_usd |
decimal string | write, required on create, > 0 |
enforcement |
str | write: block (default) | alert_only |
alert_thresholds |
list[float] | write: fractions in (0, 1]; default [0.8, 0.95] |
enabled |
bool | write: default true |
period_started_at |
datetime | null | read-only (reconciled) |
current_spend_usd |
decimal string | read-only (reconciled) |
remaining_usd |
decimal string | read-only (max_cost_usd - current_spend_usd, floored at 0) |
is_exceeded |
bool | read-only (current_spend_usd >= max_cost_usd) |
last_reconciled_at |
datetime | null | read-only |
created_at / updated_at |
datetime | read-only |
Status summary (from the status endpoint):
| Field | Type | Notes |
|---|---|---|
status |
str | worst zone: ok | warning | exceeded |
blocked |
bool | a hard-block cap is already exceeded |
caps[] |
list | one entry per applicable cap |
caps[].scope |
str | tenant | agent | user_agent |
caps[].interval_type |
str | day | week | month | year |
caps[].enforcement |
str | block | alert_only |
caps[].status |
str | ok | warning | exceeded |
caps[].percent_used |
float | 0–100+, one decimal |
caps[].warning_threshold |
float | null | lowest configured alert threshold |
caps[].mentor_unique_id |
str | null | set for agent/user_agent caps |
caps[].username |
str | null | set for user_agent caps |