Breaker
A budget one run carries, declared in request headers. The run is refused at its own line and stays stopped; other runs keep going.
On this page
Recovea Breaker is a budget one run carries, in dollars. When the run reaches it the next request is refused, and the run stays stopped until a person starts a new one.
What a run is
Whatever your own system already treats as one piece of work: an agent task, a chain, a nightly job, one thread. Set the run id to your trace or thread id. The envelope is the dollar figure that run may spend. Run envelopes are on every plan, Free included.
Only the calls that name a run in a header carry a Breaker. A call that names none is untouched by it, and is still bound by the caps on your organization and key.
What the request carries
Three request headers, declared by the run itself:
| Header | What it takes |
|---|---|
x-recovea-run-id |
1 to 128 visible ASCII characters. A run header sent without it is refused rather than dropped. |
x-recovea-run-budget |
Decimal US dollars, 6 decimal places at most. A run may declare up to twenty-five dollars without approval. |
x-recovea-run-ttl |
Whole seconds. 3600 by default, 60 at the shortest, 86400 at the longest. |
curl -sS -D - https://api.recovea.ai/v1/chat/completions \
-H "Authorization: Bearer $RECOVEA_API_KEY" \
-H "Content-Type: application/json" \
-H "x-recovea-run-id: nightly-summary-2026-09-02" \
-H "x-recovea-run-budget: 0.50" \
-H "x-recovea-run-ttl: 900" \
-d '{"model":"gpt-4o","messages":[{"role":"user","content":"ping"}]}'Nothing is configured first. The first request that names a run opens its envelope, and every request that names the same run id spends from it.
A malformed run header is a syntax error, and the request never reaches your
provider: it answers 400 with error.type invalid_request_error and
error.code invalid_run_id, invalid_run_budget or invalid_run_ttl. Run
headers are control, so a bad one is refused rather than dropped: a caller whose
budget was silently dropped would believe the run was bounded while it ran with
no envelope at all.
What the gateway does
It keeps a counter per run and checks it at admission, before your provider is contacted, exactly as it checks a cap. An envelope only ever tightens: it nests inside the caps on your organization and your key and can never raise them.
A run that declares more than it may declare on its own is not refused. Its envelope is lowered to the most it may declare, and the response says so.
The x-recovea-* run headers are removed before the request is forwarded, so
they never reach your provider.
What comes back
Served
Every answer on a run carries the envelope that actually binds, to six decimal places:
x-recovea-run-budget-effective: 0.500000Declare more than a key may declare on its own and the figure is lowered rather
than refused, with x-recovea-run-clamp-reason: policy_max beside it saying
which bound cut it. When nothing cut the declaration, no clamp reason is sent.
Refused at the run's line
Every Recovea limit answers in one shape — one status, one type, one code — and the sentence inside it names the window that reached its line. A run budget says run.
HTTP/1.1 402 Payment Required
{
"error": {
"message": "Run budget reached. This run is stopped for good; start a new run to continue.",
"type": "insufficient_quota",
"param": null,
"code": "budget_exceeded"
}
}The refusal carries x-recovea-budget-scope: run and
x-recovea-budget-window: run. The body does not name the run. Your own run id
names it on the request, and the refusal is receipted against the run itself.
Three codes reach a run:
| Status | error.code |
What it means |
|---|---|---|
402 |
budget_exceeded |
The run reached its envelope and was stopped mid-run, before the provider was called. |
402 |
run_expired |
The run's time-to-live ran out and the policy is armed death, so later requests were refused. THIS WAS NOT AN OVERSPEND. |
503 |
budget_unverifiable |
An armed limit could not verify live spend, so it refused rather than let spend run unwatched. THIS WAS NOT AN OVERSPEND. |
The meanings are the platform's own sentences, printed the same way here. Both
402s carry error.type insufficient_quota. run_expired reads no figure,
so it carries no budget headers. budget_unverifiable carries Retry-After: 1
and clears on its own; retry it, as the
quickstart shows.
A tripped run stays tripped
A tripped run does not re-arm. Declaring a bigger budget under the same run id is refused the same way; only a new run id opens a new envelope. A sibling run on the same key keeps its own envelope and keeps going.
An envelope is evaluated between requests, so a response already streaming when the line was reached can overshoot it by roughly one reconcile interval.
In the platform
The Runs page lists your runs, each with its key and its state, and a run's own page records the refusals and requests made under it, with the figures as they stood when they were recorded. The most a run may declare without approval is the Self-declare threshold on the run policy page, which an Owner or an Admin can change (Roles).