Управление версиями Anthropic API и бета-заголовками в многотенантном шлюзе
Рассматривайте антропную версию и антропную бета-версию как возможности на уровне клиента. Создавайте реестр версий, применяйте белые списки бета-версий, направляйте только к совместимым восходящим потокам, сохраняйте потоки с прямой совместимостью и записывайте действующий протокол при каждом событии использования.
An Anthropic-compatible API gateway should not treat anthropic-version and anthropic-beta as ordinary pass-through headers. They select protocol behavior and experimental capabilities that can change validation rules, response blocks, streaming events, availability, pricing, and cache accounting.
The practical problem is preserving native Claude behavior without allowing one tenant's request headers to bypass platform policy. The solution is to make protocol admission a first-class gateway decision: authenticate the caller, resolve its version policy, validate requested beta features, select a compatible route, and record exactly what was sent upstream.
Define the policy boundary
Fact: Direct Claude API requests require an anthropic-version header, although official SDKs normally supply it. Anthropic beta features are requested through anthropic-beta or an SDK-specific beta parameter.
Recommendation: Interpret both values as requested capabilities, not trusted routing instructions. A client may ask for a version or beta, but the gateway decides whether that combination is supported for the tenant, API key, model, region, and upstream credentials.
Do not silently strip an unapproved beta. That can turn an explicit request into a successful call with different semantics. Reject it before dispatch with a stable gateway error that identifies the unsupported capability without exposing upstream credentials or internal route details.
{
"type": "gateway_policy_error",
"code": "beta_not_allowed",
"message": "The requested Anthropic beta is not enabled for this API key.",
"details": {
"beta": "example-beta-2026-01-01",
"policy_id": "claude-production"
}
}
Return a 4xx response for admission failures and reserve 5xx responses for gateway or upstream failures. Keep the error schema stable even if Anthropic changes its own error format.
Build a version and capability registry
A date-shaped version string is easy to validate syntactically but impossible to govern from syntax alone. The gateway needs a registry containing the versions and beta features it has actually tested.
Version records
Each version record should include:
- Canonical version identifier and lifecycle state: testing, available, default, deprecated, or blocked.
- Supported request fields, content blocks, stop reasons, usage fields, and known streaming events.
- Compatible models, upstream platforms, regions, and credential pools.
- Contract-fixture results and the date of the latest successful test.
- Pricing-catalog compatibility and any known cache-accounting implications.
- Adoption restrictions, deprecation dates, and replacement guidance.
Beta records
Beta features need separate records because their availability and operational risk can differ from the base API version. Record the canonical beta name, compatible versions and models, permitted regions, required request fields, possible response blocks, streaming events, pricing treatment, rate-limit class, and rollout state.
Model this as data rather than application conditionals. A policy evaluator can then answer a concrete question: “May key K use version V with beta set B on route R?” The same catalog can support a multi-model API gateway for teams without forcing every adapter to implement its own tenant rules.
Give every tenant an explicit version policy
Support three policy modes:
- Pinned: The tenant or key must use one exact version. Reject missing or different values.
- Gateway current: A missing header resolves to the gateway's tested default. An explicit client value is still validated.
- Approved range: The client may select from a finite allowlist of tested versions.
Avoid interpreting “range” as every date between two endpoints. Anthropic versions are identifiers, not a continuous semantic-version sequence. Store an explicit set of admitted values.
Resolve the effective version before selecting an upstream route:
requested = parse_version_header(request)
policy = load_policy(tenant_id, api_key_id)
effective = resolve_version(requested, policy, registry)
betas = parse_and_canonicalize_betas(request)
admitted = authorize_betas(betas, policy, effective, registry)
route = select_compatible_route(model, region, effective, admitted)
dispatch(route, effective, admitted)
Keep the requested and effective values separate. They may differ when a policy supplies a default, but an audit record must make that substitution visible.
Parse beta headers structurally
Do not authorize beta features with substring matching or raw-string equality. Parse the header according to the syntax accepted by the upstream API, reject malformed or duplicate entries as appropriate, and compare canonical feature identifiers.
Canonical ordering is useful for policy-cache keys and telemetry dimensions:
requested header: beta-b, beta-a
canonical policy set: [beta-a, beta-b]
upstream header: beta-b,beta-a // serialized in accepted upstream syntax
Preserve enough request metadata for debugging, but do not retain raw headers indefinitely when normalized identifiers are sufficient. Header logs can contain unnecessary client metadata, and storing them increases the scope of retention and access controls.
Beta authorization should be tenant-scoped and preferably key-scoped. A development key can receive an experimental capability without automatically granting it to production workloads. Changes to allowlists should produce control-plane audit events with actor, reason, previous state, new state, and effective time.
Separate admission from routing
A request is not routable merely because its model name exists. Route eligibility is the intersection of protocol and infrastructure capabilities:
eligible(route) =
route.platform supports effective_version
AND route.model supports every admitted_beta
AND route.region permits every admitted_beta
AND route.credentials are entitled to those capabilities
AND route.pricing_catalog can account for the result
If no route satisfies the full set, fail before sending the request. Do not drop beta features to make a route eligible. Do not automatically substitute another version unless the tenant policy explicitly defines that behavior.
This separation matters in gateways that pool credentials, support customer-supplied keys, or operate across regions. Two routes to the same model can have different feature access, residency constraints, or rate limits.
Preserve native response semantics
Fact: Anthropic Messages responses can end with reasons including tool use, token limits, context-window limits, paused turns, and refusals. Streaming responses use named server-sent events, and Anthropic's compatibility guidance requires clients to tolerate new event types.
Recommendation: Do not collapse all successful terminal states into “completed.” Preserve the upstream stop reason in the native response and in gateway telemetry. Applications may need to execute a tool, continue a paused turn, reduce context, or surface a refusal.
The stream proxy should recognize events required for accounting and lifecycle management while remaining forward-compatible:
- Forward unknown event types when the tenant's policy allows native pass-through.
- Retain the raw event name and sequence position in bounded diagnostic metadata.
- Do not use an exhaustive enum that closes the stream on an unfamiliar additive event.
- Distinguish an upstream error event from a transport disconnect and a client cancellation.
- Finalize usage only from authoritative fields; mark incomplete streams as provisional when final usage is unavailable.
If the gateway also exposes an OpenAI-shaped endpoint, keep that adapter separate from the native Messages path. Anthropic's OpenAI compatibility layer has documented field and feature limitations. A common internal envelope can help with routing, but it must not discard content-block order, cache controls, thinking signatures, tool state, or provider-specific stop reasons.
Make accounting version-aware
Fact: Prompt-cache usage separates ordinary input tokens, cache-creation input tokens, and cache-read input tokens. Total input consumption must account for all applicable categories.
A version or beta change can alter cache eligibility, content representation, or returned usage fields. Therefore, every usage event should include:
- Requested and effective API version.
- Requested and admitted beta sets.
- Tenant, API key, policy, route, model, region, and upstream request ID.
- Status, stop reason, stream completion state, and error attribution.
- Input, output, cache-creation, and cache-read token categories.
- Pricing-catalog version and provisional or finalized cost state.
These dimensions let unified AI API billing explain why otherwise similar requests produced different costs. Avoid billing from a single combined token count when the provider exposes categories with different rates.
Test before enabling a version or beta
Create contract fixtures for ordinary text, streaming text, tool calls, structured output, refusals, token truncation, context-window exhaustion, paused turns, prompt caching, malformed requests, rate limits, and mid-stream errors.
For each fixture, compare more than HTTP status. Capture content-block type and order, stop reason, tool identifiers and arguments, event sequence, usage-field presence, cache categories, and error shape. Store approved structural expectations rather than exact prose output.
Use shadow requests only when duplicate execution is safe. Requests with tools, external side effects, sensitive data, or material cost may be unsuitable. For those workloads, replay sanitized fixtures in a controlled environment.
Prediction: New versions and beta features will continue to add response variants and events. A gateway designed around capability records and additive parsing will usually absorb those changes with less emergency work than one built around fixed enums and global defaults.
Roll out and retire capabilities safely
Promote a capability through testing, internal, opt-in, canary, available, deprecated, and blocked states. During canarying, compare status distributions, stop reasons, content-block sequences, tool behavior, usage fields, latency, and cache outcomes. Define rollback thresholds before traffic begins.
Retirement should happen in stages:
- Block new tenant adoption while existing assignments continue.
- Identify affected tenants, keys, models, and recent request patterns.
- Provide a compatibility report or test endpoint for the replacement.
- Warn on admitted requests and publish a firm cutoff.
- Require explicit exceptions with owners and expiry dates.
- Block the capability and retain the registry record for historical audit and billing interpretation.
Pinning increases reproducibility but expands the test matrix. A gateway-wide version reduces operational variants but couples unrelated tenants to one migration schedule. Broad beta pass-through accelerates feature access but weakens cost, residency, and support guarantees. For a multi-tenant service, explicit per-tenant policy is usually the defensible middle ground.
Implementation checklist
- Require a version policy for every tenant or API key.
- Maintain tested version and beta capability records.
- Reject malformed, unknown, or unauthorized capabilities before dispatch.
- Select routes only after resolving the complete protocol request.
- Preserve native content blocks, stop reasons, usage fields, and streams.
- Forward additive stream events safely and retain their event names.
- Record requested and effective protocol values on usage and audit events.
- Run contract fixtures and controlled canaries before changing defaults.
- Retire old capabilities through measured adoption controls, not silent rewrites.
The key design decision is simple: version and beta headers are policy inputs. Once the gateway admits, routes, tests, and meters them as capabilities, native API compatibility becomes governable without pretending experimental behavior is ordinary HTTP metadata.