SMART on FHIR is how an application plugs securely into an EHR or FHIR server — it is the standard that lets a third-party app launch from inside Epic or Cerner, know which patient is open, and read or write clinical data with the user’s authorization. If you have built an OAuth2 integration before, your first SMART app is very approachable; the concepts map cleanly onto patterns you already know. This guide walks through what SMART on FHIR actually is, the two launch flows, the scope model, the launch sequence step by step, how to test in a sandbox, and what changes when you go to production against a real EHR.
A scope note: this is a technical guide, not legal advice, and a production SMART app handling real patient data is handling PHI, which must be protected with appropriate security and a BAA where applicable. With that said, let’s build.
What SMART on FHIR Is
SMART on FHIR — SMART stands for Substitutable Medical Applications, Reusable Technologies — is a framework that layers OAuth 2.0 (and OpenID Connect for user identity) on top of FHIR. Its purpose is to let apps authenticate, obtain scoped, time-limited access to a FHIR API, and run inside or alongside an EHR in a standardized, secure way. The vision behind it is “substitutable” apps: write an app once to the SMART standard and run it against any EHR that supports SMART, rather than building a bespoke integration per vendor. In practice, the major EHRs support SMART App Launch, which is why it is the foundation of EHR app integration and why US regulation’s API expectations are built around it.
The Two Launch Flows
SMART defines two ways an app starts, and understanding the difference is the key concept.
EHR launch is when the app is launched from within the EHR — a clinician clicks it inside an open patient chart, and the app receives launch context, such as the current patient and encounter, so it opens already knowing who and what it is about. This is the flow for apps embedded in the clinical workflow.
Standalone launch is when the app is launched independently of the EHR — the user opens the app directly, and the app initiates authorization and obtains context (for example, by the user selecting or authorizing a patient). This is the flow for patient-facing apps and tools used outside an EHR session.
Both flows use the same underlying OAuth2 mechanics; the difference is whether the EHR hands the app its context at launch or the app obtains it through the authorization flow.
The Building Blocks
A SMART app rests on a few pieces. App registration: before an app can connect, it is registered with the FHIR server or EHR, which assigns a client ID, records the redirect URI(s) the authorization flow will use, and establishes the scopes the app may request. Discovery: the server publishes its SMART configuration (commonly at a .well-known/smart-configuration endpoint), telling the app where the authorization and token endpoints are and what capabilities it supports. And the OAuth2/OIDC flow itself: the authorization-code flow that exchanges a user’s consent for an access token. Get these three straight and the rest follows.
SMART Scopes: The Access Model
Scopes are how SMART controls what an app can touch, and the syntax is worth learning. A SMART scope combines a context prefix, a resource, and an access level — for example, patient/Observation.read means “read Observations for the patient in context,” user/*.read means “read all resources the user can access,” and system/*.read is for server-to-server access without a user. Alongside resource scopes there are special scopes: launch and launch/patient for launch context, openid and fhirUser for identity, and offline_access to obtain a refresh token for longer-lived access. It is also worth knowing that SMART has evolved — the newer scope syntax (often called v2) supports finer-grained permissions (separating read and write more precisely) than the original v1 scopes, and which an EHR supports affects how you request access. Request the least privilege your app needs, not the broadest scope you can get.
Public vs. Confidential Clients
How your app authenticates itself depends on what kind of client it is. A confidential client — typically a backend app that can keep a secret — authenticates with a client secret (or a signed assertion). A public client — a single-page app or mobile app that cannot safely hold a secret — uses PKCE (Proof Key for Code Exchange) to secure the authorization-code flow instead. Choosing the right client type and securing it correctly is fundamental; using a public-client pattern without PKCE, or trying to embed a secret in a browser app, are common and serious mistakes.
The Launch Sequence, Step by Step
Putting it together, a SMART App Launch generally proceeds like this. The app is launched (from the EHR with a launch token, or standalone). The app discovers the server’s authorization and token endpoints via the SMART configuration. The app sends an authorization request with its client ID, redirect URI, requested scopes, and (for public clients) a PKCE challenge. The user authenticates and authorizes the requested access. The server redirects back with an authorization code. The app exchanges the code at the token endpoint for an access token — along with context like the patient ID, and optionally a refresh token. Finally, the app calls the FHIR API using the access token as a bearer token, scoped to what the user authorized. That sequence is the heart of every SMART app, and once it clicks, the pattern repeats everywhere.
SMART Backend Services (System-to-System)
Not every integration involves a user clicking a button. For server-to-server access — for example, Bulk Data export for population-level analytics — SMART defines Backend Services, where the client authenticates with a signed JWT (no interactive user) and requests system/ scopes. This is the right flow for automated, non-user-facing data access, and it is distinct from the user-facing launch flows above. If your use case is “a backend pulls data on a schedule,” Backend Services, not App Launch, is what you want.
Building Your First App, Practically
Pick a Sandbox
Start in a SMART sandbox rather than a production EHR — the SMART Health IT launcher and the EHR vendors’ sandboxes (Epic on FHIR, the Oracle Health/Cerner sandbox) let you develop safely against test data.
Register Your App
Register to get a client ID and configure your redirect URI and scopes for the sandbox.
Implement the Launch and Token Exchange
Build the authorization-code flow — discovery, authorization request (with PKCE for a public client), and token exchange — to obtain an access token and launch context.
Make a FHIR Call
Use the access token to call the FHIR API, starting simple — read the in-context Patient and an Observation, for instance.
Render and Iterate
Display the data, then expand scopes and functionality from there.
Keeping the first app deliberately small — launch, token, one or two FHIR reads — is the fastest way to internalize the flow before adding complexity.
Testing in a Sandbox
Sandboxes are essential, not optional, for SMART development. The SMART Health IT launcher lets you simulate EHR and standalone launches against test data; the EHR vendors provide their own sandboxes (Epic on FHIR, the Oracle Health/Cerner developer sandbox) that mirror their real environments; and ONC’s Inferno can test conformance, which matters if certification is in scope. Developing and testing against sandboxes catches issues long before you touch a production EHR, where mistakes are costly and access is gated.
Taking It to Production
Moving from sandbox to production means registering your app with the real EHR and going through its process — which, for the major vendors, runs through their developer programs and marketplaces. See our Epic App Orchard / Connection Hub and Cerner Code / Oracle Health practices for what that entails, and our Epic integration work for the broader picture. In production you are handling real PHI, so security, proper token and secret handling, least-privilege scopes, and a BAA where applicable all become non-negotiable — see our data security practice. The SMART standard also underpins the API expectations in US regulation, which our CMS interoperability compliance work addresses.
Common Pitfalls
The recurring mistakes: mishandling the redirect or PKCE flow and creating security holes; requesting wrong or excessive scopes instead of least privilege; ignoring launch context in EHR launch and re-asking for what the EHR already provided; not handling token expiry and refresh (forgetting offline_access when you need longer-lived access); confusing EHR launch with standalone launch; and skipping sandbox testing and debugging against a production EHR. Each is avoidable with an understanding of the flow, which is exactly why building a small first app in a sandbox pays off.
How Taction Helps
We build SMART on FHIR apps — EHR-launched and standalone — handling the OAuth2/OIDC flows, scope design, launch context, token and refresh handling, and the public-versus-confidential client specifics, and we take them from sandbox to production against real EHRs through their developer programs. We also build SMART Backend Services for system-to-system and Bulk Data use cases. With deep FHIR and EHR-integration experience and ISO 27001-certified security, and PHI handled under a signed BAA, we make first apps and production apps alike. Our FHIR API development practice, within our healthcare software work, covers the full build.
Related reading: FHIR R4 Implementation Guide for Developers · HL7 v2 to FHIR Migration: A Practical Guide
Frequently Asked Questions
What’s the difference between EHR launch and standalone launch?
In EHR launch, the app is launched from within the EHR and receives launch context such as the current patient and encounter, so it opens already knowing its context. In standalone launch, the app is opened independently and obtains context through the authorization flow. Both use the same OAuth2 mechanics; the difference is how the app gets its context.
Do I need a client secret?
It depends on your client type. A confidential client (typically a backend that can keep a secret) authenticates with a secret or signed assertion. A public client (a single-page or mobile app) cannot safely hold a secret and uses PKCE instead. Choosing the right type and securing it correctly is fundamental.
How do SMART scopes work?
A SMART scope combines a context prefix (patient, user, or system), a resource, and an access level — for example patient/Observation.read. Special scopes cover launch context, identity (openid, fhirUser), and refresh tokens (offline_access). Newer v2 scopes add finer-grained permissions. Always request the least privilege your app needs.
How do I test a SMART app without a real EHR?
Use a sandbox. The SMART Health IT launcher simulates launches against test data, the EHR vendors provide their own sandboxes (Epic on FHIR, the Oracle Health/Cerner sandbox), and Inferno can test conformance. Developing against sandboxes catches issues before you touch a gated, costly production EHR.
What about server-to-server access without a user?
Use SMART Backend Services, where the client authenticates with a signed JWT and requests system/ scopes — the right flow for automated, non-user-facing access like Bulk Data export. It is distinct from the user-facing App Launch flows.
How do we get our app into Epic or Cerner in production?
Through the vendors’ developer programs and marketplaces — Epic’s Connection Hub/Showroom and Oracle Health’s Cerner Code — each with its own registration and process. You build to SMART, then go through the vendor’s program; we handle both the build and navigating those processes.
Building a SMART on FHIR app? Schedule a free consultation →
This article is a technical guide, not legal advice. Reviewed by Taction Software’s healthcare engineering team. ISO 27001-certified information security management. PHI in a production SMART app is protected under a signed BAA.




