Learn

How to build a SMART on FHIR app for Epic

Definition: A SMART on FHIR app for Epic is a web app that Epic launches with an OAuth 2.0 authorization code flow, then reads and writes FHIR data within the scopes the hospital approved.

Our founder put it bluntly on an internal call: once you know it is OAuth 2 plus a REST API, the build is pretty easy, and the hard part is implementation with a customer. This guide covers the build in the order you will do it, then what you still need before a hospital turns your app on.

1. Choose EHR launch or standalone launch

Pick your launch type first, because it changes the handshake. In an EHR launch, a clinician opens your app from inside Epic, and Epic passes iss and a launch token so you inherit the current patient and user. In a standalone launch, the user opens your app directly and picks context during authorization.

Most clinician-facing apps use EHR launch, since the user is already in the chart and expects the app to know which patient they are on. Standalone launch fits patient-facing apps and tools people open outside Epic, and it requests context with scopes such as launch/patient. Inside Epic, an embedded app often runs in an iframe, so plan for HTTPS everywhere and test cookie and session handling early.

2. Register on open.epic and fhir.epic.com

Register the app on Epic's developer site (fhir.epic.com, part of open.epic). You declare the app's audience, redirect URIs, the FHIR APIs it uses, and whether it is a confidential or public client. Epic issues two client IDs: a non-production ID for the sandbox and a production ID for customer environments.

Treat them as separate credentials. The non-production ID works against Epic's sandbox and test patients. The production ID does nothing by itself. Each Epic customer has to enable your app in its own environment before that ID works there, and that step belongs to the hospital.

3. Handle the iss/launch handshake and .well-known/smart-configuration

