• FHIR
  • API Specifications
  • Build Apps
  • Documentation 
    • App Developer Guidelines
    • App Creation & Request Process
    • OAuth 2.0 Tutorial
    • FHIR Tutorial
    • Search Parameters
    • Sandbox Test Data
    • Patient-Facing Apps Using FHIR
  • Login 
    Log in with
    Epic UserWebEpic UserWeb
    or

    Back

    Log in with
    Epic on FHIR

    Forgot your password?

    × Success! An email has been sent to reset your password.

SMART on FHIR (OAuth 2.0)

*

*

*

*

*

*

* Indicates required field

Loading...

Document not found

That document wasn't found. It may have moved or require you to log in to view.

App Developer Guidelines

As an app developer, you are obliged to be familiar with and practice principles for responsible healthcare app development. For any app that uses the Materials, as defined in the Terms of Use, you and your apps should practice the following guidelines. If you have reason to suspect your app is not following the guidelines or is misbehaving and would like Epic to suspend use of your app until the issue is resolved, or if you have any questions about these guidelines, please contact us at open.epic.com/Home/Contact.

1 Guidelines for Well-Behaved Apps

Epic recommends apps be well-behaved, adhering to the following principles:

  1. A well-behaved app does not put patients or users at risk of harm.
  2. A well-behaved app does not create or increase the risk of a security breach in any system to which it connects. The app does not introduce any code of a destructive nature into any system to which the app connects.
  3. A well-behaved app respects the privacy of patients and their families, clinicians, other end users, and anyone else.
  4. A well-behaved app is stable, predictable, and does not negatively impact operations for users or Epic Community Members. A well-behaved app performs as expected at scale and does not generate excessive or unexpected load on a user’s or Epic Community Member’s systems.
  5. A well-behaved app doesn’t corrupt or otherwise cause material inconsistencies in the Epic Community Member’s data and doesn’t cause the Epic Community Member’s other systems to behave inaccurately or unexpectedly.

1.1 Safety Guidelines

The app should reasonably adhere to usability standards that impact patient safety, specifically safety-enhanced design(g)(3) and accessibility-centered design.(g)(5)

If the app allows an end user to search for a patient, the app should collect enough information from the end user to uniquely identify the correct patient or display enough information to the end user to enable the end user to uniquely identify the correct patient.(g)(7)

If the app is launched within an Epic patient workspace for patient-specific workflows, the app should maintain the patient identity within the session. If the app is launched separately for patient-specific workflows, the app should prominently display the patient’s identifying information to avoid any confusion by the end user about which patient’s record she is interacting with.

Because it is possible that your app’s connectivity to an Epic Community Member’s Epic system may be interrupted, you should clearly inform users of your app that it might not always be available to them and that they should not rely on it in an emergency.

1.2 Security Guidelines

The app should encrypt data stored in non-volatile storage using industry-standard encryption algorithms, such as AES-128 or higher. If encryption keys must be written to disk, then the app should make appropriate use of key storage functionality provided by the host platform/operating system and require the administrative user to authenticate to gain access to the encryption keys.

The app should support securing, sending, or receiving data secured with the TLS 1.2 or higher encryption protocol. The app should verify that the server’s TLS certificate is valid and signed by a certificate authorized, recognized, and trusted by the host operating system.

Data exchange between your app and Epic’s APIs and between your app and any other system should be secured with industry standard encryption while in transit,(d)(9) and use authentication and authorization protocols.(d)(1)

The preferred mechanism for apps to authenticate with Epic is OAuth 2.0. Use of other authentication mechanisms, such as HTTP Basic Authentication, may be appropriate in certain circumstances.

The app should protect authentication credentials provided by Epic or Epic Community Members, such as user names, user passwords, access tokens, or refresh tokens. Credentials used to access Epic Community Member’s Epic systems should be unique to each Epic Community Member, should only be used in conjunction with Epic APIs, and should never be passed to non-Epic systems. If the app needs to persist this data to non-volatile storage, it should use approved secure storage functionality provided by the host platform/operating system.

If the app prompts the user for credentials such as passwords or biometric data, or other sensitive information such as credit card numbers or financial account information, you should take care to protect this data. If the data must be written to non-volatile storage, it should never be stored in clear text. The app should make use of one-way hashing algorithms such as SHA-256 or secure storage functionality provided by the host platform/operating system to persist this data. Your app should secure all data on an end-user’s device(d)(7) (d)(8), and enforce inactivity time-outs.(d)(5)

You should keep your apps’ client identifiers confidential and should not disclose them to any other party or use them for any other purpose. Each of your apps should have its own unique client identifier. Apps that are confidential clients and using refresh tokens should keep their client secret safe.

The app is expected to leverage well-known, established software packages to perform low-level cryptography and authentication operations. Authentication and cryptography software should be actively maintained and have published processes for reporting and responding to security vulnerabilities. Apps are strongly discouraged from implementing custom cryptography or authentication functionality.

You should evaluate your app for security vulnerabilities using resources such as OWASP Top 10 and address any applicable vulnerabilities in your app.

1.3 Privacy and Data Use Guidelines

You should provide users (e.g., the Epic Community Member or end user that uses your app) with a clear and understandable data use or privacy policy that includes an explanation of any data elements that your app obtains from or writes to an Epic Community Member’s Epic system and how and where that data is used. If you or your app do any of the following, then, for each, you should disclose such action to, and obtain appropriate consent from, the user: obtaining data from or writing data to an Epic Community Member's Epic system, retaining data processed by your app (including the length of time for which the data is retained), making any secondary uses of the data, providing any other party or product any access to the data, transferring the data to any other party or product.

A patient-facing app should obtain such consent from end users and provide end users with an option to decline consent or to revoke their consent in the future. A patient-facing app should not circumvent the display of any end user authentication or authorization mechanisms from Epic or any Epic Community Member.

When you use, retain, or distribute data that you obtained from an Epic Community Member’s Epic system, you should document who used, retained, or distributed what data, when they did so, and for what purpose they did so. You should provide users access to that documentation upon request.

You should provide clear and prominent notice to users how they may stop the app from reading, writing, or retaining data.

1.4 Reliability and Scalability Guidelines

Your app should be properly tested and should be stable, predictable, and not negatively impact clinical operations or patient safety. Your app should not generate excessive load on a user’s systems or an Epic Community Member’s systems, or cause other systems to behave inaccurately or unexpectedly. Whenever possible, the app should be tested in a non-production environment to gauge the impact on the Epic Community Member’s systems prior to release in production.

1.5 Data and System Integrity Guidelines

The app should appropriately manage end users’ assumptions and expectations of healthcare software to avoid negative outcomes. If the app permits end users to input data that is typically input and stored in an Epic system, or if the app uses an API to look up data from an Epic system and also permits end users to edit that data, then end users are likely to assume or expect that the app will file the new or updated data to Epic. If the app facilitates part of a workflow, then end users are likely to assume or expect the app to trigger downstream steps in the workflow (e.g., trigger a message to another end user, trigger an interface message, etc.).

You and your apps should not corrupt or otherwise cause material inconsistencies in any data used by your apps.(d)(2)

2 Intellectual Property

Direct access to Epic’s software (including an Epic Community Member’s system) is not required to develop, test, deploy, or support your product or service. You can test your product or service through the open.epic/Epic on FHIR sandbox or by working with a particular Epic Community Member. Direct access to Epic’s software can only be granted by both an Epic Community Member and Epic.

3 Transparency and Honesty

You should not misrepresent products, product capabilities, business relationships, timelines, or anything else related to Epic or open.epic in any way. This includes but is not limited to accurate representation of

  1. Your relationship with Epic or Epic Community Members;
  2. Your status or the status of your app in any Epic process;
  3. Your status or the status of your app in the sales cycle or implementation with any Epic Community Member;
  4. Your apps and their features and functionality;
  5. Your progress designing, developing, testing, or enhancing your apps;
  6. Epic products and their features and functionality;
  7. Similar functionality that Epic’s products have to your products;
  8. Your apps’ and Epic products’ abilities to interface with each other;
  9. Your apps’ total cost to the Epic Community Member or end user, including all license/sales, maintenance, subscription, implementation, training, hardware, and other fees;
  10. The data that your apps access from Epic systems, how you or your apps use that data, any secondary uses that you or your apps make of the data, and any access to that data that you or your apps provide to other parties or products; and
  11. Your apps’ cleared or approved use under any applicable regulations (e.g., FDA clearance).


Footnotes

45 CFR 170.315 (d)(1) (Authentication, Access Control, Authorization): "Verify against a unique identifier(s) (e.g., username or number) that a user seeking access to electronic health information is the one claimed; and [...] establish the type of access to electronic health information a user is permitted based on the unique identifier(s) provided"↩

45 CFR 170.315 (d)(2) (Auditable Events and Tamper-resistance): "The health IT records actions pertaining to electronic health information [...] when health IT is in use; changes to user privileges when health IT is in use; and records the date and time [each action occurs]. [...] The health IT records the audit log status [...] when the audit log status is changed and records the date and time each action occurs. [...] The health IT records the information [...] when the encryption status of locally stored electronic health information on end-user devices is changed and records the date and time each action occurs.”↩

45 CFR 170.315 (d)(5) (Automatic Access Time-out): "Automatically stop user access to health information after a predetermined period of inactivity. [...] Require user authentication in order to resume or regain the access that was stopped."↩

45 CFR 170.315 (d)(7) (End-user Device Encryption): "Technology that is designed to locally store electronic health information on end-user devices must encrypt the electronic health information stored on such devices after use of the technology on those devices stops [or] technology is designed to prevent electronic health information from being locally stored on end-user devices after use of the technology on those devices stops."↩

45 CFR 170.315 (d)(8) (Integrity): "Verify [...] upon receipt of electronically exchanged health information that such information has not been altered."↩

45 CFR 170.315 (d)(9) (Trusted Connection): "Health IT needs to provide a level of trusted connection using either 1) encrypted and integrity message protection or 2) a trusted connection for transport."↩

45 CFR 170.315 (g)(3) (Safety-enhanced Design): "User-centered design processes must be applied to each capability technology."↩

45 CFR 170.315 (g)(5) (Accessibility-centered Design): "The use of a health IT accessibility-centered design standard or law in the development, testing, implementation and maintenance of that capability must be identified."↩

45 CFR 170.315 (g)(7) (Application Access - Patient Selection): " The technology must be able to receive a request with sufficient information to uniquely identify a patient and return an ID or other token that can be used by an application to subsequently execute requests for that patient’s data."↩

45 CFR 170.315 (g)(8) (Application Access - Data Category Request): "Respond to requests for patient data (based on an ID or other token) for each of the individual data categories specified in the Common Clinical Data Set and return the full set of data for that data category (according to the specified standards, where applicable) in a computable format."↩

45 CFR 170.315 (g)(9) (Application Access - All Data Request): "Respond to requests for patient data (based on an ID or other token) for all of the data categories specified in the Common Clinical Data Set at one time and return such data (according to the specified standards, where applicable) in a summary record formatted [...] following the CCD document template."↩

App Creation & Request Process

Table of Contents

  • Overview
  • Community Member Login Steps
  • Creating, Activating, and Licensing an App
    • Licensing for Developers
    • Registration Process
    • Registering an App / Provisioning Client IDs
    • Facilitating Epic Community Member Downloads
    • Provisioning Client Secrets
  • Requesting an Epic on FHIR App
    • Before Starting the Request Process
    • Obtaining the open.epic API Subscription Agreement
    • Downloading Client IDs
    • Confirm the Client ID was Downloaded

Overview

Epic on FHIR enables Epic community members to download client IDs for applications registered on the Epic on FHIR website. In order to download client IDs, each Epic community member must have a designated point person with the security to download an app's client IDs on behalf of the organization. The community member's point person for Epic on FHIR is the same person/people as for App Orchard (i.e. the App Orchard Point Person).

The sections below walk through an overview of:

  • The app registration, creation, and activation process.
  • An Epic community member signing the open.epic API Subscription Agreement.
  • The community member downloading an Epic on FHIR app.

A more in-depth version of this guide can be accessed by signing in.

Community Member Login Steps

To download a client ID from Epic on FHIR, the point person of an Epic community member can log in to the Epic on FHIR website using their UserWeb credentials.

Epic Community Members log in to the Epic on FHIR website with UserWeb credentials:

  1. Go to https://fhir.epic.com  
  2. Open the selection list next to Login.
  3. Log in to the Epic on FHIR site by selecting the UserWeb button and enter your UserWeb credentials.


A community member who doesn't have a UserWeb account can create one using a work email address on the UserWeb registration page. App developers should log in using the 'Epic on FHIR' login option.

Creating, Activating, and Licensing an App

Licensing for Developers

App developers do not need to register or agree to terms to view the API specifications on the Epic on FHIR website. To view tutorials, use the sandbox, or obtain client IDs, the developer may register and agree to the Epic on FHIR terms.

Registration Process

  1. Anyone who is interested in accessing the tutorials and sandbox and obtaining client IDs may register online. The sign-up form collects basic demographic and contact information.
  2. During registration, developers will verify their email, agree to the terms of use, and set a password.
  3. There are no enrollment criteria and there is no approval process. Once a developer has completed the registration process and agreed to the terms, they have full access to the Epic on FHIR website.

Registering an App / Provisioning Client IDs

When a developer registers an app, the website creates an app record in the Epic database and assigns the app production and non-production client IDs. After the developer has completed development and testing, they can mark their app ready for production use.

Facilitating Epic Community Member Downloads

To allow an Epic community member to download an app, simply provide them with the Production or Non-Production Client ID from the app’s detail page.

If your app uses backend OAuth 2.0 or refresh tokens, you must also upload production and non-production public keys and/or client secrets. App developers will need to provide this information when the download request is made by navigating to Build Apps page and selecting "Review & Manage Downloads" for the appropriate app. Each public key or client secret should be different for each customer and for each environment. Public keys should be exported to a base64 encoded X.509 certificate before being uploaded. Follow the steps in Provisioning Client Secrets to add a client secret when a community member requests the app.

Provisioning Client Secrets

