How Does Epic FHIR Integration Actually Work? A Step-by-Step Guide
Epic is the FHIR integration most healthcare software teams eventually need to build. Here's the practical, step-by-step version of how it actually works — from choosing an auth model to handling Epic's specific quirks.
Step 1: Choose the right auth model for your use case
Epic supports both interactive SMART App Launch (a patient or provider logs in and authorizes your app) and SMART Backend Services (a system-to-system integration with no interactive user, authenticated via a signed JWT and registered public key). Get this decision right first — it determines almost everything downstream, including which scopes are available and how patient context is established.
As a rule of thumb: if a person is using your app in the moment (a patient-facing portal, a provider-facing app launched from inside Epic), you need interactive SMART App Launch. If your integration runs on a schedule or in response to a backend event with no user present — the pattern behind our own Prior Authorization AI Agent demo — you need Backend Services.
Step 2: Register in Epic's sandbox before anything else
Epic provides a public, shared open FHIR R4 sandbox (fhir.epic.com) with a fixed set of synthetic test patients, built specifically for connectivity testing. Register your app there first, regardless of your production timeline — validating your auth flow and a handful of real API calls against the sandbox surfaces most integration issues far more cheaply than discovering them during a production app review.
Registration requires generating a key pair, providing your public key (directly or via a hosted JWKS URL for Backend Services apps), and declaring the FHIR resource scopes your app needs. Under-scoping is a common early mistake — request every resource type your integration will eventually touch, since adding scopes later means going back through app configuration again.
Step 3: Know Epic's sandbox-specific quirks before you assume something's broken
The sandbox's synthetic patients are built for connectivity testing, not realistic demo scenarios — a given test patient may have some resource types populated (say, Condition and MedicationRequest) and not others (ServiceRequest, Coverage). Before concluding your integration is broken because a query returned nothing, check Epic's own sandbox test data documentation for which resources actually apply to the patient you're querying.
Backend Services apps cannot reliably use demographic Patient.Search (searching by name) — see our companion piece on that specific finding. Design your patient-resolution step around a known FHIR ID from the start, rather than discovering this limitation mid-project.
Pagination matters even in the sandbox: don't assume a Bundle's first page is the complete result set for larger resource types. And watch for OperationOutcome resources appearing in place of expected data on a 'no results' search — handle that shape explicitly rather than assuming every Bundle entry is the resource type you asked for.
Step 4: Build the real integration, not just the demo call
A working `curl` against the sandbox is a good first milestone, not a finished integration. Production-grade Epic integration needs token refresh handling, retry/backoff for transient failures, structured logging of every FHIR request (useful both for debugging and for the audit trail a healthcare integration should have anyway), and defensive parsing that doesn't assume every response is shaped exactly like the happy-path example in Epic's documentation.
What this looks like in practice
We've built this pattern for a real mobile phlebotomy platform's Epic integration (see our FHIR integration case study), and again for a live public demo — a multi-agent Prior Authorization system that authenticates against Epic's sandbox using SMART Backend Services, resolves patients by known FHIR ID, and pulls real clinical context to drive order detection. It's a useful reference if you want to see the auth model and resolution pattern described here actually running against Epic, rather than just described in the abstract.
FAQ