Skip to content

14 min read

Regional endpoints and data residency

On this page

Data residency is a commitment about where content is processed and stored, and it normally arrives as a clause in a data processing agreement — the DPA, the contract annexe that fixes what a supplier may do with data it handles on a customer's behalf. A typical clause says that a customer's content is processed only within the European Union. The engineering reading of that clause tends to stop at the hostname: a service calling bedrock-runtime.eu-central-1.amazonaws.com is calling Frankfurt, and Frankfurt is in the EU.

The hostname is not ambiguous about what it names. It names the regional service endpoint a request is sent to, and requests sent there are authenticated, metered and — under normal conditions — inferred on accelerators in that region. Every word of that is doing work. What happens to a request after it produces a response, what happens when the region is short of capacity, and who can read the payload later are governed by platform behaviours and contract terms that the hostname has no relationship with. This chapter enumerates those surfaces, in the order of how easily each can be closed.

The hostname settles routing, and routing is not the whole lifecycle

Start with what a hostname does mechanically, because the distance between that and what people read into it is the whole of this chapter. A hostname is a name resolved to an address, and the address belongs to a front door the platform operates in that region. Sending a request there means the connection terminates there, and that the request is authenticated, authorised and metered there. What happens next is a scheduling decision taken inside the platform: the front door hands the work to some pool of accelerators, and which pool that is depends on capacity, on where the model is deployed, and on the routing scope the request asked for. Neither DNS, nor TLS, nor HTTP has any vocabulary for constraining that second hop. The hostname binds the first hop and cannot express a statement about the second.

That is why the exposure is invisible rather than merely undocumented, and the distinction matters when somebody proposes to catch it in code review. There is no field in the response naming the second hop because the client never addressed the second hop; it addressed a front door, and the front door answered. A response is a reply from the party you contacted, so it can only carry what that party chooses to tell you about what it did on your behalf.

Follow one request through and count the places its content exists.

Flowchart: Client in Frankfurt → Regional endpoint eu-central-1; Regional endpoint eu-central-1 → Regional capacity available?; Regional capacity available? → Inference in eu-central-1 (Yes); Regional capacity available? → Inference in another region (No, cross-region profile); Inference in eu-central-1 → Abuse-monitoring store retained up to 30 days; Inference in another region → Abuse-monitoring store retained up to 30 days; Inference in eu-central-1 → Response to client; Inference in another region → Response to client; Abuse-monitoring store retained up to 30 days → Support engineer access during a case; Abuse-monitoring store retained up to 30 days → Safety classifier subprocessor; Response to client → Your own logs and traces; Your own logs and traces → Observability vendor, us-east-1.

Only one box in that diagram is constrained by the endpoint called, and two of the leaves are outside the platform entirely. The response a client receives contains no evidence about any of the others, which is the property that makes this hard to catch in review: there is no field to assert on and no error to log.

Failover is a feature somebody may have enabled without reading it

The sharpest version of this is a configuration change that looks like an availability improvement. On AWS, anthropic.claude-sonnet-4-5-20250929-v1:0 is a region-bound model id, while eu.anthropic.claude-sonnet-4-5-20250929-v1:0 is a cross-region inference profile that may route the request to any region in the EU geography, and a global. prefix routes to any region the platform operates in. The three strings differ by a prefix, all three work against the Frankfurt endpoint, and the prefixed ones exist because they materially improve availability and throughput during regional capacity crunches.

That is a real benefit and teams adopt it for good reasons. A capacity event in Frankfurt in which 3.2% of requests are served from Dublin and Stockholm is an event users did not notice, and it is also 3.2% of requests processed outside the specific member state some contracts name. The EU-geography profile keeps data inside the Union, which satisfies most EU-only clauses; the global profile does not, and nothing in the request or response distinguishes them at runtime.

Azure has the same shape under different names. A deployment created as Global Standard gives higher throughput by routing to capacity anywhere Microsoft has it, DataZone Standard bounds routing to a geography such as the EU, and Standard is region-bound. Those three names are deployment SKUs, the platform's product tiers for a deployment, and the choice between them is made once at creation time, usually by whoever was fixing a throughput complaint.

Both platforms arrange it the same way for the same reason, and that reason is why the setting gets widened by accident rather than by argument. A routing scope is an availability control. Widening it is how a platform makes a model more available when a region is short of accelerators, and the person widening it is answering a throughput complaint with the documented remedy for a throughput complaint. Nothing in that workflow surfaces geography, because geography is not what the control is for and not what its name describes. The residency consequence is a side effect of an availability decision, taken in a change whose title is about latency, by somebody who was correct about the problem they were solving. This is worth holding on to, because it decides where the control has to go: a review of the change would not have caught it, and a test that reads back the running model id would.