For apps that use refresh tokens, each instance of the application must be assigned a client secret. For apps that qualify for auto-synchronization functionality, a client secret must be set before the app can sync to the Epic community member's environment. For apps that do not auto-sync, a client secret can be added once the community member has requested the app.

Epic recommends that you use a unique client secret for each community member.

After marking an auto-sync app as Ready for Production or after a download request for an app that does not auto-sync, follow these steps to assign a client secret for a community member:

Requesting An Epic on FHIR App

Before Starting the Request Process

Epic community members should do the following prior to the request process:

  • Work closely with their Epic technical coordinator (TC) to research available integrations.
  • Ensure the right Epic products and interfaces are in place to install an app.
  • Evaluate potential costs of the app up front. In addition to vendor's fees, consider other third-party software, hardware, and content costs, new interfaces, and additional license or subscription volume that may be triggered by the application.

When an Epic community member has committed to moving forward with an app registered on the Epic on FHIR website, a kickoff call should take place to discuss the goals, timeline, and other project details. Including the right stakeholders from each organization at the start of the project enhances communication, sets the right expectations upfront, and helps identify and avoid potential issues before they impact the success of the project.

Epic does not endorse, certify, or verify the integrity, safety, performance, or practices of the developers who use Epic on FHIR or their software.

Obtaining the open.epic API Subscription Agreement

Community members who wish to use the FHIR APIs with a third-party application registered on the Epic on FHIR website must sign the open.epic API Subscription Agreement.

Community members do not need this license in order for their patients to use MU3 apps that used open.epic to develop, test, and register their products.

Downloading Client IDs

To be able to download the client IDs, the following steps must have been completed:

  1. The vendor has created an account on the Epic on FHIR website,
  2. The vendor has registered their product on the Epic on FHIR website,
  3. The vendor has marked their product ready for production use ("Active" status),
  4. The community member has signed the open.epic API Subscription Agreement, and

Community members can view downloaded apps and download new apps from the Epic on FHIR website. The app’s client ID (either Production or Non-Production) is required for downloading and must be obtained from the vendor.

Confirm the Client ID was Downloaded

To confirm the client ID was successfully downloaded, return to the Downloads page on the Epic on FHIR website. The application should now appear on this page.

OAuth 2.0 Tutorial

Using OAuth 2.0

Applications must secure and protect the privacy of patients and their data. To help meet this objective, Epic supports using the OAuth 2.0 framework to authenticate and authorize applications.

Why OAuth 2.0?

OAuth 2.0 enables you to develop your application without having to build a credential management system. Instead of exposing login credentials to your application, your application and an EHR's authorization server exchange a series of authorization codes and access tokens. Your application can access protected patient data stored in an EHR's database after it obtains authorization from the EHR's authorization server.

Before You Get Started

To use OAuth 2.0 to authorize your application's access to patient information, some information needs to be shared between the authorization server and your application:

  1. client_id: The client_id identifies your application to authentication servers within the Epic community and allows you to connect to any organization.
  2. redirect_uri: The redirect_uri confirms your identity and is used to validate and redirect authentication requests that originate from your application. Epic's implementation allows for multiple redirect_uris.
    • Note that a redirect_uri is not needed for backend services using the client_credentials grant type.
  3. Credentials: Some apps, sometimes referred to as confidential clients, can use credentials registered for a given EHR system to obtain authorization to access the system without a user or a patient implicitly or explicitly authorizing the app. Examples of this are apps that use refresh tokens to allow users to launch the app outside of an Epic client without needing to log in every time they use the app, and backend services that need to access resources without a specific person launching the app, for example, fetching data on a scheduled basis.

You can register your application for access to both the sandbox and Epic organizations here. You'll provide information to us, including one or more redirect_uris, and Epic will generate a client_id for you.

Apps can be launched from within an existing EHR or patient portal session, which is called an EHR launch. Alternatively, apps can be launched standalone from outside of an existing EHR session. Apps can also be backend services where no user is launching the app.

EHR launch (SMART on FHIR): The app is launched by the EHR calling a launch URL specified in the EHR's configuration. The EHR launches the launch URL and appends a launch token and the FHIR server's endpoint URL (ISS parameter) in the query string. The app exchanges the launch token, along with the client identification parameters to get an authorization code and eventually the access token.

  • See the Embedded Launch section of this guide for more details.

Standalone launch: The app launches directly to the authorize endpoint outside of an EHR session and requests context from the EHR's authorization server.

  • See the Standalone Launch section of this guide for more details.

Backend services: The app is not authorized by a specific person and likely does not have a user interface, and therefore calls EHR web services with system-level authorization.

  • See the Backend Services section of this guide for more details.

Desktop integrations through Subspace: The app requests access to APIs directly available on the EHR’s desktop application via a local HTTP server.

  • See the Subspace Communication Framework specification for more details.

Considerations for native apps: Additional security considerations must be taken into account when launching a native app to sufficiently protect the authorization code from malicious apps running on the same device. There are a few options for how to implement this additional layer of security:

  • Proof Key for Code Exchange (PKCE): This is a standardized, cross-platform technique for public clients to mitigate the threat of authorization code interception. However, it requires additional support from the authorization server and app. PKCE is described in IETF RFC 7636 and supported starting in the August 2019 version of Epic. Note that the Epic implementation of this standard uses the S256 code_challenge_method. The "plain" method is not supported.
  • Platform-specific link association: Android, iOS, Windows, and other platforms have introduced a method by which a native app can claim an HTTPS redirect URL, demonstrate ownership of that URL, and instruct the OS to invoke the app when that specific HTTPS URL is navigated to. Apple's iOS calls this feature "Universal Links"; Android, "App Links"; Windows, "App URI Handlers". This association of an HTTPS URL to native app is platform-specific and only available on some platforms.

Starting in the August 2019 version of Epic, the Epic implementation of OAuth 2.0 supports PKCE and Epic recommends using PKCE for native mobile app integrations. For earlier versions or in cases where PKCE cannot be used, Epic recommends the use of Universal Links/App Links in lieu of custom redirect URL protocol schemes.


The app you build will list both a production Client ID and a non-production Client ID. While testing in the Epic on FHIR sandbox, use the non-production Client ID.

The base URL for the Current Sandbox environment is:

https://fhir.epic.com/interconnect-fhir-oauth/

EHR Launch (SMART on FHIR)

Contents

  • Step 1: Your Application is Launched from the Patient Portal or EHR
  • Step 2: Your Application Retrieves the Conformance Statement
    • Additional Header Requirements
  • Step 3: Your Application Requests an Authorization Code
  • Step 4: EHR's Authorization Server Reviews The Request
  • Step 5: Your Application Exchanges the Authorization Code for an Access Token
    • If You Are Not Using a Client Secret
    • If You Are Using a Client Secret
  • OpenID Connect id_tokens
  • Validating the OpenID Connect JSON Web Token
  • Step 6: Your Application Uses FHIR APIs to Access Patient Data
  • Step 7: Use a Refresh Token to Obtain a New Access Token

How It Works

Step 1: Your Application is Launched from the Patient Portal or EHR

Your app is launched by the EHR calling the launch URL which is specified in the EHR's configuration. The EHR sends a launch token and the FHIR server's endpoint URL (ISS parameter).

  • launch: This parameter is an EHR generated token that signifies that an active EHR session already exists. This token is one-time use and will be exchanged for the authorization code.
    • Starting in the May 2020 Epic version, Epic community members can enable a feature that makes all OAuth 2.0 tokens and codes, including launch codes, JSON Web Tokens (JWTs) instead of opaque tokens. This change allows apps to inspect the tokens directly without needing to call the Introspection endpoint, but it also increases the length of these tokens significantly. This feature is enabled in the fhir.epic.com sandbox. Developers should ensure that app URL handling does not truncate OAuth 2.0 tokens and codes.
  • iss: This parameter contains the authorization server's FHIR endpoint URL.

Step 2: Your Application Retrieves the Conformance Statement

To determine which authorize and token endpoints to use in the EHR launch flow, you should make a GET request to the metadata endpoint which is constructed by taking the iss provided and appending /metadata.

If no Accept header is sent, the document returns XML by default. If an Accept header is sent with a value of application/json, application/fhir+json, or application/json+fhir, the document returns JSON.

Here's an example of what a full metadata request might look like.

GET https://fhir.epic.com/interconnect-fhir-oauth/api/FHIR/DSTU2/metadata HTTP/1.1
Accept: application/fhir+json

Here is an example of what the authorize and token endpoints would look like in the metadata response.

"extension": [
  {
    "url": "http://fhir-registry.smarthealthit.org/StructureDefinition/oauth-uris",
    "extension": [
      {
        "url": "authorize",
        "valueUri": "https://fhir.epic.com/interconnect-fhir-oauth/oauth2/authorize"
      },
      {
        "url": "token",
        "valueUri": "https://fhir.epic.com/interconnect-fhir-oauth/oauth2/token"
      }
    ]
  }
]

Retrieving Conformance Statement: Additional Header Requirements

In some cases when making your request to the metadata endpoint, you'll also need to include additional headers to ensure the correct token and authorize endpoints are returned. This is because in Epic, it is possible to configure the system such that the FHIR server's endpoint URL, provided in the iss parameter, is overridden on a per client ID basis. If you do not send the Epic-Client-ID HTTP header with the appropriate production or non-production client ID associated with your application in your call to the metadata endpoint, you can receive the wrong token and authorize endpoints. Additional CORS setup might be necessary on the Epic community member's Interconnect server to allow for this header.

Here's an example of what a full metadata request might look like with the additional Epic-Client-ID header.

GET https://fhir.epic.com/interconnect-fhir-oauth/api/FHIR/DSTU2/metadata HTTP/1.1
Accept: application/fhir+json
Epic-Client-ID: 0000-0000-0000-0000-0000

Step 3: Your Application Requests an Authorization Code

Your application now has a launch token obtained from the initial EHR launch as well as the authorize endpoint obtained from the metadata query. To exchange the launch token for an authorization code, your app needs either to redirect (using HTTP GET) from the launch URL to the authorize endpoint and append the following querystring parameters, or, in Epic version November 2020 and later, make an HTTP POST request to the authorize endpoint that includes the following parameters in the body of the request.

  • response_type: This parameter must contain the value "code".
  • client_id: This parameter contains your web application's client ID issued by Epic.
  • redirect_uri: This parameter contains your application's redirect URI. After the request completes on the Epic server, this URI will be called as a callback. The value of this parameter needs to be URL encoded. This URI must also be registered with the EHR's authorization server by adding it to your app listing.
  • scope: This parameter describes the information for which the web application is requesting access. Starting with the Epic 2018 version, a scope of "launch" is required for EHR launch workflows. Starting with the November 2019 version of Epic, OpenID Connect scopes are supported, specifically the "openid" scope is supported for all apps and the "fhirUser" scope is supported by apps with the R4 (or greater) SMART on FHIR version selected.
  • launch: This parameter is required for EHR launch workflows. The value to use will be passed from the EHR.
  • state: This optional parameter is generated by your app and is opaque to the EHR. The EHR's authorization server will append it to each subsequent exchange in the workflow for you to validate session integrity. While not required, this parameter is recommended to be included and validated with each exchange in order to increase security. For more information see RFC 6819 Section 3.6.
    • Note: Large state values in combination with launch tokens that are JWTs (see above) may cause the query string for the HTTP GET request to the authorization endpoint to exceed the Epic community member web server's max query string length and cause the request to fail. You can mitigate this risk by:
      • Following the SMART App Launch Framework's recommendation that the state parameter be an unpredictable value ... with at least 122 bits of entropy (e.g., a properly configured random uuid is suitable), and not store any actual application state in the state parameter.
      • Using a POST request. If you use this approach, note that you might still need to account for a large state value in the HTTP GET redirect back to your own server.

Additional parameters for native mobile apps (available starting in the August 2019 version of Epic):

  • code_challenge: This optional parameter is generated by your app and used for PKCE. This is the S256 hashed version of the code_verifier parameter, which will be used in the token request.
  • code_challenge_method: This optional parameter indicates the method used for the code_challenge parameter and is required if using that parameter. Currently, only the S256 method is supported.

Here's an example of an authorization request using HTTP GET. You will replace the [redirect_uri], [client_id], [launch_token], [state], and [code_challenge] placeholders with your own values.

https://fhir.epic.com/interconnect-fhir-oauth/oauth2/authorize?scope=launch&response_type=code&redirect_uri=[redirect_uri]&client_id=[client_id]&launch=[launch_token]&state=[state]&code_challenge=[code_challenge]&code_challenge_method=S256

This is an example from the SMART on FHIR launchpad:

https://fhir.epic.com/interconnect-fhir-oauth/oauth2/authorize?scope=launch&response_type=code&redirect_uri=https%3A%2F%2Ffhir.epic.com%2Ftest%2Fsmart&client_id=d45049c3-3441-40ef-ab4d-b9cd86a17225&launch=GwWCqm4CCTxJaqJiROISsEsOqN7xrdixqTl2uWZhYxMAS7QbFEbtKgi7AN96fKc2kDYfaFrLi8LQivMkD0BY-942hYgGO0_6DfewP2iwH_pe6tR_-fRfiJ2WB_-1uwG0&state=abc123

Step 4: EHR's Authorization Server Reviews the Request

The EHR's authorization server reviews the request from your application. If approved, the authorization server redirects the browser to the redirect URL supplied in the initial request and appends the following querystring parameter.

  • code: This parameter contains the authorization code generated by Epic, which will be exchanged for the access token in the next step.
    • Starting in the May 2020 Epic version, Epic community members can enable a feature that makes all OAuth 2.0 tokens and codes, including authorization codes, JSON Web Tokens (JWTs) instead of opaque tokens. This change allows apps to inspect the tokens directly without needing to call the Introspection endpoint, but it also increases the length of these tokens significantly. This feature is enabled in the fhir.epic.com sandbox. Developers should ensure that app URL handling does not truncate OAuth 2.0 tokens and codes.
  • state: This parameter will have the same value as the earlier state parameter. For more information, refer to Step 3.

Here's an example of what the redirect will look like if Epic's authorization server accepts the request:

