Skip to content

10 min read

A zero-retention flag covers the provider, not your company

On this page

The deal was worth £340,000 a year and it was blocked on one clause. The prospect's security team wanted a written commitment that their data would not be stored, sales came to engineering, engineering checked the provider's console, found a zero-data-retention setting on the organisation — ZDR, as the consoles and the vendor documents all abbreviate it, an arrangement under which the provider processes a request and keeps no copy of the payload afterwards — and enabled it in about ninety seconds. The account executive then sent the customer a message that read: "Zero retention is now enabled on our account — none of the data you send is stored anywhere."

The same company had shipped a trace pipeline eleven weeks earlier that wrote prompts and completions to a hosted observability platform with a 30-day retention policy. It also maintained an evaluation set of 1,840 cases curated from production failures, in a Git repository, with no expiry at all. Both facts were known to the engineer who ticked the box, and neither occurred to them at the time, because the question they had been asked was about the provider.

That is the shape of the error and it survives even when the sentence is narrowed to the provider. The flag is real and does something specific, and the specific thing is smaller than "nothing is stored".

What sits inside the flag and what sits beside it

Both major providers default to retaining API inputs and outputs for a limited window — 30 days is the common default at the time of writing on the Anthropic and OpenAI platforms — and both offer a zero-retention arrangement for eligible accounts, described in API and data retention on the Claude platform docs and in Data controls in the OpenAI platform. Read either one and the scope statement is careful: it is about the storage of the request and response payloads on the standard inference path. Several things sit outside that, not as loopholes but as separate systems with their own rules.

Transient processing is the least interesting and the most often misread. The payload exists in memory while the model runs, and it may exist in a queue for the duration of a streaming response. Under a zero-retention arrangement it is not written to durable storage, and a customer who reads "not stored" as "never present on their hardware" has misunderstood what inference is.

Abuse monitoring is the one that materially changes the sentence. Providers classify traffic for safety violations, and a request that trips a classifier can be retained beyond the normal window and reviewed by a human, because the alternative is a service with no enforcement. Zero-retention arrangements typically carve this out, and the carve-out is not a formality — if your product accepts free-text from end users, some fraction of it will trip classifiers.

Support access is a mechanism you invoke yourself. When an engineer opens a ticket about a malformed response and pastes a request id, the provider's ability to act on that depends on their having something to look at. Under zero retention they frequently do not, which is a real operational cost of the mode and worth knowing before you promise a customer a 4-hour incident response.

Metadata is retained under every arrangement, because billing exists. Token counts, model identifiers, timestamps, request ids, organisation and API key, latency, and error codes are business records, and business records are kept for years under tax law rather than for 30 days under a retention policy. None of that contains prompt text; all of it is data about the customer's usage, and "none of your data is stored" is false with respect to it.

The surfaces that trip teams up most are the provider features that store by design and are not on the inference path at all:

SurfaceWhat it holdsTypical lifetimeCovered by the ZDR flag?
Standard inference request/responsefull prompt and completion30 days by defaultyes — this is what it turns off
Prompt cachingthe cached prefix, verbatim5 min or 1 hour TTLno — caching is retention with a short clock
Batch APIrequest bodies and resultsretrievable for ~29 daysno — you must not use it, or delete results
Files API / uploadsthe file, verbatimuntil you delete itno — deletion is your call to make
Abuse monitoringflagged requestspolicy-defined, longerusually carved out
Usage and billing metadatatokens, model, timestampsyearsno — business records
Support-attached artefactswhatever you paste into a ticketticket lifetimeno — you put it there

Prompt caching deserves the emphasis. It is the feature you enable for cost reasons, it stores the exact tokens of your system prompt and any cached prefix on the provider's infrastructure for the TTL — the time-to-live, the interval after which the cached copy is discarded — and it is entirely compatible with a zero-retention arrangement because the retention it introduces is short and functional. It is nonetheless storage of customer content, and a customer who asks the direct question deserves the direct answer.

The sentence you can sign

Contract language has to be true under adversarial reading, which means it names the scope rather than gesturing at an outcome. Something like this survives:

For Customer Data submitted to the Service and transmitted to our model provider, we have contracted a zero-data-retention configuration under which prompt and completion content is processed in memory and not persisted by the provider, save where content is flagged by the provider's abuse-detection systems, in which case it may be retained under the provider's trust-and-safety policy for up to 30 days. Request metadata — token counts, model identifiers, timestamps and request identifiers, containing no Customer Data content — is retained by the provider as a business record. Within our own systems, prompt and completion content is retained for 72 hours in a restricted store; derived diagnostic records containing no Customer Data content are retained for 30 days. We do not use Customer Data to train models, and we do not include Customer Data in evaluation datasets without written authorisation.

Four things had to be verified before that paragraph was signable. The provider's zero-retention arrangement had to be confirmed in writing and scoped in the contract rather than inferred from a console toggle, because a documentation page is not a commitment to you specifically. The abuse-monitoring carve-out had to be read and quoted accurately, since the number in it is the provider's, not yours. Every provider feature that stores by design had to be checked against actual usage — the team found they were calling the Files API for document extraction and had 6,400 uploaded files sitting in the account, none of which had ever been deleted. And the last sentence, about evaluation datasets, required someone to go and look at the evaluation set, which is where the 1,840 curated cases surfaced.

