Skip to main content

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 010000 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:
The corresponding codes are 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

Both fields are persisted under config.max_cost_per_run_cents / config.max_cost_per_conversation_cents on the agent row.

Semantics

Both max_cost_per_run_cents and max_cost_per_conversation_cents accept a whole number of USD cents in the range 010000 ($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_id after 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 calls resolveCostCapCents and takes the first non-null value (an explicit 0 kill-switch wins over a null further down):
  1. Per-agentagents.config.max_cost_per_conversation_cents
  2. Per-orgorganizations.settings.agent_runtime_cost_cap_cents
  3. Platform default — the DEVOTEL_AGENT_COST_CAP_CENTS_DEFAULT env var
The per-run cap (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.
A platform hard cap clamps the resolved per-conversation value — regardless of source. After the chain above resolves, the runtime applies DEVOTEL_LLM_PER_CONVERSATION_MAX_USD (see Platform budget governors, default **5/500cents)asafinalceiling.Anyresolvedvalueabovethehardcapincludinganexplicitperagentmaxcostperconversationcentsissilentlyloweredtoit;aresolvednull(unbounded)inheritsit.ThismeansaperagentcapyousetthroughtheAPIcanbeenforcedatalowernumberthanyouconfigured.Toletaperconversationcapabove5 / 500 cents**) as a final ceiling. Any resolved value **above** the hard cap — including an explicit per-agent `max_cost_per_conversation_cents` — is silently lowered to it; a resolved `null` (unbounded) inherits it. This means a per-agent cap you set through the API can be enforced at a *lower* number than you configured. To let a per-conversation cap above 5 take effect, an operator must raise DEVOTEL_LLM_PER_CONVERSATION_MAX_USD (or set it to 0 to disable the ceiling); no agent- or org-level setting can lift it.

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 a TOKEN_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.
It is persisted under 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’s config:
With that configuration the agent:
  1. Runs on its primary model while daily usage stays below the first rung (under 50% of the 500,000-token daily cap).
  2. Steps down to Sonnet once usage crosses 50% of the cap — the downgrade applies to the next turn onward, progressively.
  3. Steps down to Haiku once usage crosses 80%, again for every subsequent turn that day.
  4. Resets at UTC midnight, when the daily usage window rolls over and the agent returns to its primary model.
The ladder is progressive — the highest threshold already met decides the effective model. If a rung names the agent’s primary model (nothing cheaper to step to) the threshold breach is reported but the model stays put, so a half-configured ladder never routes a live turn to an unexpected model. Both knobs are validated when the agent is created or updated: a non-integer budget, a misordered or over-long ladder, or a rung naming an unsupported model is rejected with a 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:
The response carries the resolved 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 in packages/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.
DEVOTEL_LLM_BUDGET_PLATFORM_MONTHLY_CEILING_USD is a hard 503 gate across ALL tenants — when it trips, every tenant’s agent traffic stops, not just the offending one. Raise it (or investigate the runaway tenant) before clearing the page; do not set it to 0 to silence an incident.

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 structured warn log line from the agent runtime carrying a metric field you can alert on:
Alert on the 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.
  • Tier-1 customer support agentmax_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 / sandboxmax_cost_per_run_cents=10, max_cost_per_conversation_cents=50 to catch loops fast.
The voice-agent max_cost_per_conversation_cents=1000 (10)aboveexceedsthedefault10) above **exceeds the default 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).