https://fhir.epic.com/test/smart?code=yfNg-rSc1t5O2p6jVAZLyY00uOOte5KM1y3YUxqsJQnBKEMNsYqOPTyVqcCH3YXaPkLztO9Rvf7bhLqQTwALHcHN6raxpTbR1eVgV2QyLA_4K0HrJO92et3qRXiXPkj7&state=abc123

Step 5: Your Application Exchanges the Authorization Code for an Access Token

After receiving the authorization code, your application trades the code for a JSON object containing an access token and contextual information by sending an HTTP POST to the token endpoint using a Content-Type header with value of "application/x-www-form-urlencoded". For more information, see RFC 6749 section 4.1.3.

Access Token Request: If You Are Not Using a Client Secret

The following parameters are required in the POST body:

  • grant_type: For the EHR launch flow, this should contain the value "authorization_code".
  • code: This parameter contains the authorization code sent from Epic's authorization server to your application as a querystring parameter on the redirect URI as described above.
  • redirect_uri: This parameter must contain the same redirect URI that you provided in the initial access request.
  • client_id: This parameter must contain the application's client ID issued by Epic that you provided in the initial request.
  • code_verifier: This optional parameter is used to verify against your code_challenge parameter when using PKCE. This parameter is passed as free text and must match the code_challenge parameter used in your authorization request once it is hashed on the server using the code_challenge_method. This parameter is available starting in the August 2019 version of Epic.

Here's an example of what an HTTP POST request for an access token might look like:

POST https://fhir.epic.com/interconnect-fhir-oauth/oauth2/token HTTP/1.1
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&code=yfNg-rSc1t5O2p6jVAZLyY00uOOte5KM1y3YUxqsJQnBKEMNsYqOPTyVqcCH3YXaPkLztO9Rvf7bhLqQTwALHcHN6raxpTbR1eVgV2QyLA_4K0HrJO92et3qRXiXPkj7&redirect_uri=https://fhir.epic.com/test/smart&client_id=d45049c3-3441-40ef-ab4d-b9cd86a17225 

The authorization server responds to the HTTP POST request with a JSON object that includes an access token. The response contains the following fields:

  • access_token: This parameter contains the access token issued by Epic to your application and is used in future requests.
    • Starting in the May 2020 Epic version, Epic community members can enable a feature that makes all OAuth 2.0 tokens and codes, including access tokens, JSON Web Tokens (JWTs) instead of opaque tokens. This change allows apps to inspect the tokens directly without needing to call the Introspection endpoint, but it also increases the length of these tokens significantly. This feature is enabled in the fhir.epic.com sandbox. Developers should ensure that app URL handling does not truncate OAuth 2.0 tokens and codes.
  • token_type: In Epic's OAuth 2.0 implementation, this parameter always includes the value bearer.
  • expires_in: This parameter contains the number of seconds for which the access token is valid.
  • scope: This parameter describes the access your application is authorized for.
  • id_token: Returned only for applications that have requested an openid scope. See below for more info on OpenID Connect id_tokens. This parameter follows the guidelines of the OpenID Connect (OIDC) Core 1.0 specification. It is signed but not encrypted.
  • patient: This parameter identifies provides the FHIR ID for the patient, if a patient is in context at time of launch.
  • epic.dstu2.patient: This parameter identifies the DSTU2 FHIR ID for the patient, if a patient is in context at time of launch.
  • encounter: This parameter identifies the FHIR ID for the patient’s encounter, if in context at time of launch.
  • location : This parameter identifies the FHIR ID for the patient’s location, if in context at time of launch.
  • state: This parameter will have the same value as the earlier state parameter. For more information, refer to Step 3.

Note that you can include additional fields in the response if needed based on the integration configuration. For more information, refer to the Launching your app topic. Here's an example of what a JSON object including an access token might look like:

{
  "access_token": "Nxfve4q3H9TKs5F5vf6kRYAZqzK7j9LHvrg1Bw7fU_07_FdV9aRzLCI1GxOn20LuO2Ahl5RkRnz-p8u1MeYWqA85T8s4Ce3LcgQqIwsTkI7wezBsMduPw_xkVtLzLU2O",
  "token_type": "bearer",
  "expires_in": 3240,
  "scope": "openid Patient.read Patient.search ",
  "id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IktleUlEIiwidHlwIjoiSldUIn0.eyJhdWQiOiJDbGllbnRJRCIsImV4cCI6RXhwaXJlc0F0LCJpYXQiOklzc3VlZEF0LCJpc3MiOiJJc3N1ZXJVUkwiLCJzdWIiOiJFbmRVc2VySWRlbnRpZmllciJ9",
  "patient": "T1wI5bk8n1YVgvWk9D05BmRV0Pi3ECImNSK8DKyKltsMB",
  "state": "abc123",
}

At this point, authorization is complete and the web application can access the protected patient data it requests using FHIR APIs.

Access Token Request: If You Are Using a Client Secret

Refresh tokens are not typically needed for embedded (SMART on FHIR) launches because users are not required to log in to Epic during the SMART on FHIR launch process, and the access token obtained from the launch process is typically valid for longer than the user needs to use the app.

Consult the Standalone Launch: Access Token Request for Refresh Token Apps for details on how to obtain an access token if your app uses refresh tokens.

OpenID Connect id_tokens

Epic started supporting the OpenID Connect id_token in the access token response for apps that request the openid scope since the November 2019 version of Epic.

A decoded OpenID Connect id_token JWT will have these headers:

Header Description
alg The JWT authentication algorithm. Currently only RSA 256 is supported in id_token JWTs so this will always be RS256.
typ This is always set to JWT.
kid The base64 encoded SHA-256 hash of the public key.

A decoded OpenID Connect id_token JWT will have this payload:

Claim Description Remarks
iss Issuer of the JWT. This is set to the token endpoint that should be used by the client. Starting in the May 2020 version of Epic, the iss will be set to the OAuth 2.0 server endpoint, e.g. https://<Interconnect Server URL>/oauth2. For Epic versions prior to May 2020, it is set to the OAuth 2.0 token endpoint, e.g. https://<Interconnect Server URL>/oauth2/token.
sub STU3+ FHIR ID for the resource representing the user launching the app. For Epic integrations, the sub and fhirUser claims reference one of the following resources depending on the type of workflow:
  • Provider/user workflows: Practitioner resource
  • MyChart self access workflows: Patient resource
  • MyChart proxy access workflows: RelatedPerson resource
Note apps with a SMART on FHIR version of DSTU2 will still get a STU3+ FHIR FHIR ID for the user. The STU3+ Practitioner.Read or RelatedPerson.Read APIs would be required to read the Practitioner or RelatedPerson FHIR IDs. Patient FHIR IDs can be used across FHIR versions in Epic's implementation.
fhirUser Absolute reference to the FHIR resource representing the user launching the app. See the HL7 documentation for more details. The fhirUser claim will only be present if app has the R4 (or greater) SMART on FHIR version selected, and requests both the openid and fhirUser scopes in the request to the authorize endpoint. See the remark for the sub claim above for more information about what the resource returned in these claims represents.
aud Audiences that the ID token is intended for. This will be set to the client ID for the application that was just authorized during the SMART on FHIR launch.
iat Time integer for when the JWT was created, expressed in seconds since the "Epoch" (1970-01-01T00:00:00Z UTC).
exp Expiration time integer for this authentication JWT, expressed in seconds since the "Epoch" (1970-01-01T00:00:00Z UTC). This is set to the current time plus 5 minutes.

Here is an example decoded id_token that could be returned if the app has the R4 (or greater) SMART on FHIR version selected, and requests both the openid and fhirUser scopes in the request to the authorize endpoint:

{
  "alg": "RS256",
  "kid": "liCulTIaitUzjfUh2AqNiMro47X9HcVcd9XPi8LDJKA=",
  "typ": "JWT"
}
{
  "aud": "de5dae1a-4317-4c25-86f1-ed558e85529b",
  "exp": 1595956317,
  "fhirUser": "https://fhir.epic.com/interconnect-fhir-oauth/oauth2/api/FHIR/R4/Practitioner/exfo6E4EXjWsnhA1OGVElgw3",
  "iat": 1595956017,
  "iss": "https://fhir.epic.com/interconnect-fhir-oauth/oauth2",
  "sub": "exfo6E4EXjWsnhA1OGVElgw3"
}

Here is an example decoded id_token that could be returned if the app requests at least the openid scope in the request to the authorize endpoint, but either doesn't request the fhirUser claim or doesn't meet the criteria outlined above for receiving the fhirUser claim:

{
  "alg": "RS256",
  "kid": "liCulTIaitUzjfUh2AqNiMro47X9HcVcd9XPi8LDJKA=",
  "typ": "JWT"
}
{
  "aud": "de5dae1a-4317-4c25-86f1-ed558e85529b",
  "exp": 1595956317,
  "iat": 1595956017,
  "iss": "https://fhir.epic.com/interconnect-fhir-oauth/oauth2",
  "sub": "exfo6E4EXjWsnhA1OGVElgw3"
}

Validating the OpenID Connect JSON Web Token

When completing the OAuth 2.0 authorization workflow with the openid scope, the JSON Web Token (JWT) returned in the id_token field will be cryptographically signed. The public key to verify the authenticity of the JWT can be found at https://<Interconnect Server URL>/api/epic/2019/Security/Open/PublicKeys/530005/OIDC, for example https://fhir.epic.com/interconnect-fhir-oauth/api/epic/2019/Security/Open/PublicKeys/530005/OIDC.

Starting in the May 2020 version of Epic, metadata about the server's OpenID Connect configuration, including the jwks_uri OIDC public key endpoint, can be found at the OpenID configuration endpoint https://<Interconnect Server URL>/oauth2/.well-known/openid-configuration, for example https://fhir.epic.com/interconnect-fhir-oauth/oauth2/.well-known/openid-configuration.

Step 6: Your Application Uses FHIR APIs to Access Patient Data

With a valid access token, your application can now access protected patient data from the EHR database using FHIR APIs. Queries must contain an Authorization header that includes the access token presented as a Bearer token.

Here's an example of what a valid query looks like:

GET https://fhir.epic.com/interconnect-fhir-oauth/api/FHIR/DSTU2/Patient/T1wI5bk8n1YVgvWk9D05BmRV0Pi3ECImNSK8DKyKltsMB HTTP/1.1
Authorization: Bearer Nxfve4q3H9TKs5F5vf6kRYAZqzK7j9LHvrg1Bw7fU_07_FdV9aRzLCI1GxOn20LuO2Ahl5RkRnz-p8u1MeYWqA85T8s4Ce3LcgQqIwsTkI7wezBsMduPw_xkVtLzLU2O

Step 7: Use a Refresh Token to Obtain a New Access Token

Refresh tokens are not typically needed for embedded (SMART on FHIR) launches because users are not required to log in to Epic during the SMART on FHIR launch process, and the access token obtained from the launch process is typically valid for longer than the user needs to use the app.

Consult the Standalone Launch: Use a Refresh Token to Obtain a New Access Token for details on how to use a refresh token to get a new an access token if your app uses refresh tokens.

Standalone Launch

Contents

  • Step 1: Your Application Requests an Authorization Code
  • Step 2: EHR's Authorization Server Authenticates the User and Authorizes Access
  • Step 3: Your Application Exchanges the Authorization Code for an Access Token
    • If You Are Not Using a Client Secret
    • If You Are Using a Client Secret
  • Step 4: Your Application Uses FHIR APIs to Access Patient Data
  • Step 5: Use a Refresh Token to Obtain a New Access Token

How It Works

Step 1: Your Application Requests an Authorization Code

Your application would like to authenticate the user using the OAuth 2.0 workflow. To initiate this process, your app needs to link (using HTTP GET) to the authorize endpoint and append the following querystring parameters, or, in Epic version November 2020 or later, make an HTTP POST request to the authorize endpoint that includes the following parameters in the body of the request.

  • response_type: This parameter must contain the value "code".
  • client_id: This parameter contains your web application's client ID issued by Epic.
  • redirect_uri: This parameter contains your application's redirect URI. After the request completes on the Epic server, this URI will be called as a callback. The value of this parameter needs to be URL encoded. This URI must also be registered with the EHR's authorization server by adding it to your app listing.
  • state: This optional parameter is generated by your app and is opaque to the EHR. The EHR's authorization server will append it to each subsequent exchange in the workflow for you to validate session integrity. While not required, this parameter is recommended to be included and validated with each exchange in order to increase security. For more information see RFC 6819 Section 3.6.
  • scope: This parameter describes the information for which the web application is requesting access. Starting with the Epic 2018 version, a scope of "launch" is required for EHR launch workflows. Starting with the November 2019 version of Epic, the "openid" and "fhirUser" OpenID Connect scopes are supported.

Additional parameters for native mobile apps (available starting in the August 2019 version of Epic):

  • code_challenge: This optional parameter is generated by your app and used for PKCE. This is the S256 hashed version of the code_verifier parameter, which will be used in the token request.
  • code_challenge_method: This optional parameter indicates the method used for the code_challenge parameter and is required if using that parameter. Currently, only the S256 method is supported.

Here's an example of an authorization request using HTTP GET. You will replace the [redirect_uri], [client_id], and [state] placeholders with your own values.

https://fhir.epic.com/interconnect-fhir-oauth/oauth2/authorize?response_type=code&redirect_uri=[redirect_uri]&client_id=[client_id]&state=[state]

This is an example link:

https://fhir.epic.com/interconnect-fhir-oauth/oauth2/authorize?response_type=code&redirect_uri=https%3A%2F%2Ffhir.epic.com%2Ftest%2Fsmart&client_id=d45049c3-3441-40ef-ab4d-b9cd86a17225&state=abc123

Step 2: EHR's Authorization Server Authenticates the User and Authorizes Access

