*
*
*
*
*
*
Loading...
That document wasn't found. It may have moved or require you to log in to view.
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.
Epic recommends apps be well-behaved, adhering to the following principles:
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.
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.
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.
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.
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)
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.
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
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."↩
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:
A more in-depth version of this guide can be accessed by signing in.
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:
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.
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.
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.
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.
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:
Epic community members should do the following prior to the request process:
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.
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.
To be able to download the client IDs, the following steps must have been completed:
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.
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.
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.
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.
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:
client_id
: The client_id
identifies your application to authentication servers within the Epic community and allows you to connect to any organization.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_uri
s. redirect_uri
is not needed for backend services using the client_credentials grant type.
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_uri
s, 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.
Standalone launch: The app launches directly to the authorize endpoint outside of an EHR session and requests context from the EHR's authorization server.
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.
Desktop integrations through Subspace: The app requests access to APIs directly available on the EHR’s desktop application via a local HTTP server.
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:
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/
Contents
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.
iss
: This parameter contains the authorization server's FHIR endpoint URL.
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"
}
]
}
]
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
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.
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: 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.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
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.
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
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 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.
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.
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.
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:
|
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" }
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.
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
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.
Contents
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
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.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
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 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.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. 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. 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.
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.access_token
: This parameter contains the access token issued by Epic to your application and is used in future requests.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. 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. 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. 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.
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
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"
}
Contents
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.
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:
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.
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.
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:
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.
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:
New-SelfSignedCertificate
PowerShell command above. The store used is displayed in the printout after the command completes.
-Subject
during key creation (e.g. "MyApp" above).
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:
Get-ChildItem
PowerShell command. For example, run Get-ChildItem -Path Cert:\LocalMachine\My
to find all certificate thumbprints in the local machine storage.
$ 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.
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.
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
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"
}
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==
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.
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.
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.
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
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 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.
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.
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:
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:
The total
element tells us how many elements were found in the search:
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.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.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" } } ] }
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.
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();
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.
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:
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.
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.
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.
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.
The onsetDateTime
tells you when the condition was first noticed.
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.
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.
In this section we'll cover a few advanced topics that may come up in your implementation of 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:
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.
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 |
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.
GET https://hostname/instance/api/FHIR/STU3/Patient?_id=eYg3-1aJmCMq-umIIq2Njxw3&_include=Patient:generalPractitioner:Practitioner
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.
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"
.
GET https://apporchard.epic.com/interconnect-aocurprd-username/api/FHIR/STU3/Observation?patient=e63wRTbPfr1p8UW81d8Seiw3&category=vital-signs&_count=10
{ "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}
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)]
_include=Immunization:actor:Practitioner
_include=Immunization:actor
would also work here)_include=Appointment:actor:Patient
_include=Appointment:actor:Practitioner
_include=Appointment:actor:Location
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 |
https://www.example.org/api/FHIR/R4/DocumentReference?id=eohnrt4398hwojhn3&_revInclude=Provenance:target
{ "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"} }
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 |
|
Derrick Lin |
FHIR: eq081-VQEgP8drUUqCWzHfw3 External: Z6127 MRN: 203711 MyChart Login Username: fhirderrick MyChart Login Password: epicepic1 |
|
Desiree Powell |
FHIR: eAB3mDIBBcyUKviyzrxsnAw3 External: Z6130 MRN: 203714 MyChart Login Username: fhirdesiree MyChart Login Password: epicepic1 |
|
Elijah Davis |
FHIR: egqBHVfQlt4Bw3XGXoxVxHg3 External: Z6125 MRN: 203709 |
|
Linda Ross |
FHIR: eh2xYHuzl9nkSFVvV3osUHg3 External: Z6128 MRN: 203712 |
|
Olivia Roberts |
FHIR: eh2xYHuzl9nkSFVvV3osUHg3 External: Z6131 MRN: 203715 |
|
Warren McGinnis |
FHIR: e0w0LEDCYtfckT6N.CkJKCw3 External: Z6126 MRN: 203710 |
|
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 |
|
Jason Argonaut |
FHIR: Tbt3KuCY0B5PSrJvCu2j-PlK.aiHsu2xUjUM8bWpetXoB MyChart Login Username: fhirjason MyChart Login Password: epicepic1 Note: This account has proxy access to fhirjessica |
|
Daisy Tinsley |
FHIR: TbsWqA46u3BtlZT2KSkx5cZOT1o0mGbI3VPAGXv6XcdEB MyChart Login Username: fhirdaisy MyChart Login Password: epicepic1 |
|
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:
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.
Client IDs for USCDI apps will automatically download to a community member’s Epic instances when all of the following conditions are met:
Client IDs for CCDS apps will automatically download to a community member’s Epic instances when all of the following conditions are met:
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.
† Only the following DSTU2 FHIR APIs qualify for MU3 auto-download.
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.
Due to inactivity you will be logged out in 60 seconds.