Two structural points about that paragraph are easy to miss. The first is that a commitment made to your customer must be no broader than the commitment made to you, and vendor documentation is not a commitment made to you — a published data-retention page can change, and it changes without your signature. What binds is the executed agreement, ideally with a notice period on material changes to data handling, because your contract with the customer has a term measured in years and the vendor's policy page has a term measured in whenever they edit it. The second is that the paragraph binds the whole company rather than one integration. If a second team ships a feature next quarter that calls a different model provider on a standard account, the sentence you signed is now false and nobody in that team has read it.

Notice which clause covers the largest risk. The provider's behaviour is the part governed by someone else's contract, and it is the part most likely to be correct. Your own 72-hour window, your 30-day derived records, and your evaluation practice are the parts you control, the parts nobody audited, and the parts the commitment now binds you to.

Testing the flag rather than trusting it

A configuration setting is a claim about behaviour, and behaviour is testable. The cheapest useful test is a canary: send a request containing a string that exists nowhere else in the world, then attempt to retrieve it through every surface the provider exposes.

python
import uuid, datetime, anthropic

client = anthropic.Anthropic()
canary = f"ZDR-CANARY-{uuid.uuid4().hex}"

resp = client.messages.create(
    model="claude-opus-5",
    max_tokens=64,
    messages=[{"role": "user", "content": f"Repeat this token exactly: {canary}"}],
)
print(resp.id, canary, datetime.datetime.utcnow().isoformat())

# Then, on a schedule, assert the canary is not retrievable:
#   1. console / logs export for the organisation
#   2. list Files API objects and grep their contents
#   3. list Batch API jobs and fetch any results
#   4. open a support ticket quoting only resp.id and ask what they can see
#   5. re-send the identical prefix and check usage.cache_read_input_tokens

Step five is the one people skip and the one that most often returns a surprising answer. If the second identical request reports a non-zero cache_read_input_tokens, the prefix was held somewhere for the cache TTL, which is a fact your contract paragraph now has to accommodate. Step four is uncomfortable to run and worth running once, because the answer tells you what your support path will actually look like during an incident.

The other half of verification is continuous rather than one-off. Six months after signing, the customer's auditor asks whether the mode has been on the whole time, and the honest answers range from strong to embarrassing depending on what you set up on day one. A console screenshot taken today proves the state today. What proves the interval is some combination of three things: the setting managed as code in a repository whose history is signed, so the commit log is the change record; a scheduled assertion that reads the organisation configuration through the provider's API and writes the result to an append-only log; and the provider's own audit log, which records administrative changes to the account with actor and timestamp.

yaml
# .github/workflows/zdr-assert.yml — runs hourly, fails loudly
- name: Assert zero-retention configuration
  run: |
    STATE=$(curl -sS https://api.anthropic.com/v1/organizations/settings \
      -H "x-api-key: $ADMIN_KEY" | jq -r '.data_retention.mode')
    echo "$(date -u +%FT%TZ) zdr_mode=$STATE" >> evidence/zdr.log
    test "$STATE" = "zero_retention"

An hourly assertion appended to a log held in object storage with versioning and a retention lock, which is the bucket setting that refuses any deletion or overwrite of an object until a fixed date has passed, produces something an auditor can accept: 4,344 consecutive observations over six months, each timestamped, in a store you cannot rewrite. That is a different class of artefact from a screenshot, and it costs an afternoon to build if you build it at the start and is close to unrecoverable if you did not.

What the mode costs to run

Zero retention is usually presented as free, and it is not, which matters because the costs are the reason people quietly turn it off. Provider-side debugging goes away first: when a response comes back malformed and you open a ticket, there is nothing on their side to inspect, so the burden of reproducing the problem falls entirely on your own trace store — which, if you followed the first chapter, no longer contains the prompt text after 72 hours. Those two decisions interact, and the sensible response is to keep a reproduction path that does not depend on either, usually a manually captured request from a consenting internal user.

Feature availability is the second cost. Arrangements differ by provider and by account, and some surfaces are simply unavailable under zero retention because they exist by storing things — asynchronous batch processing being the clearest case, since a batch job is a promise to hold your requests until they are processed. Teams that adopted the batch path for a cost reduction and then took a zero-retention commitment for a customer have to decide which one they are keeping, and discovering that during contract negotiation rather than during the migration is worth the hour it takes to check.

The third cost is organisational. A zero-retention account is an account-wide setting, so it applies to every workload on that account, including internal tools and experiments where the constraint buys nothing and the debugging loss is felt daily. The usual resolution is separate accounts or projects with different postures and a routing rule that sends customer traffic to the constrained one — which is a piece of architecture, with its own configuration to verify, rather than a checkbox.

Where the flag stops mattering

Zero retention constrains one vendor's storage of one payload on one path. It says nothing about the guardrail service you call before the model, the gateway that sits in front of both, the observability platform receiving your spans, or the crash reporter that captured a request body when a handler threw. Each of those has its own retention posture, and none of them is covered by a setting in the model provider's console.

It also does not reduce the number of places the payload goes. The commitment paragraph above is a statement about six or seven systems, and writing it required someone to know which systems those were. Most teams asked to list what leaves their network on a single assistant request produce a list that is short by roughly an order of magnitude, and the missing entries are the ones that end up in the contract by omission.

Data retention and privacy in logs — read the current version at https://daviddominguez.dev/learn/en/data-retention-and-privacy-in-logs/a-zero-retention-flag-covers-the-provider-not-your-company

Back to top