The EHR's authorization server reviews the request from your application, authenticates the user (sample credentials found here), and authorizes access If approved, the authorization server redirects the browser to the redirect URL supplied in the initial request and appends the following querystring parameter.

  • code: This parameter contains the authorization code generated by Epic, which will be exchanged for the access token in the next step.
    • Starting in the May 2020 Epic version, Epic community members can enable a feature that makes all OAuth 2.0 tokens and codes, including authorization codes, JSON Web Tokens (JWTs) instead of opaque tokens. This change allows apps to inspect the tokens directly without needing to call the Introspection endpoint, but it also increases the length of these tokens significantly. This feature is enabled in the fhir.epic.com sandbox. Developers should ensure that app URL handling does not truncate OAuth 2.0 tokens and codes.
  • state: This parameter will have the same value as the earlier state parameter. For more information, refer to Step 1.

Here's an example of what the redirect will look like if Epic's authorization server accepts the request:

https://fhir.epic.com/test/smart?code=yfNg-rSc1t5O2p6jVAZLyY00uOOte5KM1y3YUxqsJQnBKEMNsYqOPTyVqcCH3YXaPkLztO9Rvf7bhLqQTwALHcHN6raxpTbR1eVgV2QyLA_4K0HrJO92et3qRXiXPkj7&state=abc123

Step 3: Your Application Exchanges the Authorization Code for an Access Token

After receiving the authorization code, your application trades the code for a JSON object containing an access token and contextual information by sending an HTTP POST to the token endpoint using a Content-Type header with value of "application/x-www-form-urlencoded". For more information, see RFC 6749 section 4.1.3.

Access Token Request: If You Are Not Using a Client Secret

The following parameters are required in the POST body:

  • grant_type: For the EHR launch flow, this should contain the value "authorization_code".
  • code: This parameter contains the authorization code sent from Epic's authorization server to your application as a querystring parameter on the redirect URI as described above.
  • redirect_uri: This parameter must contain the same redirect URI that you provided in the initial access request.
  • client_id: This parameter must contain the application's client ID issued by Epic that you provided in the initial request.

Here's an example of what an HTTP POST request for an access token might look like:

POST https://fhir.epic.com/interconnect-fhir-oauth/oauth2/token HTTP/1.1
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&code=yfNg-rSc1t5O2p6jVAZLyY00uOOte5KM1y3YUxqsJQnBKEMNsYqOPTyVqcCH3YXaPkLztO9Rvf7bhLqQTwALHcHN6raxpTbR1eVgV2QyLA_4K0HrJO92et3qRXiXPkj7&redirect_uri=https://fhir.epic.com/test/smart&client_id=d45049c3-3441-40ef-ab4d-b9cd86a17225 

The authorization server responds to the HTTP POST request with a JSON object that includes an access token. The response contains the following fields:

  • access_token: This parameter contains the access token issued by Epic to your application and is used in future requests.
    • Starting in the May 2020 Epic version, Epic community members can enable a feature that makes all OAuth 2.0 tokens and codes, including access tokens, JSON Web Tokens (JWTs) instead of opaque tokens. This change allows apps to inspect the tokens directly without needing to call the Introspection endpoint, but it also increases the length of these tokens significantly. This feature is enabled in the fhir.epic.com sandbox. Developers should ensure that app URL handling does not truncate OAuth 2.0 tokens and codes.
  • token_type: In Epic's OAuth 2.0 implementation, this parameter always includes the value bearer.
  • expires_in: This parameter contains the number of seconds for which the access token is valid.
  • scope: This parameter describes the access your application is authorized for.
  • id_token: Returned only for applications that have requested an openid scope. See above for more info on OpenID Connect id_tokens. This parameter follows the guidelines described earlier in this document.
  • patient: For patient-facing workflows, this parameter identifies the FHIR ID for the patient on whose behalf authorization to the system was granted.
    • The patient's FHIR ID is not returned for provider-facing standalone launch workflows.
  • epic.dstu2.patient: For patient-facing workflows, this parameter identifies the DSTU2 FHIR ID for the patient on whose behalf authorization to the system was granted.
    • The patient's FHIR ID is not returned for provider-facing standalone launch workflows.

Note that you can pass additional parameters if needed based on the integration configuration. Here's an example of what a JSON object including an access token might look like:

{
  "access_token": "Nxfve4q3H9TKs5F5vf6kRYAZqzK7j9LHvrg1Bw7fU_07_FdV9aRzLCI1GxOn20LuO2Ahl5RkRnz-p8u1MeYWqA85T8s4Ce3LcgQqIwsTkI7wezBsMduPw_xkVtLzLU2O",
  "token_type": "bearer",
  "expires_in": 3240,
  "scope": "Patient.read Patient.search ",
  "patient": "T1wI5bk8n1YVgvWk9D05BmRV0Pi3ECImNSK8DKyKltsMB"
}

At this point, authorization is complete and the web application can access the protected patient data it requested using FHIR APIs.

Access Token Request: If You Are Using a Client Secret:

After receiving the authorization code, your application trades the code for a JSON object containing an access token and contextual information by sending an HTTP POST to the token endpoint using a Content-Type header with value of "application/x-www-form-urlencoded". For more information, see RFC 6749 section 4.1.3.

The Epic on FHIR website can generate a client secret (effectively a password) for your app to use to obtain refresh tokens, and store the hashed secret for you, or Epic community members can upload a client secret hash that you provide them when they activate your app for their system. If you provide community members a client secret hash to upload, you should use a unique client secret per Epic community member and per environment type (non-production and production) for each Epic community member.

The following parameters are required in the POST body:

  • grant_type: This should contain the value authorization_code.
  • code: This parameter contains the authorization code sent from Epic's authorization server to your application as a querystring parameter on the redirect URI as described above.
  • redirect_uri: This parameter must contain the same redirect URI that you provided in the initial access request.

Note: The client_id parameter is not passed in the the POST body if you use client secret authentication, which is different from the access token request for apps that do not use refresh tokens. You will instead pass an Authorization HTTP header with client_id and client_secret URL encoded and passed as a username and password. Conceptually the Authorization HTTP header will have this value: base64(client_id:client_secret).

For example, using the following client_id and client_secret:

client_id: d45049c3-3441-40ef-ab4d-b9cd86a17225

URL encoded client_id: d45049c3-3441-40ef-ab4d-b9cd86a17225 Note: base64 encoding Epic's client IDs will have no effect

client_secret: this-is-the-secret-2/7

URL encoded client_secret: this-is-the-secret-2%2F7

Would result in this Authorization header:

Authorization: Basic base64Encode{d45049c3-3441-40ef-ab4d-b9cd86a17225:this-is-the-secret-2%2F7}

or

Authorization: Basic ZDQ1MDQ5YzMtMzQ0MS00MGVmLWFiNGQtYjljZDg2YTE3MjI1OnRoaXMtaXMtdGhlLXNlY3JldC0yJTJGNw==

Here's an example of what a valid HTTP POST might look like:

POST https://fhir.epic.com/interconnect-fhir-oauth/oauth2/token HTTP/1.1
Content-Type: application/x-www-form-urlencoded 
Authorization: Basic ZDQ1MDQ5YzMtMzQ0MS00MGVmLWFiNGQtYjljZDg2YTE3MjI1OnRoaXMtaXMtdGhlLXNlY3JldC0yJTJGNw==

grant_type=authorization_code&code=yfNg-rSc1t5O2p6jVAZLyY00uOOte5KM1y3YUxqsJQnBKEMNsYqOPTyVqcCH3YXaPkLztO9Rvf7bhLqQTwALHcHN6raxpTbR1eVgV2QyLA_4K0HrJO92et3qRXiXPkj7&redirect_uri=https://fhir.epic.com/test/smart

The authorization server responds to the HTTP POST request with a JSON object that includes an access token and a refresh token. The response contains the following fields:

  • refresh_token: This parameter contains the refresh token issued by Epic to your application and can be used to obtain a new access token. For more information on how this works, see Step 5.
    • Starting in the May 2020 Epic version, Epic community members can enable a feature that makes all OAuth 2.0 tokens and codes, including refresh tokens, JSON Web Tokens (JWTs) instead of opaque tokens. This change allows apps to inspect the tokens directly without needing to call the Introspection endpoint, but it also increases the length of these tokens significantly. This feature is enabled in the fhir.epic.com sandbox. Developers should ensure that app URL handling does not truncate OAuth 2.0 tokens and codes.
  • access_token: This parameter contains the access token issued by Epic to your application and is used in future requests.
    • Starting in the May 2020 Epic version, Epic community members can enable a feature that makes all OAuth 2.0 tokens and codes, including access tokens, JSON Web Tokens (JWTs) instead of opaque tokens. This change allows apps to inspect the tokens directly without needing to call the Introspection endpoint, but it also increases the length of these tokens significantly.
  • token_type: In Epic's OAuth 2.0 implementation, this parameter always includes the value bearer.
  • expires_in: This parameter contains the number of seconds for which the access token is valid.
  • scope: This parameter describes the access your application is authorized for.
  • id_token: Returned only for applications that have requested an openid scope. See above for more info on OpenID Connect id_tokens. This parameter follows the guidelines of the OpenID Connect (OIDC) Core 1.0 specification. It is signed but not encrypted.
  • patient: For patient-facing workflows, this parameter identifies the FHIR ID for the patient on whose behalf authorization to the system was granted.
    • The patient's FHIR ID is not returned for provider-facing standalone launch workflows.
  • epic.dstu2.patient: For patient-facing workflows, this parameter identifies the DSTU2 FHIR ID for the patient on whose behalf authorization to the system was granted.
    • The patient's FHIR ID is not returned for provider-facing standalone launch workflows.
  • encounter: This parameter identifies the FHIR ID for the patient’s encounter, if in context at time of launch.
    • The encounter FHIR ID is not returned for standalone launch workflows.
  • location: This parameter identifies the FHIR ID for the patient’s location, if in context at time of launch.
    • The location FHIR ID is not returned for standalone launch workflows.

Note that you can pass additional parameters if needed based on the integration configuration. Here's an example of what a JSON object including an access token and refres token might look like:

{
  "access_token": "Nxfve4q3H9TKs5F5vf6kRYAZqzK7j9LHvrg1Bw7fU_07_FdV9aRzLCI1GxOn20LuO2Ahl5RkRnz-p8u1MeYWqA85T8s4Ce3LcgQqIwsTkI7wezBsMduPw_xkVtLzLU2O",
  "refresh_token": "H9TKs5F5vf6kRYAZqzK7j9L_07_FdV9aRzLCI1GxOn20LuO2Ahl5R1MeYWqA85T8s4sTkI7wezBsMduPw_xkLzLU2O",
  "token_type": "bearer",
  "expires_in": 3240,
  "scope": "Patient.read Patient.search ",
  "patient": "T1wI5bk8n1YVgvWk9D05BmRV0Pi3ECImNSK8DKyKltsMB"
}

At this point, authorization is complete and the web application can access the protected patient data it requested using FHIR APIs.

Step 4: Your Application Uses FHIR APIs to Access Patient Data

With a valid access token, your application can now access protected patient data from the EHR database using FHIR APIs. Queries must contain an Authorization header that includes the access token presented as a Bearer token.

Here's an example of what a valid query looks like:

GET https://fhir.epic.com/interconnect-fhir-oauth/api/FHIR/DSTU2/Patient/T1wI5bk8n1YVgvWk9D05BmRV0Pi3ECImNSK8DKyKltsMB HTTP/1.1
Authorization: Bearer Nxfve4q3H9TKs5F5vf6kRYAZqzK7j9LHvrg1Bw7fU_07_FdV9aRzLCI1GxOn20LuO2Ahl5RkRnz-p8u1MeYWqA85T8s4Ce3LcgQqIwsTkI7wezBsMduPw_xkVtLzLU2O

Step 5: Use a Refresh Token to Obtain a New Access Token

If your app uses refresh tokens (i.e. it can securely store credentials), then you can use a refresh token to request a new access token when the current access token expires (determined by the expires_in field from the authorization response from step 3).

Your application trades the refresh_token for a JSON object containing a new access token, (optionally) a new refresh token, and contextual information by sending an HTTP POST to the token endpoint using a Content-Type HTTP header with value of "application/x-www-form-urlencoded". For more information, see RFC 6749 section 4.1.3.

The Epic on FHIR website can generate a client secret (effectively a password) for your app when using refresh tokens, and store the hashed secret for you, or Epic community members can upload a client secret hash that you provide them when they activate your app for their system. If you provide community members a client secret hash to upload, you should use a unique client secret per Epic community member and per environment type (non-production and production) for each Epic community member.

The following parameters are required in the POST body:

  • grant_type: This parameter always contains the value refresh_token.
  • refresh_token: The refresh token received from a prior authorization request.

An Authorization header using HTTP Basic Authentication is required, where the username is the URL encoded client_id and the password is the URL encoded client_secret.

For example, using the following client_id and client_secret:

client_id: d45049c3-3441-40ef-ab4d-b9cd86a17225

URL encoded client_id: d45049c3-3441-40ef-ab4d-b9cd86a17225 Note: base64 encoding Epic's client IDs will have no effect

client_secret: this-is-the-secret-2/7

URL encoded client_secret: this-is-the-secret-2%2F7

Would result in this Authorization header:

Authorization: Basic base64Encode{d45049c3-3441-40ef-ab4d-b9cd86a17225:this-is-the-secret-2%2F7}

or

Authorization: Basic ZDQ1MDQ5YzMtMzQ0MS00MGVmLWFiNGQtYjljZDg2YTE3MjI1OnRoaXMtaXMtdGhlLXNlY3JldC0yJTJGNw==

Here's an example of what a valid HTTP POST might look like:

POST https://fhir.epic.com/interconnect-fhir-oauth/oauth2/token HTTP/1.1
Authorization: Basic ZDQ1MDQ5YzMtMzQ0MS00MGVmLWFiNGQtYjljZDg2YTE3MjI1OnRoaXMtaXMtdGhlLXNlY3JldC0yJTJGNw==
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token&refresh_token=j12xcniournlsdf234bgsd

The authorization server responds to the HTTP POST request with a JSON object that includes the new access token, and optionally a new refresh token. The response contains the following fields:

  • access_token: This parameter contains the new access token issued.
  • token_type: In Epic's OAuth 2.0 implementation, this parameter always includes the value bearer.
  • expires_in: This parameter contains the number of seconds for which the access token is valid.
  • scope: This parameter describes the access your application is authorized for.
  • refresh_token: (Optional) New refresh token issued, which replaces the previous one.

