Agent cost controls
LLM-driven agents can consume budget unboundedly if a single user message triggers an infinite tool-call loop, or if a long-running conversation crosses a cost ceiling you only want to hit once. Orbit gates every agent run on three independent ceilings, all configurable per agent.The three ceilings
Both cost caps accept
0–10000 cents ($0–$100); 10000 cents ($100) is the maximum — setting either field higher returns a 400 validation error. See Semantics for the full value table.
When any ceiling is exceeded the agent emits a final error event and stops:
COST_LIMIT (per-run cost), CONVERSATION_COST_CAP_REACHED (per-conversation cost), API_CALL_LIMIT (per-run LLM-call cap), TOKEN_LIMIT (per-conversation token aggregate), and TOOL_ITERATION_LIMIT (tool-loop guard).
Configure on agent create
tool_loop_limit is the per-agent tool-iteration knob (bounds 1..50, default 10). It is persisted under config.tool_loop_limit; when unset it falls back to the platform default of 10. The run-wide hard envelope of 50 LLM API calls (AGENT_MAX_API_CALLS_PER_RUN) is always enforced on top of it and cannot be raised by agent config.
Configure on agent update
config.max_cost_per_run_cents / config.max_cost_per_conversation_cents on the agent row.
Semantics
Bothmax_cost_per_run_cents and max_cost_per_conversation_cents accept a whole number of USD cents in the range 0–10000 ($0–$100). 10000 cents ($100) is the maximum accepted value — a request that sets either field above it is rejected with a 400 validation error before the agent is created or updated. This input ceiling is independent of the platform hard cap that silently clamps the resolved per-conversation value (see below).
How accumulated cost is computed
- Per-run: real-time accumulation across the streaming loop. Estimated from prompt + completion tokens at the model’s per-1K rate, plus a flat per-API-call overhead.
- Per-conversation: tracked in Redis under a key keyed off
agent_conversations.id. Each run debits the counter on completion; the next run’s iteration-0 probe reads the counter and refuses if it’s already over the cap. - The counter is reset only by deleting the conversation. Re-using a
conversation_idafter the cap will keep failing until you provision a fresh one.
Which cap participates in the org/env precedence chain
Only the per-conversation cap resolves through a fallback chain. The runtime callsresolveCostCapCents and takes the first non-null value (an explicit 0 kill-switch wins over a null further down):
- Per-agent —
agents.config.max_cost_per_conversation_cents - Per-org —
organizations.settings.agent_runtime_cost_cap_cents - Platform default — the
DEVOTEL_AGENT_COST_CAP_CENTS_DEFAULTenv var
max_cost_per_run_cents) is a per-agent-only knob with no org or env fallback. The runtime enforces it directly off the agent definition (accumulated run cost >= the cap → COST_LIMIT); if you leave it unset on the agent it falls back to the platform default of $1.00 per run (not unbounded), regardless of any org-level or env-level setting. To allow more than $1.00 of spend in a single run, set max_cost_per_run_cents explicitly. There is no agent_runtime_cost_cap_cents equivalent for per-run spend.
Per-conversation token ceiling
In addition to the cost caps above, every conversation is bounded by an aggregate token ceiling. When the running token total for a conversation crosses this value the agent terminates gracefully and the run ends with aTOKEN_LIMIT error code. This guards against runaway loops where token usage grows non-linearly with context length even when the per-token cost is cheap.
The
10000 default is a deliberately conservative back-compat value, not a hard platform cap. Set max_tokens_per_conversation on the agent (create or update) to raise (or lower) it — for example 50000 for long support resolutions or multi-step workflows. Send null to clear an override back to the default. When raising it, also raise max_cost_per_run_cents / max_cost_per_conversation_cents accordingly, since the tenant-configurable spend cap remains the primary ceiling.
config.max_tokens_per_conversation on the agent row; values must be a whole number of tokens between 1 and 1000000.
Token budgets, daily caps, and the model-downgrade ladder
The ceilings above stop runaway spend at a hard boundary. The token guardrails below are complementary: they let one agent spend more gracefully, stepping down to progressively cheaper models as it approaches its own daily token allowance instead of staying on its most expensive model until a hard stop. Three per-agent knobs, all persisted under the agent’sconfig:
- Runs on its primary model while daily usage stays below the first rung (under 50% of the 500,000-token daily cap).
- Steps down to Sonnet once usage crosses 50% of the cap — the downgrade applies to the next turn onward, progressively.
- Steps down to Haiku once usage crosses 80%, again for every subsequent turn that day.
- Resets at UTC midnight, when the daily usage window rolls over and the agent returns to its primary model.
422 at save time instead of being silently ignored later. Send null for either token_budget or downgrade_ladder to clear the guardrail back to “no limit at this layer”.
Token usage for the daily cap is measured on a UTC calendar day, not the workspace’s billing timezone. An enforcement boundary can drift by a few hours at local midnight; if that matters to you, set the daily cap with headroom.
How the runtime resolves the effective model
Before each turn the agent runtime calls the internal resolver endpoint and receives the effective model plus the current allowance in one round trip:model, whether it was downgraded, the reason (no_cap_configured, within_budget, no_downgrade_step, or daily_token_cap_reached), the configured token_budget and daily_token_cap, the tokens already used today (daily_tokens_used), the utilization percent, and the engaged_step that fired. Treat the endpoint as internal — it is authenticated with the runtime’s internal service token, not an end-customer API key.
Hard platform ceilings (always enforced)
These are floor / ceiling values that no agent config can override:
They exist to prevent runaway behaviour even when an agent’s per-run cap is set to a high value.
MAX_TOOL_ITERATIONS_HARD_CEILING (50) is the absolute ceiling on tool_loop_limit — a per-agent override is clamped to this and cannot exceed it. The platform default of 10 (when tool_loop_limit is unset) is not a hard ceiling: it is the overridable starting value described in the tool_loop_limit knob above.
Platform budget governors (operator-facing)
The per-agent / per-conversation knobs above are tenant-configurable via the agents API. Underneath them sit platform-level spend and rate governors set by environment variable (defaults inpackages/shared/src/env.ts, overridden per cluster via Secret Manager). These are the values an SRE greps when paged for a platform-wide 503 LlmPlatformCeilingExceeded or a tenant 429 RATE_LIMIT_EXCEEDED (scope: tenant_tpm). They are listed in .env.example under the LLM budget / cost governors block.
Observability
Cost-cap enforcement is observable through logs and metrics, not webhooks — there is no subscribable cost-cap webhook event. Every per-conversation cap fire emits a structuredwarn log line from the agent runtime carrying a metric field you can alert on:
agent.conversation_cost_cap_hit metric to catch runs that hit the cap. Clients also receive the final error event with the matching code (COST_LIMIT, CONVERSATION_COST_CAP_REACHED, API_CALL_LIMIT, TOKEN_LIMIT, or TOOL_ITERATION_LIMIT) on the aborted run. There is no agent.run.aborted webhook — do not subscribe for one.
Invocation rate limits and quotas
The ceilings above bound spend. A separate, independently-configured set of limits bounds invocation volume instead — how often the agent can be called at all, regardless of cost per call. Configure them through a dedicated endpoint rather than the agent create/update body:
For every field:
null (or an omitted field on a partial PUT) leaves that axis unlimited, and 0 is a kill-switch that blocks all invocations on that axis. A PUT only changes the fields you send — anything you omit keeps its current value.
GET /api/v1/agents/{id}/rate-limits returns the current configuration plus live usage and remaining headroom for each axis; pass ?caller_id= to also see one caller’s live usage. See the Agents API reference for the full request/response shape.
Recommended starting values
- Tier-1 customer support agent —
max_cost_per_run_cents=25,max_cost_per_conversation_cents=500. - Voice agent (longer turns, STT/TTS overhead) —
max_cost_per_run_cents=50,max_cost_per_conversation_cents=1000. - Internal QA / sandbox —
max_cost_per_run_cents=10,max_cost_per_conversation_cents=50to catch loops fast.
The voice-agent
max_cost_per_conversation_cents=1000 (5 platform hard cap** (DEVOTEL_LLM_PER_CONVERSATION_MAX_USD). Unless an operator raises that env var, the effective per-conversation ceiling is silently halved to $5 — see the precedence-chain warning. Confirm the platform value is set to at least your intended cap before relying on it.See also
- Creating Agents — full agent-create reference.
- Agents API — schema for
max_cost_per_run_cents/max_cost_per_conversation_cents, and the Rate Limits endpoints for invocation volume. - Webhooks → Events — full catalog of subscribable
agent.*events (cost-cap aborts are not among them; use logs/metrics).