json
{
  "region_endpoint": "bedrock-runtime.eu-central-1.amazonaws.com",
  "model_id": "eu.anthropic.claude-sonnet-4-5-20250929-v1:0",
  "_comment": "The 'eu.' prefix is an inference profile, not a region tag.",
  "_routing_scope": ["eu-central-1", "eu-west-1", "eu-west-3", "eu-north-1"],
  "_would_be_global_if": "global.anthropic.claude-sonnet-4-5-20250929-v1:0"
}

Checking this takes one command against a running configuration and it belongs in a compliance test rather than in a memory. The routing scope of a profile is published by the platform and changes when the platform adds regions to a geography, which is a change you inherit without a deploy.

What triggers the routing determines how often the exposure occurs and whether it can be predicted. The platform routes away from the home region when that region's capacity for the model is under pressure, which is a function of every tenant's demand rather than yours, so the fraction of your traffic served elsewhere is uncorrelated with anything you control and can go from zero for six weeks to double digits during a launch week for a popular model. Most platforms return no field naming the serving region, the latency difference between Frankfurt and Dublin is a few milliseconds and invisible in your percentiles, and the invoice aggregates across the profile. A team can run a cross-region profile for a year and have no internal evidence that a single request ever left the home region, in either direction.

Each of the three signals somebody would reach for fails for its own reason, and having all three in mind is what stops a reviewer accepting "we would have noticed". Latency fails because the regions inside a geography sit a few milliseconds apart while a response's duration is dominated by generation time, so the difference is practically invisible in any percentile anybody plots. Cost fails because the invoice aggregates across the profile, which is not an oversight — aggregation across a routing scope is what a profile is. And the response fails because it carries no region field for the reasons above. Three independent channels, none of which carries the fact, is a different situation from one channel nobody checked, and it is the reason the answer to legal cannot be assembled from anything the service already collects.

Retention for abuse monitoring outlives the request

Cloud platforms retain prompts and completions for abuse detection, commonly for up to thirty days, in a store the customer cannot query and that is operated by the platform's trust and safety function rather than by the inference service. Where that store lives is a separate question from where inference ran, and it has historically been co-located with the geography on some platforms and centralised on others.

Two things follow. The first is that "EU-only processing" as a contractual claim has to cover thirty days of retained copies, not just the milliseconds of inference. The second is that abuse monitoring is frequently something a customer can be exempted from — Microsoft's modified abuse monitoring and limited access programme, and equivalent arrangements elsewhere, remove or localise the retention — but the exemption is an application with eligibility criteria and a lead time of weeks. If the DPA is already signed, that lead time is a schedule problem somebody has inherited.

Retention has a second-order consequence that surfaces whenever a customer exercises a deletion right. Your own deletion path can remove the request, the response and every derived record you hold, and the platform's abuse-monitoring copy is outside it: not queryable, not deletable on request, and expiring on the platform's own schedule. That is defensible under most agreements, since the retention is the processor's own disclosed purpose, but it has to be disclosed in your privacy documentation rather than discovered during an audit. The engineering contribution is knowing the retention window and where it is documented, so that the thirty days appears in the answer instead of an assumption that deletion is complete when your database says it is.

Support access and subprocessors are contract surfaces, not API surfaces

When a support case is opened against an inference problem, the engineers who work it may need access to request content, and those engineers are wherever the platform staffs support. Several platforms sell a control for exactly this, generally called a lockbox: an approval workflow where a customer administrator must grant each access request, scoped and time-boxed. Where such a control exists it is off unless someone turned it on.

The subprocessor question is broader and is the one engineers most often forget, because it is answered in a document rather than in an API. A subprocessor is a third party that the platform — your processor, in the language of the regulation — hands data to in order to deliver part of its own service, and it inherits the obligations your customer imposed on you. The platform is a processor; the model vendor may be a subprocessor of the platform; the safety classifier may be a further one; and each of them has a location and a transfer mechanism. A customer's DPA either lists these or incorporates the platform's published subprocessor list by reference, and that list changes with notice periods measured in weeks.