An example response to the previous request may look like the following:

{
  "access_token": "57CjhZEdiTcCh1nqIwQUw5rODOLP3bSTnMEGNYbBerSeNn8hIUm6Mlc5ruCTfQawjRAoR8sYr8S_7vNdnJfgRKfD6s8mOqPnvJ8vZOHjEvy7l3Ra9frDaEAUBbN-j86k",
  "token_type": "bearer",
  "expires_in": 3240,
  "scope": "Patient.read Patient.search ",
  "refresh_token": "b72foiua9asdhnkjanvm"
}

SMART Backend Services (Backend OAuth 2.0)

Contents

  • Building a Backend OAuth 2.0 App
  • Complete Required Epic Community Member Setup to Audit Access from Your Backend Application
  • Creating a Public Private Key Pair for JWT Signature
    • OpenSSL
    • Windows PowerShell
    • Finding the Public Key Certificate Fingerprint (Also Called Thumbprint)
  • Using a JWT to Obtain an Access Token for a Backend Service
    • Step 1: Creating the JWT
    • Step 2: POSTing the JWT to Token Endpoint to Obtain Access Token

Overview

Backend apps (i.e. apps without direct end user or patient interaction) can also use OAuth 2.0 authentication through the client_credentials OAuth 2.0 grant type. Epic's OAuth 2.0 implementation for backend services follows the SMART Backend Services: Authorization Guide, though it currently differs from that profile in some respects. Application vendors pre-register a public key for a given Epic community member on the Epic on FHIR website and then use the corresponding private key to sign a JSON Web Token (JWT) which is presented to the authorization server to obtain an access token.

Building a Backend OAuth 2.0 App

To use the client_credentials OAuth 2.0 grant type to authorize your backend application's access to patient information, two pieces of information need to be shared between the authorization server and your application:

  1. Client ID: The client ID identifies your application to authentication servers within the Epic community and allows you to connect to any organization.
  2. Public key: The public key is used to validate your signed JSON Web Token to confirm your identity.

You can register your application for access to both the sandbox and Epic organizations here. You'll provide information to us, including a public key used to verify the JWT signature, and Epic will generate a client ID for you. Your app will need to have the Backend Systems radio button selected in order to register a public key for backend OAuth 2.0.

Complete Required Epic Community Member Setup to Audit Access from Your Backend Application

An Epic community member's Epic Client Systems Administrator (ECSA) will need to map your client ID to an Epic user account that will be used for auditing web service calls made by your backend application. Your app will not be able to obtain an access token until this build is completed.

Note: This build is not needed in the sandbox. The sandbox automatically maps your client to a default user, removing the need for this setup.

Creating a Public Private Key Pair for JWT Signature

There are several tools you can use to create a key pair. As long as you can export your public key to a base64 encoded X.509 certificate for registration on the Epic on FHIR website, the tool you use to create the key pair and file format used to store the keys is not important. If your app is hosted by Epic community members ("on prem" hosting) instead of cloud hosted you should have unique key pairs for each Epic community member you integrate with. In addition, you should always use unique key pairs for non-production and production systems.

Here are examples of two tools commonly used to generate key pairs:

Creating a Public Private Key Pair: OpenSSL

You can create a new private key named privatekey.pem using OpenSSL with the following command:

openssl genrsa -out /path_to_key/privatekey.pem 2048

Make sure the key length is at least 2048 bits.

Then you can export the public key to a base64 encoded X.509 certificate named publickey509.pem using this command:

openssl req -new -x509 -key /path_to_key/privatekey.pem -out /path_to_key/publickey509.pem -subj '/CN=myapp'

Where '/CN=myapp' is the subject name (for example the app name) the key pair is for. The subject name does not have a functional impact in this case but it is required for creating an X.509 certificate.

Creating a Public Private Key Pair: Windows PowerShell

You can create a key pair using Windows PowerShell with this command, making sure to run PowerShell as an administrator:

New-SelfSignedCertificate -Subject "MyApp"

Note that the Epic on FHIR website is extracting the public key from the certificate and discarding the rest of the certificate information, so it's not 'trusting' the self-signed certificate per se.

The subject name does not have a functional impact in this case but it is required for creating an X.509 certificate.

You can export the public key using either PowerShell or Microsoft Management Console.

If you want to export the public key using PowerShell, take note of the certificate thumbprint and storage location printed when you executed the New-SelfSignedCertificate command.

PS C:\dir> New-SelfSignedCertificate -Subject "MyApp"

PSParentPath: Microsoft.PowerShell.Security\Certificate::LocalMachine\MY

Thumbprint                                Subject
----------                                -------
3C4A558635D67F631E5E4BFDF28AE59B2E4421BA  CN=MyApp

Then export the public key to a X.509 certificate using the following commands (using the thumbprint and certificate location from the above example):

PS C:\dir> $cert = Get-ChildItem -Path Cert:\LocalMachine\My\3C4A558635D67F631E5E4BFDF28AE59B2E4421BA

PS C:\dir> Export-Certificate -Cert $cert -FilePath newpubkeypowerShell.cer

Export the binary certificate to a base64 encoded certificate:

PS C:\dir> certutil.exe -encode newpubkeypowerShell.cer newpubkeybase64.cer

If you want to export the public key using Microsoft Management Console, follow these steps:

  1. Run (Windows + R) > mmc (Microsoft Management Console).
  2. Go to File > Add/Remove SnapIn.
  3. Choose Certificates > Computer Account > Local Computer. The exact location here depends on the certificate store that was used to create and store the keys when you ran the New-SelfSignedCertificate PowerShell command above. The store used is displayed in the printout after the command completes.
  4. Then back in the main program screen, go to Personal > Certificates and find the key pair you just created in step 1. The "Issue To" column will equal the value passed to -Subject during key creation (e.g. "MyApp" above).
  5. Right click on the certificate > All Tasks > Export.
  6. Choose option to not export the private key.
  7. Choose to export in base-64 encoded X.509 (.CER) format.
  8. Choose a file location.

Finding the Public Key Certificate Fingerprint (Also Called Thumbprint)

The public key certificate fingerprint (also known as thumbprint in Windows software) is displayed for JWT signing public key certificates that are uploaded to the Epic on FHIR website. There are a few ways you can find the fingerprint for a public key certificate:

  • If you created the public key certificate using Windows PowerShell using the steps above, the thumbprint was displayed after completing the command. You can also print public keys and their thumbprints for a given certificate storage location using the Get-ChildItem PowerShell command.

    For example, run Get-ChildItem -Path Cert:\LocalMachine\My to find all certificate thumbprints in the local machine storage.

  • You can follow the steps here to find the thumbprint of a certificate in Microsoft Management Console.
  • You can run this OpenSSL command to print the public key certificate fingerprint that would be displayed on the Epic on FHIR website, replacing openssl_publickey.cert with the name of your public key certificate:

    $ openssl x509 -noout -fingerprint -sha1 -inform pem -in openssl_publickey.cert

    Note that the output from this command includes colons between bytes which are not shown on the Epic on FHIR website.

Using a JWT to Obtain an Access Token for a Backend Service

You will generate a one-time use JSON Web Token (JWT) to authenticate your app to the authorization server and obtain an access token that can be used to authenticate your app's web service calls. There are several libraries for creating JWTs. See jwt.io for some examples.

Step 1: Creating the JWT

The JWT should have these headers:

Header Description
alg The JWT authentication algorithm. Currently only RSA signing algorithms are supported so RSA 384 should be used and this should be set to RS384.
typ This should always be set to JWT.

The JWT header should be formatted as follows:

{
  "alg": "RS384",
  "typ": "JWT"
}

The JWT should have these claims in the payload:

Claim Description Remarks
iss Issuer of the JWT. This is the app's client_id, as determined during registration on the Epic on FHIR website. This is the same as the value for the sub claim.
sub The service's client_id, as determined during registration on the Epic on FHIR website. This is the same as the value for the iss claim.
aud The FHIR authorization server's token endpoint URL. This is the same URL to which this authentication JWT will be posted. See below for an example POST. It's possible that Epic community member systems will route web service traffic through a proxy server, in which case the URL the JWT is posted to is not known to the authorization server, and the JWT will be rejected. For such cases, Epic community member administrators can add additional audience URLs to the allowlist, in addition to the FHIR server token URL if needed.
jti A unique identifier for the JWT. The jti must be no longer than 151 characters and cannot be reused during the JWT's validity period, i.e. before the exp time is reached.
exp Expiration time integer for this authentication JWT, expressed in seconds since the "Epoch" (1970-01-01T00:00:00Z UTC). The exp value must be in the future, and can be no more than 5 minutes in the future at the time the access token request is received.
nbf Time integer before which the JWT must not be accepted for processing, expressed in seconds since the "Epoch" (1970-01-01T00:00:00Z UTC). The nbf value cannot be in the future, cannot be more recent than the exp value, and the exp - nbf difference cannot be greater than 5 minutes.
iat Time integer for when the JWT was created, expressed in seconds since the "Epoch" (1970-01-01T00:00:00Z UTC). The iat value cannot be in the future, and the exp - iat difference cannot be greater than 5 minutes.

Here's an example JWT payload:

{
  "iss": "d45049c3-3441-40ef-ab4d-b9cd86a17225",
  "sub": "d45049c3-3441-40ef-ab4d-b9cd86a17225",
  "aud": "https://fhir.epic.com/interconnect-fhir-oauth/oauth2/token",
  "jti": "f9eaafba-2e49-11ea-8880-5ce0c5aee679",
  "exp": 1583524402,
  "nbf": 1583524102,
  "iat": 1583524102
}

The header and payload are then base64 URL encoded, combined with a period separating them, and cryptographically signed using the private key to generate a signature. Conceptually:

signature = RSA-SHA384(base64urlEncoding(header) + '.' + base64urlEncoding(payload), privatekey)

The full JWT is constructed by combining the header, body and signature as follows:

base64urlEncoding(header) + '.' + base64urlEncoding(payload) + '.' + base64urlEncoding(signature)

A fully constructed JWT looks something like this:

eyJhbGciOiJSUzM4NCIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJkNDUwNDljMy0zNDQxLTQwZWYtYWI0ZC1iOWNkODZhMTcyMjUiLCJzdWIiOiJkNDUwNDljMy0zNDQxLTQwZWYtYWI0ZC1iOWNkODZhMTcyMjUiLCJhdWQiOiJodHRwczovL2ZoaXIuZXBpYy5jb20vaW50ZXJjb25uZWN0LWZoaXItb2F1dGgvb2F1dGgyL3Rva2VuIiwianRpIjoiZjllYWFmYmEtMmU0OS0xMWVhLTg4ODAtNWNlMGM1YWVlNjc5IiwiZXhwIjoxNTgzNTI0NDAyLCJuYmYiOjE1ODM1MjQxMDIsImlhdCI6MTU4MzUyNDEwMn0.dztrzHo9RRwNRaB32QxYLaa9CcIMoOePRCbpqsRKgyJmBOGb9acnEZARaCzRDGQrXccAQ9-syuxz5QRMHda0S3VbqM2KX0VRh9GfqG3WJBizp11Lzvc2qiUPr9i9CqjtqiwAbkME40tioIJMC6DKvxxjuS-St5pZbSHR-kjn3ex2iwUJgPbCfv8cJmt19dHctixFR6OG-YB6lFXXpNP8XnL7g85yLOYoQcwofN0k8qK8h4uh8axTPC21fv21mCt50gx59XgKsszysZnMDt8OG_G4gjk_8JnGHwOVkJhqj5oeg_GdmBhQ4UPuxt3YvCOTW9S2vMikNUnxrhdVvn2GVg

Step 2: POSTing the JWT to Token Endpoint to Obtain Access Token

Your application makes a HTTP POST request to the authorization server's OAuth 2.0 token endpoint to obtain access token. The following form-urlencoded parameters are required in the POST body:

  • grant_type: This should be set to client_credentials.
  • client_assertion_type: This should be set to urn:ietf:params:oauth:client-assertion-type:jwt-bearer.
  • client_assertion: This should be set to the JWT you created above.

Here is an example request:

POST https://fhir.epic.com/interconnect-fhir-oauth/oauth2/token HTTP/1.1
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer&client_assertion=eyJhbGciOiJSUzM4NCIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJkNDUwNDljMy0zNDQxLTQwZWYtYWI0ZC1iOWNkODZhMTcyMjUiLCJzdWIiOiJkNDUwNDljMy0zNDQxLTQwZWYtYWI0ZC1iOWNkODZhMTcyMjUiLCJhdWQiOiJodHRwczovL2ZoaXIuZXBpYy5jb20vaW50ZXJjb25uZWN0LWZoaXItb2F1dGgvb2F1dGgyL3Rva2VuIiwianRpIjoiZjllYWFmYmEtMmU0OS0xMWVhLTg4ODAtNWNlMGM1YWVlNjc5IiwiZXhwIjoxNTgzNTI0NDAyLCJuYmYiOjE1ODM1MjQxMDIsImlhdCI6MTU4MzUyNDEwMn0.dztrzHo9RRwNRaB32QxYLaa9CcIMoOePRCbpqsRKgyJmBOGb9acnEZARaCzRDGQrXccAQ9-syuxz5QRMHda0S3VbqM2KX0VRh9GfqG3WJBizp11Lzvc2qiUPr9i9CqjtqiwAbkME40tioIJMC6DKvxxjuS-St5pZbSHR-kjn3ex2iwUJgPbCfv8cJmt19dHctixFR6OG-YB6lFXXpNP8XnL7g85yLOYoQcwofN0k8qK8h4uh8axTPC21fv21mCt50gx59XgKsszysZnMDt8OG_G4gjk_8JnGHwOVkJhqj5oeg_GdmBhQ4UPuxt3YvCOTW9S2vMikNUnxrhdVvn2GVg

And here is an example response body assuming the authorization server approves the request:

