Written by Technical Team | Last updated 15.08.2025 | 16 minute read
Epic exposes Fast Healthcare Interoperability Resources (FHIR) endpoints that follow HL7 FHIR standards and the SMART on FHIR profiles used across modern healthcare apps. In practical terms, this means you build your application against widely understood resources such as Patient, Observation, MedicationRequest, Condition and Encounter, and then authenticate via OAuth 2.0 under the SMART umbrella. Epic implements these standards in a way that is consistent with the broader ecosystem but tailored to the realities of enterprise clinical systems, so you enjoy portability without losing access to Epic-specific capabilities.
Most developers approach Epic by first targeting a sandbox FHIR server for early integration, then moving toward customer-specific sandboxes and production tenants. You will interact with multiple FHIR base URLs over the life of your product because each Epic customer (a hospital, trust, or integrated care system) hosts its own endpoints and has its own configuration for scopes, consent, and access. Your design therefore needs to treat the FHIR base URL and the SMART/OIDC metadata endpoints as tenant-specific settings rather than constants compiled into the app.
SMART defines three common patterns you will likely need to support. The EHR-launched flow is initiated from within Epic’s clinical workspace and injects context—such as the selected patient or encounter—into the OAuth authorisation sequence so your app opens on the right record. The standalone user-facing flow starts outside the EHR (for instance, from a web link) and allows users to pick their Epic organisation, sign in, and then select a patient. Finally, the backend service pattern is a machine-to-machine authorisation for server processes that read or write data without an interactive user in the loop, such as nightly synchronisations or analytics pipelines.
From a security perspective, Epic expects you to meet professional-grade standards. Public clients must use PKCE to protect the authorisation code exchange, confidential clients must safely store secrets or certificates, and server-to-server processes are expected to present signed JWTs and follow UDAP-style registration and trust where applicable. You will also encounter policies around refresh tokens, session timeouts, and reconsent that vary by tenant. Building a flexible authorisation layer from the outset pays off when you encounter these nuances during customer onboarding.
Before you log into Epic’s developer portal to create an app record, step back and treat this as a regulated product integration rather than a generic OAuth registration. You will be asked to provide detailed information about your security posture, data handling, clinical safety mitigations, and support model. In the United Kingdom and Europe, buyers increasingly expect alignment with GDPR, DPIAs, and clinical risk management practices such as DCB0129/0160 or equivalents. In the United States, HIPAA and BAAs set the tone. Even if you are building a narrowly scoped viewer, be ready to explain how you protect personal data at rest and in transit, how you manage keys, what you log, and how users can exercise their rights to access or erase data.
Architecturally, isolate your Epic integration so that it is easy to configure per tenant. At minimum you should externalise: the FHIR base URL, the OIDC discovery URL, the SMART well-known configuration URL, your registered redirect URIs, and the set of scopes you request per environment. Treat these as records in your own “connection directory” so you can add a new health system without redeploying code. If you operate a multi-tenant SaaS, plan how each customer’s trust material (client IDs, secrets, JWKS or certificates) is stored—ideally in a dedicated secrets manager with strict separation and audit trails.
Decide early which SMART flows your product will support. If clinicians must open your app in the context of a patient or encounter, you need the EHR-launched flow and should design your UI to interpret launch context (for example, patient, encounter, need_patient_banner, smart_style_url). If you anticipate patient-facing features, test the standalone flow with PKCE and plan a clean organisation-picker experience so patients can choose the correct Epic tenant. For background jobs, implement the backend service flow with signed JWT client assertions and a robust job scheduler that can refresh tokens or rotate assertions automatically.
Choose your scopes deliberately. Patient-level scopes (such as patient/*.read) limit access to the patient in context but are often sufficient for focused, point-of-care tools. User-level scopes (for example, user/*.read) authorise access based on the clinician’s privileges and may cover multiple patients; they typically require stronger governance and consent. System-level scopes are reserved for backend services and demand service-level trust. Include standards-based identity scopes like openid, profile and fhirUser when you need user identity details. If your app wants to persist access beyond a single session, request offline_access where the tenant allows refresh tokens.
Finally, draw a straight line from your product’s clinical value to the minimal set of data you need. Epic’s review and customer committees will scrutinise whether the scopes you request are proportionate. If your app visualises vital signs and lab results, show that you only ask for Observation.read and Patient.read rather than a blanket user/*.*. The more precise you are, the smoother your path through registration, security review, and production go-live.
When you are ready to register, you create an application record in Epic’s developer environment with descriptive metadata, redirect URIs, and one or more launch URIs if you support EHR-launched scenarios. Treat descriptions and screenshots as part of your go-to-market collateral; health systems rely on these when deciding whether to enable your app for their clinicians or patients. Upload a high-resolution icon, provide a short and long description, and be explicit about which clinical roles benefit from your product.
The technical heart of registration is trust. For public clients—mobile and browser-based apps—you will obtain a client identifier and declare your exact redirect URIs. You will not receive a client secret because those platforms cannot safely store one; instead you must use PKCE during the authorisation code exchange. For confidential clients—typically your SaaS backend—you may register a client secret or a public key (via JWKS or certificate) for stronger, asymmetric authentication. Many organisations prefer asymmetric keys because they avoid long-lived shared secrets and support automated rotation without downtime.
Epic sandboxes are typically open for initial development against synthetic data. Use these to validate SMART flows, decoding launch context and verifying that your scope requests match your app’s needs. When progressing to a customer’s environment, you initiate a connection request that triggers review on their side. Each health system will assign your app a new client identifier and will control which scopes and launch contexts they approve. Expect iteration: hospital security teams may ask you to remove unused scopes, switch to asymmetric credentials, or enable multi-factor authentication for your user accounts before proceeding.
Approval is not the last step. Production enablement includes endpoint whitelisting, consent configuration, and occasionally the creation of dedicated clinician roles or user templates to ensure the right staff can access your app. Arrange joint testing sessions with clinical champions to confirm that launch context behaves as expected for their workflows—opening a patient from the ward list may supply different context variables than opening from an order composer or an ED tracker. Capture these nuances in your runbook so your support teams can troubleshoot quickly after go-live.
Your implementation should start from the SMART discovery documents provided by each Epic tenant. By fetching the well-known configuration, your app learns the authorisation and token endpoints, the supported grant types, the code challenge methods, and the set of SMART capabilities you can rely on. Build this discovery step into your initial handshake and cache the results with sensible expiry; do not assume all tenants support every optional feature, and avoid hard-coding endpoints.
For interactive apps, the authorisation code flow with PKCE is the default. When a user selects an Epic tenant (or when the app is launched from within Epic), your app redirects the user to the authorisation endpoint with parameters including client_id, redirect_uri, response_type=code, scope, and state. If you are in an EHR-launched flow, you will also include the launch parameter supplied by Epic, which binds your request to the in-EHR context. Generate a high-entropy code verifier on the client, derive a code challenge, and supply it alongside code_challenge_method=S256. After the user authenticates and grants consent, the Epic token endpoint will exchange the returned code for tokens only if your app presents the original code verifier. This prevents interception attacks and is mandatory for public clients.
The launch context is one of the most powerful parts of SMART on FHIR. In an EHR-launched scenario, your app receives a short-lived launch value from Epic at the moment the clinician clicks your button or activity. When you complete the OAuth handshake, the resulting ID token and SMART context claims will include details such as patient, encounter, and fhirUser (which identifies the clinician). Your app should treat these as authoritative and avoid second-guessing them by calling FHIR to “double check” the patient unless your use case demands it. Respect need_patient_banner by displaying a clear banner with the selected patient’s name and identifiers when Epic requests it.
Backend services follow a different pattern. Instead of an interactive authorisation code, your service creates a signed JWT assertion as its client credential. The assertion contains claims such as issuer (your client ID), subject (often the same as issuer), audience (the token endpoint), and a short expiry. You sign it with your private key, then POST it to the token endpoint to obtain an access token with the system-level scopes your customer has approved. Keep token lifetimes short and rotate keys on a schedule. Because these tokens are powerful, store them in memory only and avoid writing them to logs or databases.
Identity matters beyond authorisation. When your app needs to display the clinician’s name or determine their role, decode the OIDC ID token or query the fhirUser reference if available. Some tenants prefer to limit identity exposure; in those cases, rely on contextual behaviour (for example, hide administrative functions for non-admin users based on a role claim you agree with the tenant) rather than demanding full directory integration. When you do store identity data, keep it to the minimum required and obey your customer’s retention policy.
Token management is where robust engineering makes a difference. Treat access tokens as opaque and short-lived; rely on refresh tokens only when you truly need long-lived sessions and have explicit approval to request offline_access. Implement revocation handling: if refresh token rotation is enabled, detect replay and force re-authentication. Clock skew between your systems and the tenant’s identity provider can lead to occasional validation errors, so incorporate a small tolerance when validating token expiry. When you run in browsers, remember that third-party cookie restrictions can affect embedded app frames; prefer the OAuth authorisation code with PKCE and avoid implicit flows.
From a developer-experience perspective, invest in tooling that makes these flows observable. Wrap your OAuth client so it emits structured events for redirections, token exchanges, refresh attempts, and failures. Mask sensitive values but log correlation IDs so you can reconstruct a user’s journey during support incidents without exposing secrets. For mobile clients, include an in-app diagnostics screen that shows the connected tenant, the granted scopes, token expiry, and the current patient context—this reduces back-and-forth with clinical teams when troubleshooting on the ward.
Security reviews will ask about your approach to redirect URIs. Always register exact, fully qualified redirect URIs and avoid overly broad patterns. On mobile, prefer app-claimed HTTPS links where possible; custom schemes are acceptable but demand extra care to avoid hijacking. Validate state and nonce values on return from the authorisation server to prevent cross-site request forgery and token injection. If your app supports multiple tenants, ensure the iss (issuer) in the returned ID token or the SMART configuration matches the tenant you initiated the flow with; if not, abort and alert.
Finally, think about failure modes from the start. A clinician may attempt to launch your app without a selected patient; decide whether your UI should prompt them to pick one or show a read-only home screen. Network conditions vary across hospitals, and firewalls may block certain ports; retry gracefully and surface a clear status page when Epic endpoints are unreachable. Your support documentation should include common HTTP error mappings—403 caused by missing scopes, 401 from expired tokens, 409 for optimistic concurrency conflicts when updating FHIR resources—so your team can decode issues quickly under pressure.
The launch experience is your product’s first impression inside the clinical workflow, and small details matter. In Epic’s EHR-launched model, your app typically appears as a button, activity, or tab. When a clinician clicks it, Epic generates a launch identifier and opens your app either in an embedded frame or a separate window, depending on configuration. Your app should start up quickly, read the SMART context, and render an interface that clearly reflects the patient in focus. Many successful teams add a slim patient banner that mirrors Epic’s own banners, with full name, MRN/NHS number, date of birth, and encounter details that update if the clinician switches context and relaunches.
Testing begins in the public sandbox but becomes meaningful in a customer sandbox with realistic configuration, identities, and workflows. Work with clinical champions to script scenario-based testing rather than just endpoint checks. Test launching from different Epic worklists and activities, because each path may supply unique combinations of context. Validate that your app gracefully handles missing or partial data—for example, a historical encounter without vitals, or a newborn with a different set of identifiers. If you write data back, practise on synthetic or masked records and make sure your app handles FHIR server responses that add or alter fields beyond your request.
Performance is often overlooked in standards-compliant apps, yet it drives clinician satisfaction. Build simple heuristics for paging and caching: if you request observations for an inpatient with a long stay, ask for the last few days first and paginate older data on demand. When the app opens inside the EHR, keep your first render fast by parallelising calls that are independent and by showing skeleton loading states for sections that rely on slower queries. Respect rate limits by using conditional requests (If-None-Match, If-Modified-Since) and ETags where supported, and fall back sensibly when not.
Promoting your app inside Epic organisations is as much about governance as it is about code. Once you have passed technical review, you will present to a change advisory or clinical safety group. Bring evidence of clinician training materials, incident response plans, and a rollback strategy if problems arise during the first week of go-live. Agree monitoring signals in advance: API error rate thresholds, token exchange failures, and performance SLOs that, if breached, trigger a rollback or temporary disablement. Plan communications to frontline staff so they know what to expect on the day of launch and how to request support.
After go-live, iterate deliberately. Build telemetry that correlates feature usage with clinical context (for example, which wards or specialties use your app most) without collecting personal data beyond what your customers permit. Share insights and roadmap proposals with your Epic customers: perhaps your app works well for adult medicine but needs tweaks for paediatrics, or you discover that emergency clinicians benefit from a condensed, high-contrast view under poor lighting. Treat each hospital as a partner in shaping the product to their workflows, and you will find subsequent enablements become easier as your reputation grows.
Registering, authenticating, and launching applications with Epic’s FHIR APIs is a repeatable craft if you approach it with both engineering rigour and respect for clinical workflows. Start by understanding the standards—FHIR resources and SMART on FHIR flows—and make those the backbone of your architecture. Prepare your organisation to answer hard questions about security, privacy, and patient safety, because those are inseparable from healthcare integration. Register carefully with tenant-specific trust, choose scopes that match your product’s value, and build your OAuth layer to be both portable and observable.
Implement the authorisation code with PKCE for interactive apps and signed JWT assertions for backend services. Handle SMART launch context deterministically so clinicians land on the right patient and encounter every time. Engineer token management and error handling to the same standard you would expect from a banking app, because clinical decisions depend on the integrity and availability of the data you present. Test beyond the happy path, anticipating differences across Epic workflows and hospitals, and invest in performance so your app feels like part of the EHR rather than an external add-on.
When you launch, sweat the details of the first render, the patient banner, and the way your app “breathes” inside the EHR frame. After you go live, measure what matters, share insights with your customers, and refine your scope requests and flows as you learn. Epic’s ecosystem rewards apps that are respectful of clinicians’ time, conservative in their permissions, and precise in how they use context to reduce clicks. Do these things consistently and you will not only pass registration—you will earn a place in the clinician’s daily practice.
By treating Epic integration as a disciplined, standards-driven programme rather than a one-off connection, you create an app that is easier to register, safer to authenticate, and more delightful to launch—no matter how many Epic tenants you support.
Is your team looking for help with Epic integration? Click the button below.
Get in touch