FHIR as It's Actually Deployed: The Map of Every US Hospital Endpoint

Open Epic publishes one CapabilityStatement. Production deploys thousands of shapes of it. The map plots ~3,800 real hospital FHIR endpoints by the shape they actually serve — and lets you build against them before the customer call.

mock.health · 8 min read · 2026-06-18


When you build against Epic, you open Epic's public sandbox, read the one CapabilityStatement it serves, and code to that shape. Then you go live at an actual hospital — Stanford, or Mass General, or a 40-bed critical access hospital in Montana — and the shape is different. The auth flow is different. The pagination is different. Observation.category has an extra coding you've never seen.

The sandbox shows you one server. Production is thousands of servers, each a little bit its own thing. That gap is where go-live week goes to die, and there has never been a good way to see it coming.

So we mapped it. The map is ~3,800 real production FHIR endpoints — every Epic, Oracle Health (Cerner), and MEDITECH deployment we could harvest from the public Brands Bundles — clustered by the actual shape of the CapabilityStatement each one serves. And then we made the shapes runnable, so you can build against the deployment you're about to sell into instead of the demo server everyone tests on.

One CapabilityStatement, many production shapes

Here's the thing the sandbox hides: the HTI-1 rule (§170.404) requires every certified deployment to publish its CapabilityStatement. So the variation is documented, sitting on the public internet, one Brands Bundle at a time. Nobody had bothered to plot it.

When you plot it, the dots cluster. Most Epic sites serve nearly the same CapStmt — that's the modal cluster, the shape you'd guess. But there are 9 distinct Epic shape-clusters across 481 endpoints, including a cluster of stripped-down deployments that advertise zero US Core profiles. Cerner serves 2,810 endpoints across two separate host fronts — provider (fhir-ehr) and patient (fhir-myrecord) — with different scopes and different available resources on each. MEDITECH: 542 endpoints, 6 shapes.

The dots that drift away from the modal cluster are the dangerous ones. Those are the deployments that diverge from what the vendor publishes for developers — the surprises that don't show up until a specific hospital is on the call asking why your app 400s on their patient search.

(We're honestly not certain the cluster count is stable. As we re-harvest the Brands Bundles, shapes split and merge at the edges; the modal clusters are rock-solid but the long tail moves.)

Vendorisms on the wire

The differences that bite are small, specific, and they live in the bytes. The clearest example is Observation.category.

Epic dual-codes it — the standard coding, plus a proprietary OID alongside it:

// Epic — dual-coded with proprietary OID
"category": [{
  "coding": [
    {
      "system": "http://terminology.hl7.org/CodeSystem/observation-category",
      "code": "laboratory"
    },
    {
      "system": "urn:oid:1.2.840.114350.1.13.0.1.7.10.798268.30",
      "code": "Laboratory"
    }
  ]
}]

Cerner and MEDITECH send the same field with a single, standard coding and no OID:

// Cerner / MEDITECH — single coding, standard system
"category": [{
  "coding": [{
    "system": "http://terminology.hl7.org/CodeSystem/observation-category",
    "code": "laboratory"
  }]
}]

If your code matches on coding[0], you might pass against Epic and quietly grab the wrong entry against everyone else — or the reverse. That urn:oid:1.2.840.114350. prefix is a reliable Epic-vs-not signal at the wire; Cerner and MEDITECH never emit one.

The same pattern shows up across the contract you actually have to handle: Epic narrows POST /Patient to a demographic subset and silently drops everything else (the SSN identifier has to use urn:oid:2.16.840.1.113883.4.1, not the URI form, or it vanishes). Epic defaults to application/fhir+xml when you forget the Accept header. Epic's Bundle.total reports the page size, not the total — its first vital-signs page is 1,000 entries. Cerner and MEDITECH return non-FHIR HTTP envelopes on 4xx errors, so your OperationOutcome parser gets a surprise. None of these are in the spec. All of them are in production.

Why this matters more than another sandbox

The companies doing serious interoperability work already know production data is a mess — Flexpa, Zus, and Particle have all written about building proprietary normalization layers because the standard is the floor, not the guarantee. But cleaning up dirty data is downstream of a different problem: you can't even reach the data until your client speaks the specific dialect of the specific server in front of you.

That's the gap the map closes. The overlays aren't a static report — Pro turns them into runtime behavior. You point your client at /v/cluster/epic-cluster-A/fhir/r4/…, /v/cluster/cerner-cluster-A/fhir/r4/…, or /v/cluster/meditech-cluster-A/fhir/r4/… and get the same synthetic patients served three ways, each shaped like the vendor it names. Same person, three EHR personalities, one set of credentials.

So when the hospital asks "does this work against our Epic build," you can answer with a demo configured to the shape they actually run — three months before the contract, not the morning after it goes live. That's the difference between a sandbox and a sales tool.

Go look

Browse the shapes at /map, then walk through discovering a cluster, reading a vendor-shaped Patient, and diffing the same resource across all three EHRs in the how-to guide. The Epic OID is the fastest "aha" — diff it against Cerner and you'll never trust a single sandbox again.


Related posts

All posts · Home · Docs