{
  "access_token": "i82fGhXNxmidCt0OdjYttm2x0cOKU1ZbN6Y_-zBvt2kw3xn-MY3gY4lOXPee6iKPw3JncYBT1Y-kdPpBYl-lsmUlA4x5dUVC1qbjEi1OHfe_Oa-VRUAeabnMLjYgKI7b",
  "token_type": "bearer",
  "expires_in": 3600,
  "scope": "Patient.read Patient.search"
}
  • Note: Starting in the May 2020 Epic version, Epic community members can enable a feature that makes all OAuth 2.0 tokens and codes, including access tokens, JSON Web Tokens (JWTs) instead of opaque tokens. This change allows apps to inspect the tokens directly without needing to call the Introspection endpoint, but it also increases the length of these tokens significantly. This feature is enabled in the fhir.epic.com sandbox. Developers should ensure that app URL handling does not truncate OAuth 2.0 tokens and codes.

Non-OAuth 2.0 Authentication

Epic supports forms of authentication in addition to OAuth 2.0. Only use these forms of authentication if OAuth 2.0 is not an option.

HTTP Basic Authentication

HTTP Basic Authentication requires that Epic community members provision a username and password that your application will provide to the web server to authenticate every request.

Epic supports HTTP Basic Authentication via a system-level user record created within Epic's database. You may hear this referred to as an EMP record. You will need to work with an organization's Epic user security team to have a username and password provided to you.

When calling Epic's web services with HTTP Basic Authentication the username must be provided as follows: emp$<username>. Replace <username> with the username provided by the organization during implementation of your application.

Base64 encode your application's credentials and pass them in the HTTP Authorization header. For example, if you've been given a system-level username/password combination that is username/Pa$$w0rd1, the Authorization header would have a value of ZW1wJHVzZXJuYW1lOlBhJCR3MHJkMQ== (the base64 encoded version of emp$username:Pa$$w0rd1):

GET http://localhost:8888/website HTTP/1.1
Host: localhost:8888
Proxy-Connection: keep-alive
Authorization: Basic ZW1wJHVzZXJuYW1lOlBhJCR3MHJkMQ==

Storing HTTP Basic Authentication Values

The username and password you use to authenticate your web service requests are extremely sensitive since they can be used to pull PHI out of an Epic organization's system. The credentials should always be encrypted and should not be stored directly within your client's code. You should make sure that access to decrypt the credentials should be limited to only the users that need access to it. For example, if a service account submits the web service requests, only that service account should be able to decrypt the credentials.

Client Certificates and SAML tokens

Epic also supports the use of Client Certificates and SAML tokens as an authentication mechanism for server-to-server web service requests. We do not prefer these methods because both require web server administrators to maintain a trusted certificate for authentication. As certificates expire or servers need to be moved around, this puts an additional burden on system administrators.

Additional Required Headers

When you use OAuth 2.0 authentication, Epic can automatically gather important information about the client making a web service request. When you use a non-OAuth 2.0 authentication mechanism, we require that this additional information be passed in an HTTP header.

Epic-Client-ID

This is the client ID you were given upon your app's creation on the Epic on FHIR site. This is always required when calling Epic's web services if your app doesn't use OAuth 2.0 authentication.

GET http://localhost:8888/website HTTP/1.1
Host: localhost:8888
Proxy-Connection: keep-alive
Authorization: Basic ZW1wJHVzZXJuYW1lOlBhJCR3MHJkMQ==
Epic-Client-ID: 0000-0000-0000-0000-0000

Epic-User-ID and Epic-User-IDType

This is required for auditing requests to FHIR resources. The Epic-User-ID corresponds to an EMP (Epic User) account that an organization creates for your application. This might be required depending on the web services you are calling. The Epic-User-IDType is almost always EXTERNAL.

GET http://localhost:8888/website HTTP/1.1
Host: localhost:8888
Proxy-Connection: keep-alive
Authorization: Basic ZW1wJHVzZXJuYW1lOlBhJCR3MHJkMQ==
Epic-Client-ID: 0000-0000-0000-0000-0000					 	
Epic-User-ID: username					 	
Epic-User-IDType: EXTERNAL

FHIR Tutorial

FHIR is all about quickly getting data about a patient from a clinical system, and FHIR is all interconnected — you can build on top of your queries to get a wide variety of data. The topics below include a walkthrough introduction to FHIR data and sample patient and clinical information - everything you need to turn up the heat on your first FHIR App!

Let's walk through a couple of examples using jQuery — in a larger application, you might build your own framework, or use an existing one, to interact with FHIR, but FHIR is easy enough to use without any special libraries. This tutorial assumes you are passing a form of authorization covered in one of our authentication guides. It's important to note FHIR APIs support OAuth 2.0 and additionally HTTP Basic Authentication.

Interacting with the API

To get started, let's define the FHIR server base URL. This is a constant address that all FHIR API endpoints for the server's default FHIR version live under. In a SMART on FHIR launch, this is specified with the iss parameter. We'll store this variable as a separate entity, so we can reference it across our queries:

    
var baseUrl = "https://fhir.epic.com/interconnect-fhir-oauth/api/FHIR/DSTU2"
    

You can expect the bold part of the above URL to be different for each unique instance of Epic you integrate with.

Finding Patients via Demographic Search

With the base URL defined, we can start building some simple queries and investigating the API. Because we're interacting with patient data, there's an obvious place to get started: finding the patient whose data we want to show. Epic's FHIR support provides two methods for applications to find and interact with patients:

  1. The Epic application can provide a patient to you, determined from the context or its patient selection activity. This method can apply to both clinician and patient-facing workflows
  2. Our Patient API provides the means to search for patients using demographic information such as their first and last name. We use this method when your application is independent from Epic.
    
var patientSearchString = "/Patient?given=Derrick&family=Lin&birthdate=1973-06-03"
    

We can use jQuery's $.getJSON() function to call into the URL and retrieve our data in JSON format. It takes a parameter describing the URL to query, and a callback for doing something with the data once the API responds. In our case, let's just log the data to the console so we can inspect it before developing code that works with it:

    
$.getJSON(baseUrl + patientSearchString, function(data,error) { console.log(data); });
    

The complete request URL at this point will read:

    
"https://fhir.epic.com/interconnect-fhir-oauth/api/FHIR/DSTU2/Patient?family=Lin&given=Derrick&birthdate=1973-06-03"
    

If you execute the above request, you'll be able to see the following response in your console:

{
  "resourceType": "Bundle",
  "type": "searchset",
  "total": 1,
  "link": [
    {
      "relation": "self",
      "url": "https://fhir.epic.com/interconnect-fhir-oauth/api/FHIR/DSTU2/Patient?family=Lin&given=Derrick&birthdate=1973-06-03"
    }
  ],
  "entry": [
    {
      "link": [
        {
          "relation": "self",
          "url": "https://fhir.epic.com/interconnect-fhir-oauth/api/FHIR/DSTU2/Patient/TGsj9rYGI8SV-LYYkdH1JqbxYuFGfrODXSPC6iLLYkJcB"
        }
      ],
      "fullUrl": "https://fhir.epic.com/interconnect-fhir-oauth/api/FHIR/DSTU2/Patient/TGsj9rYGI8SV-LYYkdH1JqbxYuFGfrODXSPC6iLLYkJcB",
      "resource": {
        "resourceType": "Patient",
        "id": "TGsj9rYGI8SV-LYYkdH1JqbxYuFGfrODXSPC6iLLYkJcB",
        "extension": [
          {
            "url": "http://hl7.org/fhir/StructureDefinition/us-core-race",
            "valueCodeableConcept": {
              "coding": [
                {
                  "system": "urn:oid:2.16.840.1.113883.5.104",
                  "code": "2028-9",
                  "display": "Asian"
                }
              ],
              "text": "Asian"
            }
          },
          {
            "url": "http://hl7.org/fhir/StructureDefinition/us-core-ethnicity",
            "valueCodeableConcept": {
              "coding": [
                {
                  "system": "urn:oid:2.16.840.1.113883.5.50",
                  "code": "UNK",
                  "display": "Unknown"
                }
              ],
              "text": "Unknown"
            }
          },
          {
            "url": "http://hl7.org/fhir/StructureDefinition/us-core-birth-sex",
            "valueCodeableConcept": {
              "coding": [
                {
                  "system": "http://hl7.org/fhir/v3/AdministrativeGender",
                  "code": "M",
                  "display": "Male"
                }
              ],
              "text": "Male"
            }
          }
        ],
        "identifier": [
          {
            "use": "usual",
            "system": "urn:oid:1.2.840.114350.1.13.0.1.7.5.737384.0",
            "value": "E4005"
          },
          {
            "use": "usual",
            "system": "urn:oid:1.2.840.114350.1.13.0.1.7.5.737384.14",
            "value": "203711"
          },
          {
            "extension": [
              {
                "url": "http://hl7.org/fhir/StructureDefinition/rendered-value",
                "valueString": "xxx-xx-0000"
              }
            ],
            "use": "usual",
            "system": "urn:oid:2.16.840.1.113883.4.1"
          }
        ],
        "active": true,
        "name": [
          {
            "use": "usual",
            "text": "Derrick Lin",
            "family": [
              "Lin"
            ],
            "given": [
              "Derrick"
            ]
          }
        ],
        "telecom": [
          {
            "system": "phone",
            "value": "785-555-5555",
            "use": "home"
          },
          {
            "system": "phone",
            "value": "785-666-6666",
            "use": "work"
          }
        ],
        "gender": "male",
        "birthDate": "1973-06-03",
        "deceasedBoolean": false,
        "address": [
          {
            "use": "home",
            "line": [
              "7324 Roosevelt Ave"
            ],
            "city": "INDIANAPOLIS",
            "state": "IN",
            "postalCode": "46201",
            "country": "US",
            "period": {
              "start": "2019-05-24T00:00:00Z"
            }
          }
        ],
        "maritalStatus": {
          "text": "Married"
        },
        "communication": [
          {
            "language": {
              "coding": [
                {
                  "system": "urn:oid:2.16.840.1.113883.6.99",
                  "code": "en",
                  "display": "English"
                }
              ],
              "text": "English"
            },
            "preferred": true
          }
        ]
      },
      "search": {
        "mode": "match"
      }
    }
  ]
}

Let's pick out a couple of key elements to learn more about:

  1. The total element tells us how many elements were found in the search:

    • a total of 0 elements tells us that no results were found in the search. This may mean that you didn't provide enough demographic data to make a match, or that the API doesn't know about the patient you're looking for.
    • a total of 1 element tells us that exactly one patient was found. A single patient returned in a search likely means that you've found the right person, but you should always check the demographic information to confirm.
    • a total of more than 1 element tells us that the demographic information wasn't sufficient to match to a single patient. If this happens, you can examine the response to match against known demographic data, or perform a second query against the API with additional demographic information.

    Ideally, you provide enough patient information in your initial search for the API to return a single patient — it's much safer to confirm a single patient than to compare a list of possible results, and it reduces the complexity on your app's users to select a single patient rather than review a list.

    In our case, we found exactly one patient, Derrick — exactly the patient we were looking for. If we were building a more complex app, we might need to handle other scenarios gracefully, such as when no patients are found.

    The following response is an example of what to expect if a patient match is not found. Note the text contained within the OperationOutcome resource:

    {
      "resourceType": "Bundle",
      "type": "searchset",
      "total": 0,
      "link": [
        {
          "relation": "self",
          "url": "https://fhir.epic.com/interconnect-fhir-oauth/api/FHIR/DSTU2/Patient?given=Derrick"
        }
      ],
      "entry": [
        {
          "resource": {
            "resourceType": "OperationOutcome",
            "issue": [
              {
                "severity": "warning",
                "code": "informational",
                "details": {
                  "coding": [
                    {
                      "system": "urn:oid:1.2.840.114350.1.13.0.1.7.2.657369",
                      "code": "4101",
                      "display": "Resource request returns no results."
                    }
                  ],
                  "text": "Resource request returns no results."
                }
              }
            ]
          },
          "search": {
            "mode": "outcome"
          }
        }
      ]
    }
    
    

  2. The entry[n].link[n].url element points to the permanent location of a unique patient. If we make a request to this URL, we will always get back that patient's data. Storing this URL can be useful if you want to save off a list of recently used patients, for example. The last part of the url in our search example is Derrick's FHIR ID. We'll use this to make all of our other queries to FHIR APIs.

  3. The entry[0].resource element contains all of the actual data about Derrick Lin. Within this object, you'll find information about Derrick such as his address, phone number, and birth date.

That's a quick overview of the patient search endpoint, but if you're interested in learning more about our Patient search, you can read more here. Let's continue on by saving off Derrick's FHIR ID so we can refer to it later:

    
var patientId = data.entry[0].link[0].url.split("/").pop();
    

Interlude: Relative Resource URLs (R4 and Later)

There are several versions of the FHIR standard. You might use DSTU2 FHIR resources (like in the example above), STU3 resources, or more recent versions of FHIR resources, such as R4 resources. A variety of R4 resources are supported starting in the August 2019 version of Epic.

For R4 and later versions of FHIR, Epic uses relative URLs for all referenced FHIR resources included in a query response. For example, if we look back at the sample response from our Patient query above, if that query was an R4 version Patient query, instead of returning an absolute reference URL for the careProvider:

"careProvider": [
  {
    "display": "Physician Family Medicine, MD",
    "reference": "https://fhir.epic.com/interconnect-fhir-oauth/api/FHIR/DSTU2/Practitioner/Thh97FZ9lU9.p-Rgpozo6vwB"
  }
]

An R4 resource would return a relative URL instead:

"careProvider": [
  {
    "display": "Physician Family Medicine, MD",
    "reference": "Practitioner/Thh97FZ9lU9.p-Rgpozo6vwB"
  }
]

This distinction is important to keep in mind, particularly if you’re working with some FHIR resources that use the DSTU2 or STU3 standard, and other resources that use the R4 standard. Make sure to append the baseURL to the front of the relative URL when querying referenced FHIR resources.

Searching for Clinical Data by Patient ID

So we've found our patient, and we have some demographics that we could parse and display to an end user. But how do we get access to clinical information about that patient? To retrieve this data, we need to use another endpoint within our API, along with the patient ID we saved off above.

