Epic Registration
Epic's developer sandbox is free and requires no approval process. You can have a working non-production client ID in about 15 minutes โ the only wait is a 1-hour sync window after creating your app.
What you get for freeโ
- A non-production client ID accepted by Epic's sandbox FHIR server
- Access to Epic's non-production FHIR R4 API at
fhir.epic.com - Pre-loaded synthetic test patients (no real PHI)
- Epic LaunchPad โ a browser tool that simulates Hyperspace clicking your app
- Unlimited sandbox API calls at no cost
The non-production client ID only works against Epic's sandbox servers. Going live in a real hospital requires App Orchard registration and hospital IT approval โ a separate process covered in the Production Checklist.
Step 1 โ Create a developer accountโ
- Go to fhir.epic.com
- Click Sign In โ Sign Up
- Fill in your name, email, and organisation
- Check your inbox and click the verification link
The account is free and does not require any company affiliation.
Step 2 โ Create your appโ
- Sign in to fhir.epic.com
- Click Build Apps in the top navigation
- Click Create (top right)
Fill in the form:
| Field | Value |
|---|---|
| Application Name | SMART FHIR Client (or your own name) |
| Application Audience | Clinicians or Administrative Users |
| Application Type | Patient Facing or Clinician Facing โ choose Clinician Facing for EHR launch |
| SMART on FHIR Version | SMART App Launch 2.0 |
| Redirect URIs | http://localhost:8080/callback |
Click Save.
You can register several URIs at once โ add both your local and any staging redirect URIs before saving:
http://localhost:8080/callback
https://staging.yourapp.com/callback
Step 3 โ Note your client IDsโ
After saving, the page shows two client IDs:
Non-Production Client ID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Production Client ID: yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy
Copy the Non-Production Client ID. This is the value for EPIC_CLIENT_ID.
Client IDs are not secret (they appear in browser network traffic), but treat the non-production ID as environment-specific config โ not hardcoded in source files. Use environment variables or a secrets manager.
Step 4 โ Wait for sandbox syncโ
After creating an app or changing its configuration, Epic's sandbox takes up to 1 hour
to propagate the changes. During this window, authorisation requests will fail with
invalid_client.
This sync only happens after you create or update the app. Subsequent test launches work immediately once the first sync completes.
You can check sync status by attempting a discovery request:
curl "https://fhir.epic.com/interconnect-fhir-oauth/api/FHIR/R4/.well-known/smart-configuration"
If it returns a JSON document with authorization_endpoint, the server is ready.
Step 5 โ Configure the appโ
Export your client ID and run with the Epic profile:
export EPIC_CLIENT_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
mvn spring-boot:run -Dspring-boot.run.profiles=epic
Confirm the app started and config was accepted:
curl http://localhost:8080/health
# โ {"status":"UP","app":"smart-fhir-client","epicClientId":"xxxxxxxx..."}
Step 6 โ Test with LaunchPadโ
Epic LaunchPad simulates a clinician clicking your app inside Hyperspace. It generates
a real launch token and redirects your browser to your app's /launch endpoint,
completing the full SMART EHR launch flow.
- Go to open.epic.com/launchpad
- Select the SMART on FHIR App Launch tab
- In Launch URL, enter:
http://localhost:8080/launch - In FHIR Server, select
R4and choose any sandbox patient - Click Launch
Your browser will:
- Be redirected to
http://localhost:8080/launch?iss=...&launch=... - Be redirected to Epic's authorisation page (auto-approved in sandbox)
- Return to
http://localhost:8080/callback?code=...&state=... - Land on
http://localhost:8080/โ the dashboard with real sandbox FHIR data
What the logs show during a successful launchโ
INFO SmartLaunchController - EHR launch received โ iss=https://fhir.epic.com/...
INFO SmartDiscoveryService - Fetching SMART configuration from ISS: https://fhir.epic.com/...
INFO SmartDiscoveryService - SMART configuration cached โ authEndpoint=..., tokenEndpoint=...
DEBUG SmartAuthRequestBuilder - PKCE generated and stored in session โ challenge method: S256
INFO SmartLaunchController - Redirecting to authorize endpoint โ mode=ehr, state=...
INFO SmartCallbackController - SMART EHR launch complete โ patient=eXXXXX, encounter=eYYYYY, scope=launch openid...
INFO IdTokenValidator - id_token validated โ subject=eProvider123, fhirUser=Practitioner/eProvider123
INFO UiController - Fetching Patient/eXXXXX
Verifying FHIR data accessโ
After a successful launch, test each API endpoint:
# Session info (always safe โ works even before a patient is selected)
curl http://localhost:8080/api/session
# Clinician profile (requires openid scope)
curl http://localhost:8080/api/me
# Patient demographics
curl http://localhost:8080/api/patient
# Active conditions
curl http://localhost:8080/api/conditions
# Active medications
curl http://localhost:8080/api/medications
# Combined summary
curl http://localhost:8080/api/summary
Or use the UI directly at http://localhost:8080/.
Epic sandbox test patientsโ
Epic's sandbox includes several pre-loaded synthetic patients with realistic clinical data. The most commonly used for testing:
| Patient | FHIR ID | Notable data |
|---|---|---|
| Camila Lopez | erXuFYUfucBZaryVksYEcMg3 | Conditions, medications |
| Derrick Lin | eq081-VQEgP8drUUqCWzHfw3 | Allergies, encounters |
| Jason Argonaut | Tbt3KuCY0B5PSrJvCu2j-PlK | Minimal data โ good for blank-slate testing |
You can find the full patient list at fhir.epic.com/Documentation#patientlist.
Registered scopes in App Orchardโ
The scopes you configure in application.yml must also be registered in your app's
App Orchard entry. If you request a scope that isn't registered, Epic silently drops it
from the token response โ no error, just missing data.
To update registered scopes:
- Go to fhir.epic.com โ Build Apps โ your app
- Find the Clinical Data section
- Check each FHIR resource and its allowed operations
The scope names in App Orchard correspond to SMART v2 scope strings:
| App Orchard resource | SMART v2 scope |
|---|---|
| Patient โ Read | patient/Patient.rs |
| Condition โ Read | patient/Condition.rs |
| MedicationRequest โ Read | patient/MedicationRequest.rs |
| OpenID | openid |
| FHIR User | fhirUser |
After updating scopes, wait up to 1 hour for the sandbox to sync.
Troubleshootingโ
invalid_client error on the callbackโ
Cause: The client ID in EPIC_CLIENT_ID doesn't match the registered app, or the sync
hasn't completed yet.
Fix:
- Verify
EPIC_CLIENT_IDmatches the Non-Production Client ID shown in fhir.epic.com โ your app - If you recently created or updated the app, wait up to 1 hour for the sandbox sync
invalid_redirect_uri errorโ
Cause: The redirect-uri in application.yml doesn't exactly match what's registered.
Fix: Check for trailing slashes, case differences, and port numbers. The comparison is character-for-character. Update fhir.epic.com โ your app โ Redirect URIs.
App redirects but patient is null in the token responseโ
Cause: The launch scope was not registered in App Orchard, so Epic didn't include
patient context in the token.
Fix: In App Orchard โ your app โ Clinical Data, ensure EHR Launch or the equivalent
launch scope is checked.
id_token validation failed โ JWKS key not foundโ
Cause: The JWKS endpoint derivation in IdTokenValidator couldn't locate the signing key.
Fix: This is a non-fatal warning โ FHIR data access still works. The validator logs a
warning and continues. Full RS256 signature verification requires adding
nimbus-jose-jwt โ see the OIDC docs for the
upgrade path.
Session expires after launch but before the dashboard loadsโ
Cause: Session timeout set too short, or the SMART flow took more than MAX_LAUNCH_AGE_SECONDS
(300 seconds / 5 minutes) between the /launch and /callback steps.
Fix: Epic LaunchPad sometimes auto-approves quickly, but if you interact with it manually,
it can take a few minutes. The 5-minute launch window is configurable in
SmartLaunchController.MAX_LAUNCH_AGE_SECONDS.