feat(claude): scope overage/credit rejections to the requested model (subscription-only toggle)
Summary
On a Claude OAuth credential where usage credits (overage) are enabled but the org spend cap is reached, a 429 for a model that can only be served from overage cools down the entire credential for the upstream Retry-After (33.7h in my case), even though the same credential is still healthy and serving on its included subscription allowance.
I'd like a toggle to keep CPA on subscription allowance only — never spending usage credits, and never generalizing an overage-only rejection to the whole credential.
Version: v7.3.6 (Docker eceasy/cli-proxy-api:v7.3.6).
What happens
A claude-fable-5-1 request landed on a team-plan credential. Fable on that org can only be served from overage, so Anthropic returned 429 with these unified headers:
Anthropic-Ratelimit-Unified-Status = rejected
Anthropic-Ratelimit-Unified-Representative-Claim = seven_day_overage_included
Anthropic-Ratelimit-Unified-7d-Status = allowed <-- included weekly is fine
Anthropic-Ratelimit-Unified-7d-Utilization = 0.69
Anthropic-Ratelimit-Unified-5h-Utilization = 0.00
Anthropic-Ratelimit-Unified-7d_oi-Status = rejected
Anthropic-Ratelimit-Unified-7d_oi-Utilization = 1.02
Anthropic-Ratelimit-Unified-Overage-Status = rejected
Anthropic-Ratelimit-Unified-Overage-Disabled-Reason = org_spend_cap_reached
Retry-After = 121180
CPA recorded a model-scoped cooldown for claude-fable-5-1 (correct) and a credential-level next_retry_after 33.7h out, which set unavailable: true on the credential. From GET /v0/management/auth-files:
{
"account": "…", "unavailable": true, "status": "error",
"next_retry_after": "2026-09-19T09:00:13+08:00",
"cooldowns": [{ "scope": "model", "model_key": "claude-fable-5-1", "reason": "quota", "http_status": 429 }]
}
The credential then served no model at all — Opus and Sonnet included — for the full window.
Why that's wrong here
I called api.anthropic.com/v1/messages directly with the same credential while CPA had it benched:
POST /v1/messages model=claude-opus-4-5-20251101 -> HTTP 200
anthropic-ratelimit-unified-status = allowed
anthropic-ratelimit-unified-representative-claim = five_hour
anthropic-ratelimit-unified-7d-status = allowed (0.69 utilization)
anthropic-ratelimit-unified-overage-status = rejected (org_spend_cap_reached)
The representative claim — and therefore the unified status — depends on which window the requested model draws from. For an overage-only model it is seven_day_overage_included and rejected; for a subscription-served model it is five_hour and allowed. So a credential-level Retry-After taken from an overage-only rejection does not describe the credential.
Requested behavior
Either or both, off by default:
claude.model-level-cooling— the Claude counterpart of the existingcodex.model-level-cooling: scope Anthropic quota cooldowns to the requested model rather than cooling the credential across sibling models.A "subscription only / no usage credits" toggle (e.g.
claude.disable-usage-credits), which:- treats a rejection whose
Overage-Status: rejectedwhile5h-Status/7d-Statusareallowedas model-scoped, not credential-scoped; - optionally declines to route a request to a credential when the model would have to draw on overage.
- treats a rejection whose
A narrow, header-driven rule would cover the case without a new setting at all: if Unified-Status: rejected but the non-overage windows (5h, 7d) report allowed, and Overage-Status: rejected, apply the cooldown to the requested model only. That is exactly the information Anthropic already returns, and CPA already parses it into quota.signals.
Workaround
Per-credential excluded_models in the auth JSON (PATCH /v0/management/auth-files/fields) keeps overage-only models off the affected credential, so the rejection never happens. That works, but it requires knowing in advance which models a given org can only serve from credits, and it has to be maintained per credential as the model list changes.
Source: router-for-me/CLIProxyAPI