> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Agent cost controls

> Per-run and per-conversation spend caps, token limits, and tool-iteration ceilings for AI agents.

# 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

| Ceiling                           | Scope                                                                                                            | Default                                                                        | What triggers it                                                                                                                                        |
| --------------------------------- | ---------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `max_cost_per_run_cents`          | One `chat` / `invoke` / `chat-stream` call (one user turn → one assistant turn, including tool-call iterations). | `$1.00` platform default when unset (per-agent only — **no** org/env fallback) | Aggregated cost across all LLM calls within a single run, computed from per-model token rates.                                                          |
| `max_cost_per_conversation_cents` | The lifetime of a single `agent_conversations.id` — every run that touches the same conversation row.            | unbounded (falls back to the org/env cap — see precedence below)               | Probed before every LLM iteration, including iteration 0, so a conversation that hit the cap on a previous turn is also blocked on the next turn.       |
| `tool_loop_limit`                 | Tool-call loop iterations within one run (the per-turn tool-iteration loop guard).                               | 10 (bounds 1..50)                                                              | Counts each tool-call loop iteration. The run-wide hard envelope of 50 LLM API calls (`AGENT_MAX_API_CALLS_PER_RUN`) is always enforced on top of this. |

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](#semantics) for the full value table.

When any ceiling is exceeded the agent emits a final `error` event and stops:

```json theme={null}
{ "type": "error", "code": "COST_LIMIT", "message": "Cost budget exceeded" }
```

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

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/agents \
  -H "X-API-Key: dv_live_sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Support Agent",
    "model": "claude-sonnet-4-6",
    "system_prompt": "You are a customer support agent for Acme Corp.",
    "max_cost_per_run_cents": 25,
    "max_cost_per_conversation_cents": 500,
    "tool_loop_limit": 10
  }'
```

`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

```bash theme={null}
curl -X PUT https://api.orbit.devotel.io/api/v1/agents/agent_abc123 \
  -H "X-API-Key: dv_live_sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "max_cost_per_run_cents": 25,
    "max_cost_per_conversation_cents": 500
  }'
```

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 `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).

| Value               | Meaning                                                                                                                                                                                                                                                                                                                                                          |
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Positive integer    | The cap (in USD cents), from `1` to `10000` (`$100` max — higher values return a `400` validation error). Run / conversation aborts when accumulated cost exceeds this value.                                                                                                                                                                                    |
| `null` (or omitted) | Falls back to the platform default. For `max_cost_per_conversation_cents` the org/env cap still applies (see precedence below); for `max_cost_per_run_cents` there is **no** org/env fallback — an unset per-run cap falls back to the platform default of **`$1.00` per run** (it is *not* unbounded). Set the field explicitly to raise or lower that ceiling. |
| `0`                 | **Kill switch.** Blocks ALL spend immediately — first LLM call fails with `COST_LIMIT`. Useful when you want to disable an agent without deleting it.                                                                                                                                                                                                            |

## 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-agent** — `agents.config.max_cost_per_conversation_cents`
2. **Per-org** — `organizations.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.

<Warning>
  **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](#platform-budget-governors-operator-facing), default \*\*$5 / 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.
</Warning>

## 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.

| Setting                       | Scope                                                          | Default | Overridable      |
| ----------------------------- | -------------------------------------------------------------- | ------- | ---------------- |
| `max_tokens_per_conversation` | Aggregate tokens across the lifetime of a single conversation. | `10000` | Yes — per agent. |

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.

```bash theme={null}
curl -X PUT https://api.orbit.devotel.io/api/v1/agents/agent_abc123 \
  -H "X-API-Key: dv_live_sk_..." \
  -H "Content-Type: application/json" \
  -d '{ "max_tokens_per_conversation": 50000 }'
```

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`.

## Hard platform ceilings (always enforced)

These are floor / ceiling values that no agent config can override:

| Limit                              | Value |
| ---------------------------------- | ----- |
| `AGENT_MAX_API_CALLS_PER_RUN`      | 50    |
| `MAX_TOOL_ITERATIONS_HARD_CEILING` | 50    |

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`](#the-three-ceilings) 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.

| Env var                                           | Default  | Scope            | What it does                                                                                                                                                                                                                                                                    |
| ------------------------------------------------- | -------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `DEVOTEL_LLM_BUDGET_DEFAULT_MONTHLY_USD`          | `200`    | Per tenant       | Default monthly LLM spend budget — long-horizon spend governor.                                                                                                                                                                                                                 |
| `DEVOTEL_LLM_BUDGET_DEFAULT_DAILY_USD`            | `20`     | Per tenant       | Default daily LLM spend budget. Pairs with the monthly cap; both gates fire, whichever exceeds first wins. Defends the "burn the whole month in 6h" runaway pattern.                                                                                                            |
| `DEVOTEL_LLM_BUDGET_PLATFORM_MONTHLY_CEILING_USD` | `10000`  | **All tenants**  | Hard aggregate monthly ceiling across every tenant. When summed all-tenant monthly spend would exceed this, the gateway raises `LlmPlatformCeilingExceeded` (**503**) platform-wide and fires a Sentry alert. Set `0` to disable (not recommended).                             |
| `DEVOTEL_LLM_PER_CONVERSATION_MAX_USD`            | `5`      | Per conversation | Hard cap on per-conversation LLM cost (USD).                                                                                                                                                                                                                                    |
| `DEVOTEL_LLM_PER_CONVERSATION_MAX_ITERATIONS`     | `12`     | Per conversation | Per-conversation tool/LLM iteration cap (minimum `1`).                                                                                                                                                                                                                          |
| `DEVOTEL_LLM_TPM_PER_TENANT`                      | `200000` | Per tenant       | Tokens-per-minute sliding-window breaker. Rejects any new Anthropic call that would push the tenant's 60s token sum past this cap with a **429** `RATE_LIMIT_EXCEEDED` + `Retry-After: 60`. Default mirrors Anthropic's Tier-2 TPM floor. Set `0` to disable (not recommended). |

<Warning>
  `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.
</Warning>

## 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:

```json theme={null}
{
  "level": "warn",
  "metric": "agent.conversation_cost_cap_hit",
  "tenantId": "org_abc123",
  "conversationId": "conv_def456",
  "currentCents": 514,
  "capCents": 500,
  "msg": "Conversation cost cap reached — refusing further agent runs"
}
```

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.

## 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=50` to catch loops fast.

<Note>
  The voice-agent `max_cost_per_conversation_cents=1000` ($10) 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](#which-cap-participates-in-the-orgenv-precedence-chain). Confirm the platform value is set to at least your intended cap before relying on it.
</Note>

## See also

* [Creating Agents](/agents/creating-agents) — full agent-create reference.
* [Agents API](/api-reference/agents) — schema for `max_cost_per_run_cents` / `max_cost_per_conversation_cents`.
* [Webhooks → Events](/webhooks/events) — full catalog of subscribable `agent.*` events (cost-cap aborts are not among them; use logs/metrics).