Let's start with a simple query — retrieving a patient's diagnoses. The Condition endpoint exposes exactly that, and provides a search endpoint to find diagnoses by Patient ID. We can construct a query that provides Derrick's FHIR ID to grab all of the active diagnoses recorded in our system:

    
var conditionSearchString = "/Condition?patient=" + patientId;
    

We'll use the same method as we did with the Patient search to investigate the response returned from the Condition API, and log the response body to the console as a simple means to view the data.

    
$.getJSON(baseUrl + conditionSearchString, function(data,error) { console.log(data); });
    

Executing the above request returns the following:

{
  "resourceType": "Bundle",
  "type": "searchset",
  "total": 1,
  "link": [
    {
      "relation": "self",
      "url": "https://fhir.epic.com/interconnect-fhir-oauth/api/FHIR/DSTU2/Condition?patient=TGsj9rYGI8SV-LYYkdH1JqbxYuFGfrODXSPC6iLLYkJcB&category=diagnosis"
    }
  ],
  "entry": [
    {
      "link": [
        {
          "relation": "self",
          "url": "https://fhir.epic.com/interconnect-fhir-oauth/api/FHIR/DSTU2/Condition/TH12jkQYrabXcEh-2vXMxLAB"
        }
      ],
      "fullUrl": "https://fhir.epic.com/interconnect-fhir-oauth/api/FHIR/DSTU2/Condition/TH12jkQYrabXcEh-2vXMxLAB",
      "resource": {
        "resourceType": "Condition",
        "id": "TH12jkQYrabXcEh-2vXMxLAB",
        "patient": {
          "display": "Derrick Lin",
          "reference": "https://fhir.epic.com/interconnect-fhir-oauth/api/FHIR/DSTU2/Patient/TGsj9rYGI8SV-LYYkdH1JqbxYuFGfrODXSPC6iLLYkJcB"
        },
        "asserter": {
          "display": "Physician Family Medicine, MD",
          "reference": "https://fhir.epic.com/interconnect-fhir-oauth/api/FHIR/DSTU2/Practitioner/Thh97FZ9lU9.p-Rgpozo6vwB"
        },
        "dateRecorded": "2019-05-28",
        "code": {
          "coding": [
            {
              "system": "http://hl7.org/fhir/sid/icd-9-cm/diagnosis",
              "code": "V49.89",
              "display": "Risk for coronary artery disease between 10% and 20% in next 10 years"
            },
            {
              "system": "urn:oid:2.16.840.1.113883.6.90",
              "code": "Z91.89",
              "display": "Risk for coronary artery disease between 10% and 20% in next 10 years"
            },
            {
              "system": "http://snomed.info/sct",
              "code": "315016007",
              "display": "At risk of coronary heart disease (finding)"
            }
          ],
          "text": "Risk for coronary artery disease between 10% and 20% in next 10 years"
        },
        "category": {
          "coding": [
            {
              "system": "http://loinc.org",
              "code": "29308-4",
              "display": "Diagnosis"
            },
            {
              "system": "http://snomed.info/sct",
              "code": "439401001",
              "display": "Diagnosis"
            },
            {
              "system": "http://hl7.org/fhir/condition-category",
              "code": "diagnosis",
              "display": "Diagnosis"
            },
            {
              "system": "http://argonautwiki.hl7.org/extension-codes",
              "code": "problem",
              "display": "Problem"
            }
          ],
          "text": "Diagnosis"
        },
        "clinicalStatus": "active",
        "verificationStatus": "confirmed",
        "onsetDateTime": "2019-05-28"
      },
      "search": {
        "mode": "match"
      }
    }
  ]
}

Looking at the response, there are a couple of key elements to highlight to help us understand the condition information:

  1. As with Patient, the total element lets you know the number of diagnoses returned for a query. In this case, it lets us know how many diagnoses our system has documented for Derrick Lin.

  2. The entry[n].link[n].url element points to the permanent location of a unique Condition resource. If we make a request to this URL, we will always get back data for this specific diagnosis for the patient. The last part of the url in our search example is the FHIR ID for this Condition. The FHIR ID for this resource can also be retrieved directly from entry[n].resource[n].id. This might be useful for future reference to check whether the patient has any new conditions documented.

    • For an R4 query, the entry[n].link[n].url element value in this example would be shortened to Condition/TH12jkQYrabXcEh-2vXMxLAB. All other resources included in this response, such as the Patient and Practitioner resources, would also use relative URLs. Refer back to the Interlude: Relative Resource URLs (R4 and Later) topic for a summary of this difference.
  3. The entry[n].resource[n].patient element describes the patient the diagnosis relates to. This allows us to confirm the association between diagnosis and patient, and shows how FHIR works by linking these smaller resources to paint a larger picture. In this case, the element in each diagnosis entry points to our patient, Derrick.

  4. The entry[n].resource[n].asserter element describes the clinician who recorded the diagnosis in the system. A link to the provider resource is included to allow you to search additional information about the provider who recorded this diagnosis.

  5. The onsetDateTime tells you when the condition was first noticed.

  6. The recordedDate element tells you when the diagnosis was actually made and recorded.

That's a lot of information with a simple query, and it demonstrates how quickly we can build up and associate the different endpoints available on our API.

Wrap Up

Nice work! You made it to the end of our walkthrough, and you understand how to find patients, and start working with their data. There's even more to discover in our documentation, like additional data types, and more complex searches.


Advanced Topics

In this section we'll cover a few advanced topics that may come up in your implementation of FHIR.

Content negotiation in FHIR

Epic’s FHIR server defaults to using XML for FHIR resources, but many apps may prefer sending and receiving JSON instead. FHIR allows both XML and JSON MIME-types and specifies how a client can request one or the other. Per the FHIR spec and in Epic’s FHIR server, a client can specify XML or JSON through either the _format query parameter or by specifying the MIME-type in an HTTP header. A client should use the Accept HTTP header to specify the MIME-type of the content that it wishes to receive from the server and the Content-Type HTTP header to specify the MIME-type of the content that it is sending in the body of its request, for example, as part of a create or update.

Epic supports all of the following MIME-types for FHIR interactions:

  • application/fhir+json
  • application/json+fhir
  • application/xml+fhir
  • application/fhir+xml

Using STU3 Resources in a DSTU2 Environment

You might come across a FHIR API that you want to use that is only available in STU3, but the base URL for the environment returns a DSTU2 endpoint. This means that you will have a patient's DSTU2 FHIR ID but an STU3 resource requires an STU3 FHIR ID. As of the November 2018 version, Patient FHIR resources are FHIR ID version agnostic and you can use the Patient DSTU2 ID with the Patient STU3 resource and vice versa. As of the May 2019 version, all FHIR resources are Patient FHIR ID version agnostic and you can use the Patient DSTU2 ID with all STU3 resources and vice versa. You can pass in PatientID and PatientIDType to return a collection of different identifiers unique to a patient using the Patient.Read FHIR (STU3) resource. This can be used to get different Patient FHIR version IDs or to get an Epic-specific ID from a FHIR ID or vice versa. To start, we need to construct a search string for this resource.

Using the provided URL template for this web service, we can create a search string. Note, this can be a DSTU2 ID for the STU3 resource in November 2018+:

    
var baseUrl = "https://fhir.epic.com/interconnect-fhir-oauth/api/FHIR/STU3"
var identifierSearchString = "/Patient/" + patientId;
    

We can use jQuery's $.getJSON() function to call into the URL and retrieve our data in JSON format. It takes a parameter describing the URL to query, and a callback for doing something with the data once the API responds. In our case, let's just log the data to the console so we can inspect it before developing code that works with it:

    
$.getJSON(baseUrl + identifierSearchString, function(data,error) { console.log(data); });
    

The response contains a number of identifiers for the patient, most notably Derrick's FHIR STU3 ID, in the identifier element:

{
  "resourceType": "Patient",
  "id": "eq081-VQEgP8drUUqCWzHfw3",
  "extension": [
    {
      "valueCode": "M",
      "url": "http://hl7.org/fhir/us/core/StructureDefinition/us-core-birthsex"
    },
    {
      "extension": [
        {
          "valueCoding": {
            "system": "http://hl7.org/fhir/us/core/ValueSet/omb-race-category",
            "code": "2028-9",
            "display": "Asian"
          },
          "url": "ombCategory"
        },
        {
          "valueString": "Asian",
          "url": "text"
        }
      ],
      "url": "http://hl7.org/fhir/us/core/StructureDefinition/us-core-race"
    },
    {
      "extension": [
        {
          "valueCoding": {
            "system": "http://hl7.org/fhir/us/core/ValueSet/omb-ethnicity-category",
            "code": "UNK",
            "display": "Unknown"
          },
          "url": "ombCategory"
        },
        {
          "valueString": "Unknown",
          "url": "text"
        }
      ],
      "url": "http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity"
    }
  ],
  "identifier": [
    {
      "use": "usual",
      "type": {
        "text": "EPIC"
      },
      "system": "urn:oid:1.2.840.114350.1.13.0.1.7.5.737384.0",
      "value": "E4005"
    },
    {
      "use": "usual",
      "type": {
        "text": "EXTERNAL"
      },
      "value": "Z6127"
    },
    {
      "use": "usual",
      "type": {
        "text": "FHIR"
      },
      "value": "TGsj9rYGI8SV-LYYkdH1JqbxYuFGfrODXSPC6iLLYkJcB"
    },
    {
      "use": "usual",
      "type": {
        "text": "FHIR STU3"
      },
      "value": "eq081-VQEgP8drUUqCWzHfw3"
    },
    {
      "use": "usual",
      "type": {
        "text": "INTERNAL"
      },
      "value": "     Z6127"
    },
    {
      "use": "usual",
      "type": {
        "text": "EPI"
      },
      "system": "urn:oid:1.2.840.114350.1.13.0.1.7.5.737384.14",
      "value": "203711"
    },
    {
      "extension": [
        {
          "valueString": "xxx-xx-0000",
          "url": "http://hl7.org/fhir/StructureDefinition/rendered-value"
        }
      ],
      "use": "usual",
      "system": "urn:oid:2.16.840.1.113883.4.1"
    }
  ],
  "active": true,
  "name": [
    {
      "use": "usual",
      "text": "Derrick Lin",
      "family": "Lin",
      "given": [
        "Derrick"
      ]
    }
  ],
  "telecom": [
    {
      "system": "phone",
      "value": "785-555-5555",
      "use": "home"
    },
    {
      "system": "phone",
      "value": "785-666-6666",
      "use": "work"
    }
  ],
  "gender": "male",
  "birthDate": "1973-06-03",
  "deceasedBoolean": false,
  "address": [
    {
      "use": "home",
      "line": [
        "7324 Roosevelt Ave"
      ],
      "city": "INDIANAPOLIS",
      "district": "MARION",
      "state": "IN",
      "postalCode": "46201",
      "country": "US",
      "period": {
        "start": "2019-05-24"
      }
    }
  ],
  "maritalStatus": {
    "text": "Married"
  },
  "communication": [
    {
      "language": {
        "coding": [
          {
            "system": "http://hl7.org/fhir/ValueSet/languages",
            "code": "en",
            "display": "English"
          }
        ],
        "text": "English"
      },
      "preferred": true
    }
  ],
  "managingOrganization": {
    "reference": "https://fhir.epic.com/interconnect-fhir-oauth/api/FHIR/STU3/Organization/enRyWnSP963FYDpoks4NHOA3",
    "display": "Epic Hospital System"
  }
}

You can now save off Derrick's STU3 FHIR ID taking advantage of Jquery's $.grep() function:

    
var result = $.grep(data, function(x) { return x.type[n].text == "FHIR STU3"; });
var patientIdSTU3 = result[0].value  //We’ll only ever return one FHIR STU3 ID
    

Now that you have the STU3 ID, you're free to call into STU3 resources. Remember to change your base URL to reference STU3. Here's an example of the same Condition search we did earlier but now using Derrick's STU3 FHIR ID.

var completeSearchString = "https://fhir.epic.com/interconnect-fhir-oauth/api/FHIR/STU3/Condition?patient=" + patientIdSTU3 + "&clinical-status=active"

Of note, one difference you might notice between DSTU2 and STU3 resources is that paging is only supported in STU3 resources. DSTU2 responses come back in one request.

Search Parameters

FHIR Search Parameters

This document describes the FHIR Search result parameters currently available, what they can do, and how to use each of these parameters with Epic FHIR APIs.

Search result parameters modify how results are returned in a Search API’s response. See the table below for the search result parameters that are supported by Epic and the versions in which they are supported.


Parameter Supported in DSTU2? Supported in STU3? Supported in R4?
_id Yes Yes Yes
_count Yes, for the Observation resource only Yes Yes
_include No Yes Yes
_revInclude No No Yes

  

_id Parameter

The _id parameter is used to indicate a FHIR ID, allowing read-type functionality in a search FHIR API.

So why would you want to use _id in a search API instead of a read API? Well, what if you wanted to perform a read with a FHIR search parameter? The _id parameter allows you to do just that, by using FHIR search parameters while still reading data about a single resource.

Examples:

GET https://hostname/instance/api/FHIR/STU3/Patient?_id=eYg3-1aJmCMq-umIIq2Njxw3&_include=Patient:generalPractitioner:Practitioner

_count Parameter

The _count parameter is used to limit the number of results returned by a search request. Once the number of results specified has been reached, the URL to the next page of results will be generated and included in the response. This URL contains a session ID which returns the next group of _count results. This is useful for resources that could return a large number of results (such as Observation), as using _count can minimize the demand on the server.  _count can be set to any number from 1 to 999.

Important note on resources that return many results:

If your search returns more than 1000 results, the search will automatically be broken into pages with 1000 results each, regardless of whether or not you use the _count parameter. This means that your app needs to support this functionality to ensure that you receive all applicable data associated with the API request.

The API response when using the _count parameter behaves differently for STU3 and later requests, versus DSTU2 requests, as described below.

Examples:

For STU3 and above, look for the link.relation parameter. If link.relation is "next", then use the request in link.url to obtain the next set of resources. Continue until link.relation is "self".

Sample Request

