.png&w=3840&q=75)
Your Router Keeps “Helping” by Sending Sensitive Prompts to the Wrong Provider
top your LLM router from failing over to a provider that stores your prompts. Learn how FastRouter’s :zdr suffix routes only to ZDR providers.

.png&w=3840&q=75)
Shipping an LLM feature into a real product tends to follow a familiar arc: quality and latency finally get good enough, and then Legal or Security asks whether the model provider can store prompts or completions. The answer is often "no." The routing layer underneath, meanwhile, may have a different idea: it might fail over to a different provider when the primary one gets flaky.
That fallback is the footgun. In production, "fallback" often means the same model slug served by a different underlying provider, and different providers carry different retention terms and settings. For regulated or contractually sensitive data (PHI, financial information, legal documents, unreleased source code, or similar material), a silent fallback isn't resilience. It's a compliance incident that goes unnoticed until an audit.
FastRouter's :zdr suffix addresses this by making the router fail closed: it routes only to providers FastRouter treats as Zero Data Retention for that request, errors out if no such provider is available, and never silently downgrades to a provider that stores data. That changes how a system gets designed, for the better.
TL;DR
- Add :zdr to a model slug (example: deepseek/deepseek-v4.1-flash:zdr) to restrict routing to ZDR providers.
- If no ZDR provider can serve the model, the request fails. There's no silent fallback, and that's the entire point.
- :zdr only constrains which model provider receives the request. It doesn't prevent retention through plugins, web search (:online), preserved state, or response caching.
- For end-to-end retention control, pair :zdr with an API key that has content logging disabled, and keep retention-prone features off the sensitive traffic path.
What :zdr actually does in the routing pipeline
FastRouter model IDs can be served by multiple providers behind the scenes. Without constraints, the router picks among them based on availability and routing preferences. Appending :zdr changes the candidate set:
- Resolve <provider>/<model> to the providers that can serve this model.
- Filter that set down to providers treated as Zero Data Retention.
- Apply normal routing behavior (for example, provider.sort) within that filtered set.
- Return an error if the filtered set is empty.
The model ID format is:
<provider>/<model>:zdr
For example, deepseek/deepseek-v4.1-flash becomes deepseek/deepseek-v4.1-flash:zdr. Requests and responses stay in the standard Chat Completions shape; there's no separate endpoint to opt into, only a constraint added to an existing one.
This is the right layer to enforce a privacy constraint at: in the router, where fallback actually happens, not in application code and not in a policy document.
Supported ZDR providers
FastRouter currently treats the following as ZDR providers: Fireworks, Baseten, Groq, Perplexity, DeepInfra, and Together. A model can only be used with :zdr if at least one of these providers serves it; if none do, the request returns a hard error rather than a best-effort attempt. Best-effort ZDR would be worse than no ZDR at all, since it creates false confidence.
The tradeoff: policy correctness over maximum uptime
:zdr shrinks the provider pool for a given request, and that isn't free.
What it buys: sensitive prompts stop accidentally routing to an unapproved provider, and failure becomes deterministic when the constraint can't be met.
What it costs: some availability, since non-compliant failover is refused, and sometimes fewer cost or latency options, since only the ZDR subset is available to choose from.
For sensitive traffic, the hard failure is the better trade every time. A system that needs both 99.99% uptime and strict retention constraints needs queuing, delayed processing, or human fallback built in regardless, not a router that quietly retries somewhere else.
Example requests
These use FastRouter's OpenAI-compatible endpoint (base URL , Chat Completions at /chat/completions):
1curl --location 'https://api.fastrouter.ai/api/v1/chat/completions' \2 --header "Authorization: Bearer $FASTROUTER_API_KEY" \3 --header 'Content-Type: application/json' \4 --data '{5 "model": "deepseek/deepseek-v4.1-flash:zdr",6 "stream": false,7 "max_completion_tokens": 200,8 "messages": [9 { "role": "user", "content": "Summarize the key obligations in section 4.2." }10 ]11 }'
Python, using the OpenAI SDK pointed at FastRouter:
1import os2from openai import OpenAI34client = OpenAI(5 api_key=os.environ["FASTROUTER_API_KEY"],6 base_url="https://api.fastrouter.ai/api/v1",7)89resp = client.chat.completions.create(10 model="deepseek/deepseek-v4.1-flash:zdr",11 max_completion_tokens=200,12 messages=[13 {"role": "user", "content": "Summarize the key obligations in section 4.2."}14 ],15)1617print(resp.choices[0].message.content)
TypeScript, same pattern:
1import OpenAI from "openai";23const client = new OpenAI({4 apiKey: process.env.FASTROUTER_API_KEY!,5 baseURL: "https://api.fastrouter.ai/api/v1",6});78const resp = await client.chat.completions.create({9 model: "deepseek/deepseek-v4.1-flash:zdr",10 max_completion_tokens: 200,11 messages: [{ role: "user", content: "Summarize the key obligations in section 4.2." }],12});1314console.log(resp.choices[0].message?.content);
The response's usage.provider field reports which provider served the request; on a :zdr request, it will always be one of the six providers listed above. Teams planning to assert on this programmatically should note that an SDK's response typings may not surface the field even when it's present in the raw JSON, so inspecting the raw response object directly may be necessary.
What :zdr does not cover
:zdr controls provider selection only. It doesn't disable FastRouter features, or application features, that inherently store or forward content. Four categories can still result in stored data even when the model provider itself is ZDR-compliant:
- Plugins such as PDF processing — the plugin has to read and process the prompt or file content to do its job.
- Web search tool / :online — the query is sent to a search service outside the ZDR provider boundary.
- Responses API with preserved state — keeping state across requests means storing earlier prompts and responses somewhere; there's no state without storage.
- FastRouter response caching — caching works by storing prompts and responses so matching future requests can be served from cache.
A common real-world version of this: a team turns on response caching on a summarization endpoint to cut cost. It works well and the endpoint is still technically served by a ZDR provider, but raw contract text is now sitting in a cache layer that was never part of the original retention review. The risk didn't disappear, it moved from the provider to the middleware. If the goal is that sensitive prompts are not stored anywhere, these four categories need to be disallowed on that request path, not just handled carefully.
The minimal viable safe setup
The simplest setup that holds up under a production review is a three-part constraint:
- Use :zdr on sensitive calls — prevents provider fallback from violating retention requirements.
- Use an API key with content logging disabled — turns off FastRouter's own prompt and response logging for that key.
- Keep retention-prone features off that traffic — no plugins, no :online, no preserved state, no response caching.
Anything short of that baseline is a gap that eventually gets found.
Failure modes worth designing for
Hard errors when ZDR coverage isn't there. If no ZDR-compliant provider can serve the model, FastRouter returns an error instead of falling back, by design. The application needs a plan for "this strict privacy request can't be served right now": queue and retry later, degrade to a human workflow, or return a user-visible message explaining the privacy-driven delay. What doesn't work is catching the error and retrying without :zdr, which silently violates the same policy the system was built to enforce.
Empty intersections with provider filters. Combining :zdr with provider filters that exclude every ZDR provider makes every request fail. The exact error looks like this:
{
"error": "deepseek/deepseek-v4.1-flash is not a valid model ID or no
providers found. No provider for this model supports Zero Data
Retention (:zdr)"
}
This happens when the model isn't served by any ZDR provider, when provider.only or another routing filter excludes every ZDR provider that could serve it, or when the model ID itself is invalid. The fix is to remove the conflicting filter, choose a model with ZDR coverage, or correct the slug.
Retention-prone features getting re-enabled later. This is the quiet failure mode: no errors, no broken tests, just data quietly starting to get stored again once someone turns on search, plugins, state, or caching on a path that was supposed to stay clean. The mitigation that actually works is separation: dedicated API keys, dedicated client wrappers, and code paths that structurally refuse to enable the risky features, rather than relying on engineers remembering not to.
Structuring this in a real codebase
For a system with both normal and sensitive traffic, treating them as two different clients rather than one client with a flag tends to hold up better over time.
A normal LLM client can use caching, web search, plugins, and aggressive failover freely.
A sensitive LLM client should be deliberately boring: it always uses :zdr, it uses a key with content logging disabled, it rejects attempts to turn on plugins, search, or caching, and it fails closed rather than open.
That separation makes the sensitive path independently reviewable and auditable, and means a small refactor elsewhere in the codebase can't quietly reintroduce retention on traffic that was never supposed to carry it.
A practical starting checklist
- Pick one real sensitive endpoint (contract summarization, ticket triage handling PII, health-note extraction) rather than trying to make everything ZDR at once.
- Create a dedicated FastRouter API key for that endpoint and disable content logging on it.
- Append :zdr to the model slug that endpoint uses.
- Audit that endpoint for retention footguns and turn them off there: plugins, web search / :online, preserved state, response caching.
- Add a test that enforces the invariant, checking that the model slug includes :zdr and that the code path uses the sensitive key. Cheap test, high value.
- Decide the failure behavior for ZDR routing errors ahead of time: queue and retry, human fallback, or an explicit user-facing message, implemented before production forces the decision.
The single highest-leverage move here is adding :zdr on sensitive traffic and accepting the hard failures that come with it. Silent fallback is how teams end up writing a postmortem that starts with "the router wasn't supposed to do that."
Getting started
Full details on :zdr, including how it interacts with provider routing preferences, are documented at docs.fastrouter.ai/explore-features/zero-data-retention.
Related Articles
.png&w=3840&q=75)
.png&w=3840&q=75)
Add an MCP Server in Two Inputs, Not Seven
Add an MCP server in two inputs instead of seven. Templates handle GitHub, Slack, and Jira setup while keeping full credential vaulting and audit logs

.png&w=3840&q=75)
.png&w=3840&q=75)
Building Safer AI Applications: A Practical Guide to Guardrails in FastRouter.ai
When you deploy an AI-powered application, you're not just shipping a feature—you're establishing trust.



Is an OpenAI-compatible API the same as using OpenAI directly?
OpenAI compatible API vs OpenAI: the interface is not the service. Compare routing, model behavior, governance, and migration checks before switching.