cd /news/developer-tools/openai-responses-api-user-migration-… · home topics developer-tools article
[ARTICLE · art-115873] src=dev.to ↗ pub= topic=developer-tools verified=true sentiment=· neutral

OpenAI Responses API `user` Migration: Split Safety from Prompt Caching

OpenAI's Responses API migration splits the deprecated `user` field into `safety_identifier` and `prompt_cache_key`, separating end-user safety attribution from prompt-cache routing. A developer demonstrates a .NET implementation that uses HMAC-SHA-256 with a secret pepper for the safety identifier and a structured cache key, ensuring privacy and cache efficiency.

read4 min views4 publishedAug 30, 2026

The OpenAI Responses API user

migration is easy to misread as a one-field rename. It is actually a split. The deprecated field mixed end-user safety attribution with prompt-cache routing, while the current request contract provides safety_identifier

and prompt_cache_key

for those separate jobs.

I would rather make that distinction explicit in one request builder than scatter it across call sites. The result is easier to review, keeps raw identity out of the payload, and can be verified without sending a model request.

user

migration is a split The Responses API create reference marks user

as deprecated and says it is being replaced by both fields. It describes safety_identifier

as a stable end-user identifier used to help detect policy abuse, with a maximum length of 64 characters. It describes prompt_cache_key

as a key that helps route requests with similar reusable prefixes.

Those are different lifecycles.

A safety identifier should remain stable for one account across prompts. In this scheme, I change the cache key when the reusable prompt contract changes, and I can share it across users whose requests have the same prefix. Blindly copying the old value into both fields misses the chance to separate the policies; it may still be valid when the old value is privacy-preserving and per-user cache grouping is intentional.

I model the split with two inputs:

That makes a review question concrete: does this value identify a user, or does it identify reusable prompt structure?

OpenAI's safety guidance recommends hashing a username or email instead of sending identifying information. In production I prefer an opaque internal account ID when one exists. I also use HMAC-SHA-256 with a secret pepper, rather than an unkeyed hash, so a copied digest is less useful for guessing common identifiers.

HMAC is an application choice here, not an API requirement. The pepper belongs in a secret manager and should be at least 32 random bytes. The committed sample uses an obvious fixed fixture solely to keep its output reproducible.

The derived value remains a stable, linkable pseudonym—not anonymous—and its privacy depends on protecting the pepper.

var subjectBytes = Encoding.UTF8.GetBytes(subject);
var safetyIdentifier = Convert.ToHexString(
    HMACSHA256.HashData(privacyPepper, subjectBytes))
    .ToLowerInvariant();

var promptCacheKey =
    $"{cacheGroup.Length}_{cacheGroup}_{cacheVersion.Length}_{cacheVersion}";

if (promptCacheKey.Length > 64)
    throw new ArgumentException("Prompt cache key is too long.");

var request = new
{
    model = "your-model",
    input,
    safety_identifier = safetyIdentifier,
    prompt_cache_key = promptCacheKey
};

The lowercase hexadecimal digest is exactly 64 characters, which fits the documented safety-identifier maximum. The sample also caps the cache key at 64 characters and restricts its components to a conservative ASCII subset. Length prefixes keep pairs such as a-b

plus c

distinct from a

plus b-c

.

The cache key says nothing about the person. In the sample, 12_support-flow_2_v3

identifies one reusable prompt contract; changing the prompt contract to v4

changes the key.

That separation also makes rotation decisions visible. Rotating the HMAC pepper changes safety identifiers, so a production rollout may need a deliberate overlap plan. In this sample's policy, a deliberate prompt-contract revision advances the cache version even when the identity policy stays unchanged.

The runnable sample on main uses only the .NET 10 shared framework. It builds JSON locally and performs fourteen checks, including these invariants:

user is absent
safety_identifier is stable for the same subject
different subjects produce different identifiers
raw identity is absent from JSON
prompt_cache_key is shared across matching prompt contracts
a prompt-version change produces a different cache key

It also rejects a short pepper before a transport boundary and serializes the same request twice to prove the bytes are identical. The executable makes no OpenAI request and requires no runtime network connection; restore and vulnerability-audit commands may contact configured NuGet sources.

This is useful because a successful HTTP response would not prove the migration is correct. Both replacement fields are optional. A request can be accepted while omitting the per-user safety signal, the application-supplied cache-routing key, or both. A local contract test catches the omission where the request is assembled.

The prompt-caching guide adds one important boundary: prompt_cache_key

influences routing, but it does not pin traffic to a machine or guarantee a cache hit. Prefix content still has to match, and cache eligibility depends on the model and prompt shape. I treat the key as a routing hint, not a response cache or correctness mechanism.

This sample does not prove that OpenAI accepts a chosen model, that a cache read occurs, or that any safety action will result. Those belong to integration tests and production telemetry. It also does not prescribe one universal cache-key scheme; high-volume applications should tune grouping against real prefix reuse and overflow behavior.

For anonymous previews, OpenAI's guidance allows a session ID as the safety identifier. For trusted internal batch jobs with no individual end user, forcing a fictional per-user identity would be misleading. I would document that boundary instead of inventing one.

The practical migration rule is small: remove user

, derive safety_identifier

from a stable privacy-preserving identity policy, derive prompt_cache_key

from a reusable prompt policy, and test both independently.

What deprecated request field are you turning into an explicit contract test next?

Happy coding!

── more in #developer-tools 4 stories · sorted by recency
── more on @openai 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/openai-responses-api…] indexed:0 read:4min 2026-08-30 ·