GET https://apporchard.epic.com/interconnect-aocurprd-username/api/FHIR/STU3/Observation?patient=e63wRTbPfr1p8UW81d8Seiw3&category=vital-signs&_count=10

Sample Response

{ 
   "resourceType": "Bundle",
    "type": "searchset",
    "total": 10,
    "link": [
        {
            "relation": "next",
            "url": "https://apporchard.epic.com/interconnect-aocurprd-oauth/api/FHIR/STU3/Observation?patient=e63wRTbPfr1p8UW81d8Seiw3&category=vital-signs&_count=10&sessionID=16-BAF392808B2B11EA92E00050568B7BE6"
        }
...{response continues}

_include Parameter

The _include parameter is used to return resources related to the initial search result. Any other resource included as a reference can be returned immediately following the search result. This reduces the number of requests needed to see information. For example, when searching for a patient's encounters, you can include the encounter providers as well.

Epic supports the _include parameter primarily for FHIR search interactions. In addition, it is supported for particular operation types, like Appointment.$find.

For this parameter to work, the Search response must include a reference to another FHIR resource.

General format: _include = [resource you’re searching]:[name of reference element you want included]: [type of resource if element can refer to multiple (optional)]

  • [resource you’re searching] – this is the initial API you are calling
    • Ex: When calling Observation.Search, you are searching the Observation resource.
  • [name of reference element you want included] – one of the response parameters for that API that has a type of “Reference”
  • [type of resource if element can refer to multiple] – when the second parameter could refer to multiple resources, the third parameter specifies which resource to include

Examples:

  • Immunization.Search
    • Parameter in response: Practitioner.actor (Reference)
    • From this, the second parameter would be “actor”
    • _include=Immunization:actor:Practitioner
    • Note: there is only one actor for Immunizations so the third parameter is optional (_include=Immunization:actor would also work here)
  • Appointment $find (STU3)
    • Parameters in response: Participant.actor (Reference(Patient|Practitioner|Location))
    • From this, the second parameter would be "actor" just like in the first example
    • Unlike the first example, you need to specify a third parameter as there are three potential actors referenced. Any of the following examples are valid here:
      • _include=Appointment:actor:Patient
      • _include=Appointment:actor:Practitioner
      • _include=Appointment:actor:Location

_revInclude Parameter

The _revInclude parameter (reverse include) is used to return a particular resource, along with other resources that refer to that resource. Epic currently supports using this parameter to fetch provenance data for Search interactions with any FHIR resources that are part of the U.S. Core Data for Interoperability data classes and elements. This parameter is supported in Epic version February 2020 or later (special updates are required to use this feature on the February 2020, May 2020, or August 2020 versions).

For this parameter to return results, the FHIR resource specified in the Search response must be referenced by the resource specified in the _revInclude parameter.

General format: _revInclude=Provenance:target

The response will have the following format:

Element Type Cardinality Description
target Reference (resource) 1..* The resource this provenance supports
recorded Instant 1..1 Timestamp that the activity was recorded/updated
agent Backbone Element 1..* Actor involved
agent.type CodeableConcept 0..1 How the agent participated
agent.who Reference (Practitioner | Patient | Organization) 1..1 Who participated
agent.onBehalfOf Reference (Organization) 0..1 Who was the target representing

Examples:

Sample Request

https://www.example.org/api/FHIR/R4/DocumentReference?id=eohnrt4398hwojhn3&_revInclude=Provenance:target

Sample Response

{
    "link": [{
        "relation": "self",
        "url": "https://www.example.org/api/FHIR/R4/Provenance/eC7YEH-XJufbPDHTDFWShEk2b9Ki98xTRyLbhFXwQ6sfcjhyfmeem2nFqdxZYHcS0FhROyzJUToN.o1UE0DQeXQ3"
    }],
    "fullUrl": "https://www.example.org/api/FHIR/R4/Provenance/eC7YEH-XJufbPDHTDFWShEk2b9Ki98xTRyLbhFXwQ6sfcjhyfmeem2nFqdxZYHcS0FhROyzJUToN.o1UE0DQeXQ3",
    "resource": {
        "resourceType": "Provenance",
        "id": "eC7YEH-XJufbPDHTDFWShEk2b9Ki98xTRyLbhFXwQ6sfcjhyfmeem2nFqdxZYHcS0FhROyzJUToN.o1UE0DQeXQ3",
        "target": [{"reference": "DocumentReference/eiuHBlNpiGxJwXRUWVqHjN9QaKO0ODrkAJrvUByRLuMc3"}],
        "recorded": "2020-06-23T07:33:12Z",
        "agent": [{
            "type": {
                "coding": [{
                    "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type",
                    "code": "author",
                    "display": "Author"
                }],
                "text": "Author"
            },
            "who": {
                "reference": "Practitioner/eIOnxJsdFcfgJ4iy716I3MA3",
                "display": "Starter Provider, MD"
            },
            "onBehalfOf": {"display": "Example Organization"}
        },
        {
            "type": {
                "coding": [{
                    "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type",
                    "code": "transmitter",
                    "display": "Transmitter"
                }],
                "text": "Transmitter"
            },
            "who": {
                "display": "Example Organization"
            }
        }]
    },
    "search": {"mode": "include"}
}

Sandbox Test Data

These test users can be used to authenticate when testing Provider-Facing Standalone OAuth 2.0 workflows with the FHIR sandbox.

Sandbox URI - https://fhir.epic.com/interconnect-fhir-oauth/
FHIR Endpoints - See 'FHIR API Endpoints' on open.epic

User

User Login

User Password

FHIR, USER

FHIR

EpicFhir11!

These are some of the test patients that exist in the Epic on FHIR sandbox.

Sandbox URI - https://fhir.epic.com/interconnect-fhir-oauth/
FHIR Endpoints - See 'FHIR API Endpoints' on open.epic

Patient

FHIR Identifier

Applicable Resources

Camila Lopez

FHIR: erXuFYUfucBZaryVksYEcMg3

External: Z6129

MRN: 203713

MyChart Login Username: fhircamila

MyChart Login Password: epicepic1

  • DiagnosticReport
  • Medication
  • MedicationOrder
  • MedicationRequest
  • MedicationStatement
  • Observation (Labs)
  • Patient
  • Procedure

Derrick Lin

FHIR: eq081-VQEgP8drUUqCWzHfw3

External: Z6127

MRN: 203711

MyChart Login Username: fhirderrick

MyChart Login Password: epicepic1

  • CarePlan
  • Condition
  • Goal
  • Medication
  • MedicationOrder
  • MedicationRequest
  • MedicationStatement
  • Observation (Smoking History)
  • Patient

Desiree Powell

FHIR: eAB3mDIBBcyUKviyzrxsnAw3

External: Z6130

MRN: 203714

MyChart Login Username: fhirdesiree

MyChart Login Password: epicepic1

  • Immunization
  • Observation (Vitals)
  • Patient

Elijah Davis

FHIR: egqBHVfQlt4Bw3XGXoxVxHg3

External: Z6125

MRN: 203709

  • AllergyIntollerance
  • Binary
  • Condition
  • DocumentReference
  • Medication
  • MedicationOrder
  • MedicationRequest
  • MedicationStatement
  • Observation (Smoking History)
  • Patient

Linda Ross

FHIR: eh2xYHuzl9nkSFVvV3osUHg3

External: Z6128

MRN: 203712

  • Condition
  • Medication
  • MedicationOrder
  • MedicationRequest
  • MedicationStatement
  • Observation (Vitals)
  • Patient

Olivia Roberts

FHIR: eh2xYHuzl9nkSFVvV3osUHg3

External: Z6131

MRN: 203715

  • Binary
  • Condition
  • Device
  • DocumentReference
  • Patient

Warren McGinnis

FHIR: e0w0LEDCYtfckT6N.CkJKCw3

External: Z6126

MRN: 203710

  • AllergyIntollerance
  • Binary
  • Condition
  • DiagnosticReport
  • DocumentReference
  • Observation (Labs)
  • Observation (Vitals)
  • Patient
  • Procedure

If you're using the legacy DSTU2 sandbox for testing, here are a few patients you can check out.

Sandbox URI - https://open-ic.epic.com/argonaut/
FHIR Endpoints - See 'FHIR API Endpoints' on open.epic

Patient

Account Details

Applicable Resources

Jessica Argonaut

FHIR: TUKRxL29bxE9lyAcdTIyrWC6Ln5gZ-z7CLr2r-2SY964B

MyChart Login Username: fhirjessica

MyChart Login Password: n/a

Note: This account is only enabled as a proxy account

  • Patient
  • AllergyIntollerance
  • MedicationOrder
  • MedicationStatement
  • Condition
  • DiagnosticReport
  • DocumentReference
  • Observation

Jason Argonaut

FHIR: Tbt3KuCY0B5PSrJvCu2j-PlK.aiHsu2xUjUM8bWpetXoB

MyChart Login Username: fhirjason

MyChart Login Password: epicepic1

Note: This account has proxy access to fhirjessica

  • Patient
  • AllergyIntollerance
  • MedicationOrder
  • MedicationStatement
  • Condition
  • DiagnosticReport
  • Immunization
  • CarePlan
  • Goal
  • Procedure
  • DocumentReference
  • Observation

Daisy Tinsley

FHIR: TbsWqA46u3BtlZT2KSkx5cZOT1o0mGbI3VPAGXv6XcdEB

MyChart Login Username: fhirdaisy

MyChart Login Password: epicepic1

  • Patient
  • AllergyIntollerance
  • MedicationOrder
  • MedicationStatement
  • Condition
  • DiagnosticReport
  • Immunization
  • CarePlan
  • Procedure
  • DocumentReference
  • Observation

Patient-Facing Apps Using FHIR

FHIR API Response Variations in Patient-Facing Applications

Some FHIR APIs may return different data when the application is used in a patient-facing context. This typically occurs because data that should not be visible to a patient is filtered out of FHIR API responses in patient-facing applications. Below are some examples of possible differences in the results returned by a FHIR API:

  • FHIR APIs will only return results relevant to the authenticated patient. If one patient, Susan, is logged in to the application, the Observation (Labs) API won’t return lab results for a patient other than Susan.
  • FHIR APIs may not return patient-entered data that has not yet been reviewed and reconciled by a clinician.
  • FHIR APIs may return patient friendly names for resources, such as medications. For example, a patient-facing app may return the medication name pseudoephedrine instead of PSEUDOPHEDRINE HCL 30 MG PO TABS.

Community members may choose to disable this filtering functionality for their Epic instance, resulting in near identical behavior between patient-facing FHIR API responses and the response of apps with other audiences.

Epic recommends that you thoroughly test each API at each Community Member’s site prior to a go-live in production to ensure that you understand the behavior of each API for that community member’s Epic deployment.

Automatic Client ID Distribution

Automatic Client ID Distribution: USCDI Apps

Client IDs for USCDI apps will automatically download to a community member’s Epic instances when all of the following conditions are met:

  • The application:
    • Is created through Epic on FHIR
    • Uses only FHIR APIs (of any supported version) listed within the USCDI core data set
    • Does not write any data into Epic
    • Is patient-facing
    • Does not use refresh tokens OR uses refresh tokens and has a client secret uploaded by the vendor for that community member
    • Is marked "Ready for Production" and was marked ready after Sept. 3rd, 2020
      • Apps can be marked "Ready for Sandbox Use" to test with our Epic on FHIR environment prior to marking the app "Ready for Production"
      • We recommend that any apps marked "Ready for Production" will be tested with a customer's non-production environment before going live
  • The Community Member:
    • Has signed the open.epic API Subscription Agreement
    • Has not disabled auto-download functionality

Automatic Client ID Distribution: CCDS (MU3) Apps

Client IDs for CCDS apps will automatically download to a community member’s Epic instances when all of the following conditions are met:

  • The application:
    • Is created through Epic on FHIR
    • Uses only DSTU2 FHIR APIs †
    • Does not write any data into Epic
    • Is patient-facing
    • Does not use refresh tokens
    • Is marked "Ready for Production" and was marked ready after Dec. 17th, 2020
      • Apps can be marked "Ready for Sandbox Use" to test with our Epic on FHIR environment prior to marking the app "Ready for Production"
      • We recommend that any apps marked "Ready for Production" will be tested with a customer's non-production environment before going live
  • The Community Member:
    • Need not sign the open.epic API Subscription Agreement

Manual Client ID Distribution

Any applications that do not meet all of the above criteria will need to be manually downloaded to the community member's Epic instances. Steps to do so can be found in the App Creation and Request Process guide.

MU3 Auto-Download Remarks

† Only the following DSTU2 FHIR APIs qualify for MU3 auto-download.

  • AllergyIntolerance.Read
  • AllergyIntolerance.Search
  • Binary.Read
  • CarePlan.Read
  • CarePlan.Search
  • Condition.Read
  • Condition.Search
  • Device.Read
  • Device.Search
  • DiagnosticReport.Read
  • DiagnosticReport.Search
  • DocumentReference.Read
  • DocumentReference.Search
  • Goal.Read
  • Goal.Search
  • Immunization.Read
  • Immunization.Search
  • Medication.Read
  • Medication.Search
  • MedicationOrder.Read
  • MedicationOrder.Search
  • MedicationStatement.Read
  • MedicationStatement.Search
  • Observation.Read
  • Observation.Search
  • Patient.Read
  • Patient.Search
  • Practitioner.Read
  • Practitioner.Search
  • Procedure.Read
  • Procedure.Search

We use cookies from Google Analytics to improve our website. By accepting, you will receive these cookies from Epic on FHIR. Decline if you wish to use Epic on FHIR without these cookies. Read our privacy policy here.

Frequently Asked Questions (FAQs)
Contact
Terms of Use
Privacy Policy
1979 Milky Way, Verona, WI 53593
© 1979-2021 Epic Systems Corporation. All rights reserved. Protected by U.S. patents. For details visit www.epic.com/patents.
HL7®, FHIR® and the FLAME mark are the registered trademarks of HL7 and are used with the permission of HL7. The use of these trademarks does not constitute a product endorsement by HL7.

Due to inactivity you will be logged out in 60 seconds.

Saving
Loading
False