SurfaceBounded by the endpoint?Who controls itWhat evidence exists
Inference executionYes, under normal capacityYou, via model id and deployment typePlatform trace records
Cross-region routingNoYou, via inference profile / deployment SKUPlatform trace records, per request
Abuse-monitoring retentionNoThe platform, subject to exemption programmesContract terms and programme approval
Support engineer accessNoThe platform, unless a lockbox control is enabledAccess approval logs, if the control is on
SubprocessorsNoThe platformPublished subprocessor list, DPA annexes
Your logs and tracesNoYou, entirelyYour own configuration

Your own telemetry is often the larger leak

The last row is the one that can be fixed this afternoon and the one most likely to be wrong. A service that scrupulously calls a Frankfurt endpoint and then attaches the full prompt to an error-tracking event, shipped to a vendor's US ingestion endpoint, has exported the payload more reliably than any platform failover would. The same applies to distributed traces carrying prompt attributes, to structured logs with the request body at debug level, to a shared evaluation set built by sampling production requests, and to a prompt-management tool that stores the last few completions for debugging.

python
# Ships the prompt to whichever region the observability vendor ingests in.
span.set_attribute("llm.prompt", messages[0]["content"])       # exports content
span.set_attribute("llm.completion", resp.content[0].text)     # exports content

# Keeps the diagnostic value without exporting the payload.
span.set_attribute("llm.prompt_sha256", sha256_of(messages))   # correlatable
span.set_attribute("llm.input_tokens", resp.usage.input_tokens)
span.set_attribute("llm.request_id", resp.request_id)          # the audit key

A prompt hash is a genuine substitute for most of what prompt logging is used for. It groups identical inputs, confirms that a reported bad answer came from the prompt version you think it did, and joins a support ticket to a request, without moving content.

Why it can do those three jobs is worth a paragraph, because the same property explains the fourth job it cannot do. A cryptographic digest is a deterministic function of its input: identical bytes always produce an identical digest, and any change at all to the bytes produces a completely different one. That gives equality testing for free — two requests whose digests match had byte-identical prompts — and grouping, version confirmation and ticket-joining all reduce to equality testing. What it does not give is the input back. No operation recovers the text from the digest, which is simultaneously the property that makes the digest safe to export and the property that makes it useless for the thing somebody will want at three in the morning, which is reading the prompt.

Where the full text is genuinely needed for debugging, the workable pattern therefore is to store it in your own in-region store keyed by request id and send only the key to the vendor. The third line of that second block is what the next chapter depends on, so it is worth writing now even if nobody has asked for it.

Residency rests on safeguards, not on geography alone

The frequent error in the other direction is to treat any byte crossing a border as a breach. Under the General Data Protection Regulation, the GDPR, transfers outside the European Economic Area — the EU plus Iceland, Liechtenstein and Norway — are lawful where they rest on an adequacy decision, meaning the European Commission has ruled that the destination country protects personal data to an equivalent standard, or on appropriate safeguards such as the standard contractual clauses the major platforms provide. What a customer's EU-only clause does is contract for something stricter than the regulation requires, usually because that customer's own regulator or their own customers asked for it. The engineering question is therefore not "did any packet leave the EU" in the abstract; it is "does the actual data flow match what this specific agreement says", and the agreement is the authority.

That distinction determines who can change the answer. If a flow is unlawful, engineering must stop it. If a flow is lawful but outside a commitment the company made to one customer, the options include changing the flow, changing the commitment, or getting that customer's written agreement to the exception — and the last two are commercial decisions.

Where this is heading is worth knowing even though it does not help today. Every large platform now sells a bounded variant — EU Data Boundary, sovereign and geography-scoped deployment SKUs, region-only inference profiles, customer-managed keys and access-approval controls — because enough regulated customers asked. These variants generally cost more, offer fewer models, receive new models later, and have lower per-region capacity, so choosing one trades against exactly the availability and throughput a cross-region profile was providing.

That trade is the conflict this leaves on the table. Cross-region routing is what keeps a service inside its availability SLO through regional capacity events, and turning it off to satisfy one customer's clause reduces availability for every customer, including the ones whose contracts have penalties attached to uptime rather than to geography. Both commitments were made by the same company, both are real, and the resolution is a decision about which promise to keep rather than a technical problem with a correct answer. Naming the gaps is half of what legal needs; producing per-request evidence is the other half, and nothing described here produces it.

Cloud model platforms — read the current version at https://daviddominguez.dev/learn/en/cloud-model-platforms/regional-endpoints-and-data-residency

Back to top