On an EHR launch, Epic calls your launch URL with iss (the hospital's FHIR base URL) and launch (an opaque context token). Your app fetches {iss}/.well-known/smart-configuration to discover the authorization and token endpoints, then redirects to authorize, sending back the same launch value.

Two rules keep this safe. Check iss against the list of FHIR servers you expect, and never send a user to an authorization server you did not discover from a trusted base URL. Do not hardcode Epic's sandbox endpoints, because every hospital has its own base URL. Set aud in the authorize request to the iss value. Under SMART App Launch v2, servers expect it.

4. Use authorization code with PKCE

SMART App Launch v2 uses the OAuth 2.0 authorization code flow with PKCE. Generate a random code_verifier, send its SHA-256 code_challenge with code_challenge_method=S256 on the authorize request, then send the verifier when you exchange the code for a token. Include a random state and check it on return.

Confidential clients also authenticate at the token endpoint. Epic supports asymmetric client authentication with a signed JWT, which avoids storing a shared secret. The token response carries the access token plus launch context, such as the patient ID, and an ID token for fhirUser if you asked for openid fhirUser. Keep tokens on the server, out of the browser.

5. Ask for narrow scopes

Request only the scopes your app actually uses. SMART v2 scopes look like patient/Observation.rs or user/Patient.r, where the suffix letters mean create, read, update, delete, and search. Hospital reviewers read scope lists closely, and every broad scope is one more question you will have to answer before go-live.

Start from features. If the app only reads vitals for the patient in context, patient/Observation.rs plus launch, openid, and fhirUser may be the whole list. Add write scopes only when a workflow writes back, and expect that workflow to get its own sign-off. Scopes must match the APIs you registered in step 2.

6. Test in the sandbox

Epic's sandbox lets you run the whole flow against test patients with your non-production client ID: launch, discovery, authorization, token exchange, and FHIR calls. Test both a fresh launch and a relaunch, an expired token, a denied scope, and a patient with sparse data.

Test data is cleaner than production data, and some behavior differs by customer and Epic version. A clean sandbox run shows the integration is built correctly. It does not mean any hospital has approved your app.

7. Get ready for production

Production readiness has two parts. On the code side: your production client ID registered with the right APIs, redirect URIs on HTTPS on your production domain, logging that keeps PHI out of logs, and token storage you can explain to a reviewer. On the hosting side, your servers must be ready to hold real PHI.

Then a hospital has to say yes. Its IT team enables your production client ID in its Epic environment and gives you the FHIR base URL. Usually its security team and legal team need to finish their reviews first. In our experience that review work sets the go-live date.

The code is the easy part

Once the seven steps work in the sandbox, most of what remains has nothing to do with code. As our founder said on that same call, nothing here is technically hard. The slow parts are implementation with a customer and getting an app through Epic's process.

What usually comes next:

  • BAA. A hospital will expect a business associate agreement covering your handling of PHI before production.
  • Security review. Expect a vendor questionnaire and requests for evidence, such as a SOC 2 Type II report and where production PHI is hosted.
  • Hosting. Your launch URL, backend, and token store need to run on HIPAA-eligible infrastructure.
  • Showroom. Epic's Connection Hub, the listing on Showroom where hospitals discover apps, requires at least one live production connection at an Epic customer, and the listing belongs to the app owner. So listing comes after your first hospital is live. Epic's review of a submission commonly runs weeks 2–8 after intake, which typically runs weeks 1–3.

This is the part SoFaaS covers. SoFaaS is a compliant SMART on FHIR runtime: your app runs on it inside Epic. SoFaaS is SOC 2 Type II audited. A BAA is in place with every hospital deployment. Production PHI stays in U.S. regions, and in-flight traffic uses TLS 1.2 or higher. SoFaaS also prepares the materials for your Showroom submission. The listing stays yours, and Epic makes the decision. For more on this stage, see stuck on Epic integration, how to get your app into Epic, and hosting a SMART on FHIR app. Security details are on the security page.

Who owns each step

StepVendor (you)EpicHospitalRuntime (SoFaaS)
1. Launch typeChooses EHR or standalone and builds the launch URL. Code tools can scaffold this.Defines how it launches apps.Decides where the app appears in the workflow.Hosts the launch URL.
2. RegistrationRegisters the app, APIs, and redirect URIs on fhir.epic.com.Issues sandbox and production client IDs.Enables the production client ID in its environment.None. Registration stays with you.
3. iss/launch + discoveryValidates iss, reads .well-known/smart-configuration. Libraries handle the mechanics.Serves the launch and discovery document.Owns its FHIR base URL.Runs the handshake on hosted infrastructure.
4. Auth code + PKCEImplements the flow. OAuth libraries handle most of it.Runs the authorization and token servers.Controls user access.Stores tokens server-side.
5. ScopesPicks the minimum scopes. No tool can decide this for you.Enforces registered APIs.Reviews and approves scopes.None.
6. Sandbox testingWrites and runs the tests.Provides the sandbox and test patients.None.None.
7. Production readinessOwns app code, PHI handling in the app, and the customer contract.None.Runs security review, signs the BAA, provisions.HIPAA hosting, BAA with every hospital deployment, SOC 2 Type II evidence packet, quarterly Epic-change maintenance.
Showroom listingHolds the listing and files it after the first live connection.Reviews and decides.Is the live connection the listing requires.Prepares submission materials. Does not hold or file the listing.

Non-goals

So nobody buys the wrong thing:

  • Not a code generator. Your team still writes the app.
  • Not a public website host. Your marketing site is a separate job.
  • Not a server-to-server FHIR consumer. Backend services with no user launch use a different SMART pattern.
  • Not a Redox replacement. Redox moves and translates clinical data between systems. Compose with it for data flows outside the SMART launch path.

FAQ

How hard is it to build a SMART on FHIR app for Epic?

The build is usually the smaller part. SMART on FHIR is OAuth 2.0 plus FHIR's REST API, and an experienced web team can get a working launch in Epic's sandbox fairly quickly. Most of the calendar goes to what follows: a hospital's security review, a BAA, HIPAA hosting, and provisioning.

What is the difference between an EHR launch and a standalone launch?

In an EHR launch, Epic opens your app from inside the chart and passes iss and launch parameters, so the app inherits the current patient and user. In a standalone launch, the user opens the app directly and selects context during authorization. Clinician-facing apps usually use EHR launch.

Why does Epic give me two client IDs?

The non-production client ID works against Epic's sandbox and test patients. The production client ID is for real customer environments, and each hospital has to enable it in its own Epic instance before it works there. Having a production ID does not mean any hospital has approved your app.

Do I need PKCE for an Epic SMART app?

Plan on it. SMART App Launch v2 requires PKCE with the S256 method for the authorization code flow, for public and confidential clients alike. Generate a code verifier, send its SHA-256 challenge on the authorize request, and send the verifier when you exchange the code for a token.

Can I get listed on Epic Showroom before I am live?

No. Epic's Connection Hub listing terms require that your product have a live, active connection to an Epic system, and Epic describes the listing as being for live apps with at least one live Epic connection. The app owner holds the listing. List once your first hospital is live.

Related reading

If your app works in the sandbox and you are working out what it takes to go live at a hospital, talk to us.

Have a deal stuck on Epic?

If your app works in the sandbox and you are working out what it takes to go live at a hospital, start here.

Talk to us