Prompt Cache Control in a Multi-Model API Gateway: Stable Prefixes, Tenant Isolation, and Cache-Hit Analytics
A practical gateway architecture for protecting prompt-cache hit rates across OpenAI, Anthropic, and Gemini-style APIs: stable prompt regions, provider metric normalization, tenant isolation, billing attribution, and rollout checks.
Prompt caching is easy to waste. A team may have a 40,000-token system prompt, tool schema, policy block, repository map, or agent memory that should be reusable, then accidentally put a timestamp, request ID, user name, retrieval snippet, or randomized tool ordering near the top of the prompt. The provider sees a different prefix, the cache is missed, latency rises, and the bill looks confusing.
In a single-provider application, you can fix this inside the application template. In a multi-model gateway, the problem is bigger: each provider exposes different cache controls, token thresholds, time-to-live behavior, usage fields, and billing semantics. The gateway needs a portable control-plane pattern for assembling cache-safe prompts, measuring cache behavior, isolating tenants, and attributing cost.
This article describes a reference architecture. It is not a customer case study and does not claim benchmark results. The facts below come from provider documentation and public research; the design recommendations are gateway-level operating guidance.
The failure mode: cache-breaking prompt assembly
Prompt caching generally rewards repeated prompt prefixes. The exact mechanics vary by provider, but the practical implication is consistent: if the front of the prompt changes, reuse suffers.
Common cache breakers include:
- Per-request metadata at the top: timestamps, trace IDs, session IDs, deployment IDs, or generated request labels.
- User-specific data in the prefix: names, account attributes, permissions, or private preferences placed before reusable policy or tool blocks.
- Unstable tool serialization: tool schemas emitted in nondeterministic order, with changing whitespace or generated IDs.
- Retrieval snippets too early: RAG context inserted before stable system instructions or shared repository context.
- Template drift: small wording changes released frequently without versioning or cache diagnostics.
A gateway cannot magically make an unstable prefix cacheable, but it can enforce a prompt assembly contract and make cache misses visible.
Provider facts to design around
The details matter because a gateway must normalize behavior without pretending providers are identical.
- OpenAI: OpenAI has documented prompt caching for the longest previously computed prompt prefix. It begins at 1,024 tokens, increases in 128-token increments, and exposes cached token counts in usage fields. OpenAI also states that prompt caches are typically cleared after 5–10 minutes of inactivity and are always removed within one hour of the cache’s last use.
- Anthropic: Anthropic prompt caching can be requested with
cache_control. Its documentation describes cache matching over prompt components such as tools, system content, and messages up to the block marked with cache control. Anthropic documents an ephemeral cache, including a 5-minute duration and a 1-hour option at additional cost. - Gemini: Google Gemini context caching exposes cache-hit token counts through usage metadata such as
total_cached_tokens, and its documentation lists minimum input token counts by model. - Data-control implication: OpenAI’s API data-control documentation notes that extended prompt caching requires storing key/value tensors as application state in GPU-local storage. Even when providers maintain isolation guarantees, gateways should treat cache behavior as sensitive infrastructure, not as a shared application datastore.
- Research signal: Public research has examined whether gateway-style architectures can introduce prompt-caching vulnerabilities that bypass provider-level cache isolation assumptions. That does not prove a specific gateway is vulnerable, but it supports conservative tenant isolation design.
Recommendation: implement cache control as a gateway feature with explicit policies, not as an accidental side effect of repeated prompts.
A three-region prompt assembly contract
The most important design decision is to separate stable and volatile content before the request reaches a provider adapter.
Region 1: stable prefix
The stable prefix is content expected to remain identical across many requests for the same application, model route, and prompt template version. Examples include:
- core system instructions;
- safety and policy blocks;
- tool schemas;
- static product documentation;
- repository maps for coding agents;
- fixed output-format instructions.
This region should be deterministic. The gateway should build it from versioned templates, canonicalized JSON, and stable ordering rules. If a tool registry is included, sort tools by stable tool ID. If JSON schemas are included, serialize them with deterministic key ordering and no generated timestamps.
Region 2: semi-stable tenant or workspace context
The semi-stable region changes less often than individual requests but is not globally shared. Examples include:
- tenant-specific policy overrides;
- workspace-level tool allowlists;
- customer-specific terminology;
- team coding conventions;
- long-lived project context.
This region should be scoped to a tenant, workspace, or application boundary. It may still be cacheable, but the gateway should never assume that another tenant can safely reuse it.
Region 3: volatile suffix
The volatile suffix is the per-request portion:
- user message;
- retrieved snippets for this query;
- current timestamp, if truly needed;
- request ID and trace metadata, if included in the prompt at all;
- short-term conversation turns;
- runtime tool results.
Most cache misses caused by application design happen because volatile suffix data is accidentally placed in the prefix. A gateway-side builder should make that difficult.
Implementation pattern: stable-prefix builders
A practical gateway implementation can expose a prompt assembly interface rather than accepting one opaque prompt string from every application.
{
"template_id": "code-agent-v3",
"tenant_id": "tenant_123",
"route": "coding-long-context",
"stable_prefix": {
"system_policy_version": "2026-08-01",
"toolset_version": "tools-v12",
"repo_context_version": "repo-map-8491"
},
"semi_stable_context": {
"workspace_policy_version": "workspace-44-v6"
},
"volatile_suffix": {
"user_message": "Explain why this test fails...",
"retrieval_context_ids": ["chunk_7", "chunk_19"],
"trace_id": "not_inserted_into_prompt"
}
}
The gateway then renders the provider-specific request. This gives the gateway a place to enforce rules:
- reject timestamps in stable prefix fields;
- canonicalize tool schemas;
- hash each region separately;
- attach cache controls where a provider supports them;
- preserve prompt semantics while moving volatile material later;
- record template and prefix fingerprints for diagnostics.
For legacy applications that send only raw messages, the gateway can still provide a lint mode: inspect message order, compute prefix fingerprints, and report likely cache breakers without rewriting the prompt initially.
Provider adapter layer: normalize cache usage without hiding differences
A multi-model gateway should not expose three unrelated cache reports to developers. It also should not flatten provider-specific economics so aggressively that invoices become impossible to explain.
Create a normalized cache ledger with fields such as:
{
"request_id": "req_abc",
"tenant_id": "tenant_123",
"app_id": "code-agent",
"route": "coding-long-context",
"provider": "provider_name",
"model": "model_id",
"template_id": "code-agent-v3",
"stable_prefix_hash": "sha256:...",
"semi_stable_hash": "sha256:...",
"input_tokens_total": 58200,
"input_tokens_uncached": 8200,
"cache_write_tokens": 50000,
"cache_read_tokens": 0,
"output_tokens": 1300,
"cache_ttl_class": "ephemeral_5m",
"provider_cache_fields": {
"raw_field_names": "stored_or_redacted_provider_usage"
}
}
The adapter maps provider usage into normalized categories:
- Uncached input tokens: tokens processed without a cache read discount or cache-read accounting.
- Cache write tokens: tokens that created or refreshed a provider-side cache entry when the provider reports this distinction.
- Cache read tokens: tokens served from cache or counted as cached by provider usage metadata.
- Output tokens: generated tokens, which should remain separate from prompt cache economics.
- TTL option: the selected cache duration class where a provider exposes a choice.
Recommendation: store raw provider usage in a redacted, schema-versioned form alongside normalized fields. Normalization is useful for dashboards; raw fields are necessary for reconciliation when provider semantics change.
Cache observability: dashboards that explain misses
A useful cache dashboard does more than show total cached tokens. It should help teams answer: “Which workload is breaking the prefix, and what changed?”
Track cache metrics by:
- tenant;
- workspace or app;
- model route;
- provider and model;
- prompt template version;
- stable prefix hash;
- semi-stable context hash;
- API key or service account, where appropriate;
- time window, especially because cache TTLs are short for many workloads.
Useful derived metrics include:
- Cache read rate: cached input tokens divided by total input tokens eligible for caching.
- Prefix churn: number of distinct stable prefix hashes per template version per hour.
- Template drift: cache-hit changes after a template release.
- Cold-start cost: cache write or uncached input spend for the first request in a burst.
- Route comparison: hit rates across provider routes for the same logical workload.
Do not default to storing raw prompts for debugging. Prefer hashes, region lengths, template IDs, canonicalization warnings, and redacted diffs. If a team needs deeper debugging, require explicit access controls and retention limits.
Tenant isolation policy: do not design for cross-tenant reuse
The safest gateway assumption is simple: cacheable behavior should be tenant-scoped. Even if two tenants share an identical public policy block, the gateway should not intentionally route or shape traffic to exploit cross-tenant cache reuse.
A conservative policy includes:
- Tenant-aware routing: route cacheable traffic using tenant, workspace, and application boundaries.
- No shared secret-bearing prefixes: never place tenant secrets, credentials, private documents, or user-specific data in a reusable shared prefix.
- Separate prefix fingerprints: compute fingerprints with tenant scope included in the gateway ledger, even if the rendered text is identical.
- Org-level controls: allow administrators to disable provider cache features for sensitive workloads.
- Provider isolation is not a product feature to resell: treat provider cache isolation as a baseline protection, not as permission to build cross-customer cache pooling.
Prediction: as long-context agents become more common, cache behavior will become part of security reviews, not just cost reviews. Gateways that can prove tenant-scoped cache policy will be easier to govern.
Billing attribution: separate cache reads, writes, and normal tokens
Prompt caching can make invoices harder to understand if all input tokens are shown as one number. The billing ledger should preserve at least five categories:
- uncached input tokens;
- cache write tokens;
- cache read tokens;
- output tokens;
- provider-specific TTL or cache-control charges.
This matters when one provider discounts cached reads, another charges differently for cache writes, and another exposes a longer TTL option. A customer invoice should be able to explain why two requests with similar total input tokens had different costs.
For internal chargeback, attribute cache effects to the tenant and application that made the request. Avoid allocating a cache-read benefit from one tenant to another. If a shared internal platform team owns the stable prompt template, report template-level cache performance separately from tenant invoices.
Cache-linting checklist
Before enabling cache enforcement, run prompt templates through a lint checklist:
- Stable system instructions appear before volatile user input.
- Tool schemas are sorted by stable ID or name.
- JSON is serialized deterministically.
- No timestamps, random IDs, request IDs, or trace IDs appear in the stable prefix.
- No user-specific secrets appear in shared reusable blocks.
- RAG snippets are placed after reusable policy and tool sections unless there is a deliberate reason not to.
- Prompt templates have explicit versions.
- Template releases can be correlated with cache-hit-rate changes.
- Provider cache controls are used only through adapter code, not scattered application logic.
- Raw prompt logging is disabled by default or protected by strict retention and access rules.
Rollout plan
1. Observe before changing prompts
Start by collecting provider usage fields and normalized cache metrics for existing traffic. Compute prefix fingerprints for the first N tokens or for gateway-defined prompt regions. The goal is to find high-volume, long-context routes with high prefix churn.
2. Classify workloads
Group traffic into categories: agent sessions, coding assistants, RAG, support automation, document analysis, batch jobs, and short chat. Prompt cache work usually pays most attention to long-context and repeated-prefix workloads. Short prompts below provider thresholds may not benefit.
3. Introduce stable-prefix builders
Move one workload from raw prompt construction to region-based assembly. Keep the rendered provider request semantically equivalent. Do not combine this change with model migration, tool redesign, or major prompt rewrites, or you will not know what caused metric changes.
4. Canary one route
Enable cache controls for a small slice of one tenant or internal app. Compare cache-read rate, prefix churn, time to first token, error rate, and cost categories. Avoid claiming savings until provider bills reconcile with gateway ledgers.
5. Enforce gradually
After the canary, turn lint warnings into policy checks. For example, warn on unstable tool order at first, then reject new template versions that include volatile metadata in the stable prefix.
Trade-offs
- Higher cache hit rate vs. prompt flexibility: stable prefixes improve reuse, but teams may need to move dynamic instructions later or redesign templates.
- Provider-native caching vs. portability: using each provider’s cache controls can improve economics, but thresholds, TTLs, fields, and pricing semantics differ.
- Observability vs. sensitive logging: prompt diffs help debug misses, but hashes and redacted diagnostics are safer defaults.
- Tenant isolation vs. maximum reuse: broad reuse may look attractive, but tenant-scoped behavior is safer and easier to explain.
- Longer retention vs. cost and policy complexity: longer TTL options can help agent sessions, but may introduce different pricing and data-control considerations.
Actionable conclusion
Treat prompt caching as a gateway control-plane problem, not a provider checkbox. The practical pattern is: define stable, semi-stable, and volatile prompt regions; render them deterministically; adapt provider-specific cache controls behind one interface; normalize cache usage into a ledger; expose cache-hit diagnostics by tenant, app, route, and template version; and enforce tenant-scoped assumptions.
The first useful step is not a rewrite. Add cache observability to your longest prompts, identify prefix churn, and lint the templates causing the most misses. Once you can explain cache behavior, you can safely optimize it.