Overview
This specification describes a “hook”-based pattern for invoking decision support from within a clinician’s EHR workflow. The API supports:
- Synchronous, workflow-triggered CDS calls returning information and suggestions
- Launching a user-facing SMART app when CDS requires deeper interaction
- Long-running, non-modal CDS sessions that observe EHR activity in progress
How it works
User activity inside the EHR triggers CDS hooks in real-time. For example:
patient-view
when opening a new patient recordmedication-prescribe
on authoring a new prescriptionorder-review
on viewing pending orders for approval
When a triggering activity occurs, the EHR notifies each CDS service registered for the activity. These services must then provide near-real-time feedback about the triggering event. Each service gets basic details about the EHR
context (via the context
parameter of the hook) plus whatever
service-specific data are required (via the pre-fetch-template
parameter).
CDS Cards
Each CDS service can return any number of cards in response to the hook. Cards convey some combination of text (information card), alternative suggestions (suggestion card), and links to apps or reference materials (app link card). A user sees these cards — one or more of each type — embedded in the EHR, and can interact with them as follows:
information card: provides text for the user to read.
suggestion card: provides a specific suggestion for which the EHR renders a button that the user can click to accept. Clicking automatically populates the suggested change into the EHR’s UI.
app link card: provides a link to an app (often a SMART app) where the user can supply details, step through a flowchart, or do anything else required to help reach an informed decision. When the user has finished, flow returns to the EHR. At that point, the EHR re-triggers the initial CDS hook. The re-triggering may result in different cards, and may also include decisions (see below).
CDS Decisions
In addition to cards, a CDS service may also return decisions — but only
after a user has interacted with the service via an app link card.
Returning a decision allows the the CDS service to communicate the user’s choices to the EHR without displaying an additional card. For
example, a user might launch a hypertension management app, and upon
returning to the EHR’s prescription page she expects her new blood pressure
prescription to “just be there”. By returning a decision instead of a card,
the CDS service achieves this expected behavior. (Note: To return a
decision after a user interaction, the CDS service must maintain state
associated with the request’s hookInstance
;
when the EHR invokes the hook for a second time with the same
hookInstance
, the service can respond with decisions on as well as cards.)
Try it!
You can try CDS Hooks in our test harness at http://sandbox.cds-hooks.org
CDS Services
Discovery
curl "https://example.com/cds-services"
The above command returns JSON structured like this:
{
"services": [
{
"hook": "patient-view",
"title": "Static CDS Service Example",
"description": "An example of a CDS service that returns a static set of cards",
"id": "static-patient-greeter",
"prefetch": {
"patientToGreet": "Patient/{{Patient.id}}"
}
},
{
"hook": "medication-prescribe",
"title": "Medication Echo CDS Service",
"description": "An example of a CDS service that simply echos the medication being prescribed",
"id": "medication-echo",
"prefetch": {
"patient": "Patient/{{Patient.id}}",
"medications": "MedicationOrder?patient={{Patient.id}}"
}
}
]
}
Developers of CDS Services must provide a well-known endpoint allowing the EHR to discover all available CDS Services, including information such as the purpose of the CDS Service, when it should be invoked, and any data that is requested to be prefetched.
HTTP Request
The discovery endpoint is always available at {baseUrl}/cds-services
. For example, if the baseUrl
is https://example.com, the EHR would invoke:
GET https://example.com/cds-services
Response
The response to the discovery endpoint is an object containing a list of CDS Services.
Field | Description |
---|---|
services |
array. An array of CDS Services |
Each CDS Service is described by the following attributes.
Field | Description |
---|---|
hook |
string or url. The hook this service should be invoked on. See Hook Catalog |
title |
string. The human-friendly name of this service |
description |
string. The description of this service |
id |
string. The {id} portion of the URL to this service which is available at{baseUrl}/cds-services/{id} |
prefetch |
object. An object containing key/value pairs of FHIR queries to data that this service would like the EHR prefetch and provide on each service call. The key is a string that describes the type of data being requested and the value is a string representing the FHIR query. (todo: link to prefetching documentation) |
HTTP Status Codes
Code | Description |
---|---|
200 OK |
A successful response |
Calling a CDS Service
curl
-X POST \
-H 'Content-type: application/json' \
--data @hook-details-see-example-below
"https://example.com/cds-services/static-patient-greeter"
{
"hookInstance" : "d1577c69-dfbe-44ad-ba6d-3e05e953b2ea",
"fhirServer" : "http://hooks.smarthealthit.org:9080",
"hook" : "patient-view",
"redirect" : "http://hooks2.smarthealthit.org/service-done.html",
"user" : "Practitioner/example",
"context" : [],
"patient" : "1288992",
"prefetch" : {
"patientToGreet" : {
"response" : {
"status" : "200 OK"
},
"resource" : {
"resourceType" : "Patient",
"gender" : "male",
"birthDate" : "1925-12-23",
"id" : "1288992",
"active" : true
}
}
}
}
HTTP Request
An EHR calls a CDS service by POST
ing a JSON document to the service
endpoint, which can be constructed from the CDS Service base URL and an
individual service id as {baseUrl}/cds-services/{service.id}
. The CDS Hook
call includes a JSON POST body with the following input fields:
Field | Description |
---|---|
hook |
string or URL. The hook that triggered this CDS Service call (todo: link to hook documentation) |
hookInstance |
string. A UUID for this particular hook call (see more information below) |
fhirServer |
URL. The base URL EHR’s FHIR server. The scheme should be https |
oauth |
object. The OAuth2 authorization providing access to the EHR’s FHIR server (see more information below) |
redirect |
URL. The URL an app link card should redirect to (see more information below) |
user |
string. The FHIR resource type + id representing the current user. The type is one of: Practitioner, Patient, or RelatedPerson. For example, Practitioner/123 |
patient |
string. The FHIR Patient.id of the current patient in context |
encounter |
string. The FHIR Encounter.id of the current encounter in context |
context |
object. Hook-specific contextual data that the CDS service will need. For example, with the medication-prescribe hook this will include MedicationOrder being prescribed. |
prefetch |
object. The FHIR data that was prefetched by the EHR (see more information below) |
hookInstance
While working in the EHR, a user can perform
multiple activities in series or in parallel. For example, a clinician might prescribe
two drugs in a row; each prescription activity would be assigned a
unique hookInstance
. The [[activity catalog|Activity]]
provides a description of what events should initiate and terminate
a given hook. This allows an external
service to associate requests with activity state, which is necessary to
support the following app-centric decision sequence, where
the steps are tied together by a common hookInstance
:
- EHR invokes CDS hook
- CDS service returns app link card
- User clicks app link and interacts with app
- Flow returns to EHR, which re-invokes CDS hook
- CDS service returns decision with user’s choice
Note: the hookInstance
is globally unique and should contain enough entropy
to be un-guessable.
oauth
Security details allowing the CDS service to connect to the EHR’s
FHIR server. These fields allow the CDS service to access EHR data in a
context limited by the current user’s privileges. expires
expresses the
token lifetime as an integer number of seconds. scope
represents the set of
scopes assigned to this token (see SMART on FHIR
scopes).
Finally, token
is a bearer token to be presented with any API calls the CDS
service makes to the EHR, by including it in an Authorization header like:
Authorization: Bearer {{token}}
redirect
This field is only used by services that will return an app
link card: when a user clicks the card’s link to launch an app, it becomes
the app’s job to send the user to this redirect
URL upon completion of
user interaction. (Design note: this field is supplied up-front, as part of
the initial request, to avoid requiring the EHR to append any content to app
launch links. This helps support an important “degenerate” use case for app
link cards: pointing to static content. See below for details.)
prefetch
As a performance tweak, the EHR may pass along data according to the service’s [[Prefetch-Template]]. This helps provide the service with all the data it needs to efficiently compute a set of recommendations. Each key matches a key described in the CDS Service Discovery document; and each value is a FHIR Bundle.entry indicating a response status and returned resource.
Note that in the absence of prefetch
, an external service can always execute
FHIR REST API calls against the EHR server to obtain additional data ad-hoc.)
CDS Service Response
Example response
{
"cards": [
{
"summary": "Example Card",
"indicator": "info",
"detail": "This is an example card.",
"source": {
"label": "Static CDS Service Example",
"url": "https://example.com",
"icon": "https://example.com/img/icon-100px.png"
},
"links": [
{
"label": "Google",
"url": "https://google.com",
"type": "absolute"
},
{
"label": "Github",
"url": "https://github.com",
"type": "absolute"
},
{
"label": "SMART Example App",
"url": "https://smart.example.com/launch",
"type": "smart"
}
]
},
{
"summary": "Another card",
"indicator": "warning",
"source": {
"label": "Static CDS Service Example"
}
}
]
}
Field | Description |
---|---|
cards |
array. An array of Cards. Cards can provide a combination of information (for reading), suggested actions (to be applied if a user selects them), and links (to launch an app if the user selects them). The EHR decides how to display cards, but we recommend displaying suggestions using buttons, and links using underlined text. |
decisions |
array. An array of Decisions. A decision should only be generated after interacting with the user through an app link. Decisions are designed to convey any choices the user made in an app session. |
represents a user’s choice made while interacting with the CDS Provider’s external app. The first call to a service should never include any decisions
, since no user interaction has occurred yet.
Each Card is described by the following attributes.
Field | Description |
---|---|
summary |
string. one-sentence, <140-character summary message for display to the user inside of this card. |
detail |
string. optional detailed information to display, represented in (GitHub Flavored) Markdown. (For non-urgent cards, the EHR may hide these details until the user clicks a link like “view more details…”.) |
indicator |
string. urgency/importance of what this card conveys. Allowed values, in order of increasing urgency, are: info , warning , hard-stop . The EHR can use this field to help make UI display decisions such as sort order or coloring. The value hard-stop indicates that the workflow should not be allowed to proceed. |
source |
object. grouping structure for the Source of the information displayed on this card. The source should be the primary source of guidance for the decision support the card represents. |
suggestions |
array of Suggestions, which allow a service to suggest a set of changes in the context of the current activity (e.g. changing the dose of the medication currently being prescribed, for the medication-prescribe activity). Note that suggestions are implicitly OR’d. |
links |
array of Links, which allow a service to suggest a link to an app that the user might want to run for additional information or to help guide a decision. |
The Source is described by the following attributes.
Field | Description |
---|---|
label |
string. A short, human-readable label to display for the source of the information displayed on this card. If a url is also specified, this may be the text for the hyperlink. |
url |
URL. An optional absolute URL to load (via GET , in a browser context) when a user clicks on this link to learn more about the organization or data set that provided the information on this card. Note that this URL should not be used to supply a context-specific “drill-down” view of the information on this card. For that, use link.url instead. |
icon |
URL. An optional absolute URL to an icon for the source of this card. The icon returned by this URL should be in PNG format, an image size of 100x100 pixels, and must not include any transparent regions. |
Each Suggestion is described by the following attributes.
Field | Description |
---|---|
label |
string. human-readable label to display for this suggestion (e.g. the EHR might render this as the text on a button tied to this suggestion). |
uuid |
string. unique identifier for this suggestion. For details see Suggestion Tracking Analytics |
actions |
array. array of objects, each defining a suggested action. Within a suggestion, all actions are logically AND’d together, such that a user selecting a suggestion selects all of the actions within it. |
Each Action is described by the following attributes.
Field | Description |
---|---|
type |
string. The type of action being performed. Allowed values are: create , update , delete . |
description |
string. human-readable description of the suggested action. May be presented to the end-user. |
resource |
object. depending upon the type attribute, new resource(s) or id(s) of resources. For a type of create , the resource attribute contains new FHIR resources to apply within the current activity (e.g. for medication-prescribe , this holds the updated prescription as proposed by the suggestion). For delete , id(s) of any resources to remove from the current activity (e.g. for the order-review activity, this would provide a way to remove orders from the pending list). In activities like medication-prescribe where only one “content” resource is ever relevant, this field may be omitted. For update , existing resources to modify from the current activity (e.g. for the order-review activity, this would provide a way to annotate an order from the pending list with an assessment). This field may be omitted. |
Each Link is described by the following attributes.
Field | Description |
---|---|
label |
string. human-readable label to display for this link (e.g. the EHR might render this as the underlined text of a clickable link). |
url |
URL. URL to load (via GET , in a browser context) when a user clicks on this link. Note that this may be a “deep link” with context embedded in path segments, query parameters, or a hash. In general this URL should embed enough context for the app to determine the hookInstance , and redirect url upon downstream launch, because the EHR will simply use this url as-is, without appending any parameters at launch time. |
type |
string. The type of the given URL. There are two possible values for this field. A type of absolute indicates that the URL is absolute and should be treated as-is. A type of smart indicates that the URL is a SMART app launch URL and the EHR should ensure the SMART app launch URL is populated with the appropriate SMART launch parameters. |
Each Decision is described by the following attributes.
Field | Description |
---|---|
create |
array of strings. id(s) of new resource(s) that the EHR should create within the current activity (e.g. for medication-prescribe , this would be the updated prescription that a user had authored in an app session). |
delete |
array of strings. id(s) of any resources to remove from the current activity (e.g. for the order-review activity, this would provide a way to remove orders from the pending list). In activities like medication-prescribe where only one “content” resource is ever relevant, this field may be omitted. |
No Decision Support
Response when no decision support is necessary for the user
{
"cards": []
}
If your CDS Service has no decision support for the user, your service should return a 200 HTTP response with an empty array of cards.
Analytics
Whenever a user clicks a button from a “suggestion” card, the EHR uses the
suggestion uuid
to notify the CDS Service’s analytics endpoint via a POST
with an empty body:
POST {baseUrl}/cds-services/{serviceId}/analytics/{uuid}
If a suggestion has no uuid
, the EHR does not send a notification.
Prefetch
A performance tweak
If real-world performance were no issue, an EHR could launch a CDS Service passing only context data, and without passing any additional clinical data up-front. The CDS Service could then request any data it needed via the EHR’s FHIR REST API.
But CDS services must respond quickly (on the order of 500 ms), and so we provide a performance tweak that allows a CDS Service to register a set of “prefetch templates” with the EHR ahead of time.
The prefetch templates are a dictionary of read
and search
requests to supply
relevant data, where the following variables are defined:
variable | meaning |
---|---|
{{Patient.id}} |
The id of the patient in context for this activity (e.g. 123 ) |
{{User.id}} |
The type and id of the user for this session (e.g. Practitioner/123 ) |
An EHR may choose to honor some or all of the desired prefetch templates from an appropriate source. For example:
- The EHR may have some of the desired prefetched data already in memory, thereby removing the need for any network call
- The EHR may compute an efficient set of prefetch templates from multiple CDS Services, thereby reducing the number of network calls to a minimum
- The EHR may satisfy some of the desired prefetched templates via some internal service or even its own FHIR server
Regardless of how the EHR satisfies the prefetched templates (if at all), it is important that the prefetched data given to the CDS Service is equivalent to the CDS Service making its own call to the EHR FHIR server, where {{Patient.id}}
is replaced with the id of the current patient (e.g. 123
) inside of any URL strings and using read
and search
operations to the server’s “transaction” endpoint as a FHIR batch-type bundle.
The resulting response, which must be rendered in a single page — no “next
page” links allowed — is passed along to the CDS Service using the
prefetch
parameter (see below for a complete example).
The CDS Service must not receive any prefetch template key that the EHR chooses not to satisfy. Additionally, if the EHR encounters an error while retrieving any prefetched data, the prefetch template key should not be sent to the CDS Service. It is the CDS Service’s responsibility to check to see what prefetched data was satisfied (if any) and manually retrieve any necessary data.
Example prefetch request
{
"prefetch": {
"p": "Patient/{{Patient.id}}",
"a1c": "Observation?patient={{Patient.id}}&code=4548-4&_count=1&sort:desc=date",
"u": "Practitioner/{{User.id}}"
}
}
Here is an example prefetch property from a CDS service discovery endpoint. The goal is to know, at call time:
Key | Description |
---|---|
p |
Patient demographics |
a1c |
Most recent Hemoglobin A1c reading for this patient |
u |
Information on the current user (Practitioner) |
Example prefetch response
{
"prefetch": {
"p":{
"response": {
"status": "200 OK"
},
"resource": {
"resourceType": "Patient",
"gender": "male",
"birthDate": "1974-12-25",
"...": "<snipped for brevity>"
}
},
"a1c": {
"response": {
"status": "200 OK"
},
"resource":{
"resourceType": "Bundle",
"type": "searchset",
"entry": [{
"resource": {
"resourceType": "Observation",
"code": {
"coding": [{
"system": "http://loinc.org",
"code": "4548-4",
"display": "Hemoglobin A1c"
}]
},
"...": "<snipped for brevity>"
}
}]
}
}
}
}
The response is augmented to include two prefetch values, where the dictionary
keys match the request keys (p
and a1c
in this case).
Note that the missing u
key indicates that either the EHR has decided not to satisfy this particular prefetch template or it was not able to retrieve this prefetched data. The CDS Service is responsible for retrieving this Practitioner data from the FHIR server (if required).
Prefetch query restrictions
To reduce the implementation burden on EHRs that support CDS services, CDS Hooks recommends that prefetch queries only use a subset of the full functionality available in the FHIR specification. Valid prefetch URLs should only contain:
- instance level read interactions (for resources with known ids such as
Patient
andPractitioner
) - type level search interactions
- Patient references (e.g.
patient={{Patient}}
) - token search parameters using equality (e.g.
code=4548-4
) and optionally the:in
modifier (no other modifiers for token parameters) - date search parameters on
date
,dateTime
,instant
, orPeriod
types only, and using only the prefixeseq
,lt
,gt
,ge
,le
- the
_count
parameter to limit the number of results returned - the
_sort
parameter to allow for most recent and first queries
Security
CDS Hooks defines the agreed upon security model between an EHR and the CDS Service. Like SMART on FHIR, the security model of CDS Hooks leverages the same open and well supported standards like OAuth 2 and JSON web tokens. However, as CDS Hooks differs from SMART, the manny in which these standards are used is specific to CDS Hooks.
Trusting CDS Services
As the EHR initiates every interaction with the CDS Service, it is responsible for establishing trust with the CDS Services it intends to call. This trust is established via a TLS connection to the CDS Service. Thus, all CDS Service endpoints must be deployed to a TLS protected URL (https). This includes both the Discovery and individual CDS Service endpoints.
EHRs should use accepted best practices for verifying the authenticity and trust of these TLS connections. For instance, rfc5280 and rfc6125. Additionally, it is assumed that EHRs configure the CDS Services they connect to via some offline process according to the business rules and practices of both the EHR and CDS Service organizations.
Trusting EHRs
Since the CDS Service is invoked by the EHR, the CDS Service does not have the same mechanism as EHRs to establish trust of the EHR invoking it. To establish trust of the EHR, JSON web tokens (JWT) are used. Specifically, the JWT is the same id_token
used in SMART on FHIR.
Each time the EHR makes a request to the CDS Service, it should send an Authorization
header where the value is Bearer <token>
, replacing <token>
with the actual JWT. Note that this is for every single CDS Service call, whether that be Discovery calls, CDS Service invocations, etc.
Example JSON web token payload
{
"iss": "https://fhir-ehr.example.com/",
"sub": "some-username",
"aud": "44b16507-8a59-4369-96f9-1e9b1f9a0ace",
"exp": 1422568860,
"iat": 1311280970
}
Using the above JWT payload, the complete JWT as passed in the Authorization HTTP header would be:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2ZoaXItZWhyLmV4YW1wbGUuY29tLyIsImF1ZCI6Imh0dHBzOi8vY2RzLmV4YW1wbGUub3JnL2Nkcy1zZXJ2aWNlcyIsImV4cCI6MTQyMjU2ODg2MCwianRpIjoiMmFmNWU4YTUtYjVlZi00ZmQwLThkOTMtYzU2MGRhYzQwYzk4In0.ahR57rtcMFhvrHEEo9w13vVdLrhZs_gRY2NV6R2GAoU
The JWT from the EHR is signed with the EHR’s private key and contains the following fields:
Field | Value |
---|---|
iss | The base URL of the EHR’s FHIR server. This must be the same URL as the fhirServer field in a CDS Service request. |
sub | The unique identifer for the current user |
aud | The OAuth 2 client id of the CDS Service |
exp | Expiration time integer for this authentication JWT, expressed in seconds since the “Epoch” (1970-01-01T00:00:00Z UTC). |
iat | The time at which this JWT was issued, expressed in seconds since the “Epoch” (1970-01-01T00:00:00Z UTC). |
Note that the use of JWT in CDS Hooks resembles the SMART on FHIR Backend Services usage of JWT. However, there are differences due to the differing use cases and concerns between CDS Hooks and SMART Backend Services.
https://jwt.io/ is a great resource not only for learning about JSON web tokens, but also for parsing a JWT value into its distinct parts to see how it is constructed. Try taking the example JWT here and pasting it into the form at https://jwt.io/ to see how the token is constructed.
Mutual TLS
Mutual TLS (mTLS) can be used between an EHR and CDS Service and that would allow the CDS Service to establish trust of the EHR. However, if mTLS is used, this should be in addition to using JSON web tokens to establish trust of the EHR. As mTLS is not well supported across all platforms and technologies, it is not the standard means of establishing trust with the EHR.
FHIR Resource Access
The CDS Service is able to use the FHIR server of the EHR to obtain any additional data it needs in order to perform its decision support. This is similar to SMART on FHIR where the SMART app can obtain additional data via the provided FHIR server.
Like SMART on FHIR, CDS Hooks requires that access to the FHIR server be controlled by an Authorization server utilizing the OAuth 2 framework. Thus, the CDS Service is able to consume the given FHIR server via an access (bearer) token just like a SMART app. While CDS Hooks shares the underlying technical framework and standards as SMART on FHIR, there are very important differences between SMART and CDS Hooks.
Obtaining an Access Token
In SMART on FHIR, the SMART app requests and ultimately obtains an access token from the Authorization server using the SMART launch workflow. This process utilizes the authorization code grant model as defined by the OAuth 2.0 Authorization Framework in rfc6749.
With CDS Hooks, the EHR provides the access token directly in the request to the CDS Service. Thus, the CDS Service does not need to request the token from the authorization server as a SMART app would. This is done purely for performance reasons as the authorization code grant model in OAuth 2 involves several HTTPS calls and redirects. In contrast with a SMART app, a CDS Service may be invoked many times during a workflow. Going through the authorization code grant model on every hook invocation would likely result in a slow performing CDS Service due to the authorization overhead.
{
"fhirAuthorization" : {
"access_token" : "some-opaque-fhir-access-token",
"token_type" : "code",
"expires_in" : 300,
"scope" : "patient/Patient.read patient/Observation.read"
}
}
Access Token
The access token is specified in the CDS Service request via the fhirAuthorization
request parameter. This parameter is an object that contains both the access token as well as other related information.
Field | Description |
---|---|
access_token |
string. This is the OAuth 2 access token that provides access to the FHIR server. |
token_type |
string. Always the value code |
expires_in |
integer. The lifetime in seconds of the access token. |
scope |
string. The scopes the access token grants the CDS Service. |
It is recommended that the expires_in
value be very short lived as the access token should be treated as a transient value by the CDS Service.
It is recommended that the scope
value contain just the scopes that the CDS Service needs for its logic and no more.
As the CDS Service is executing on behalf of a user, it is important that the data the CDS Service has access to is under the same restrictions/authorization as the current user. As such, the access token should be scoped to:
- The CDS Service being invoked
- The current user
- The current patient
Frequently Asked Questions
- How is the user (clinician) prompted to authorize the CDS Service to obtain an access token on behalf of the user? If we’re aligning with how security works for SMART apps, this is what happens today.
Not quite. There is actually no requirement that there be any user interaction to authorize a SMART app to obtain an access token. More generally, the OAuth 2 framework does not prescribe how authorization is determined. That is, authorization may be determined via some user interruptive decision, some pre-existing business rules, or even by random chance! The authorization server is free to determine how it grants access to the resources it protects.
With SMART on FHIR, we have seen real production behavior in which the authorization server:
- Grants access to practictioner facing SMART apps via some predefined business arrangement that was done out of bounds. The user (practitioner) never is asked to authorize the SMART app as their organization (hospital) has already made this decision for them.
- Grants access to patient facing SMART apps by asking the user explicitly for permission to both launch the SMART app as well as what specific scopes (data permissions) the SMART app may havel
- The FHIR access token model places quite a bit of work on the EHRs. Why?
Yes, requiring the EHRs to obtain and share the access token with each CDS Service on each hook invocation isn’t trivial work. However, the need to control the performance of the CDS Service invocations necessitates a different approach than SMART on FHIR. By putting the burden on the EHRs to obtain this access token, it is left to the EHR to manage this cost appropriately. Given the authorization server and EHR are controlled by the same organization, it is assumed the EHR is in a much better position to implement strategies to both obtain and manage access tokens in a performant manner in a model like CDS Hooks.
Unlike SMART apps, CDS Services should treat access tokens as transient tokens used during the course of a single evaluation of decision support. If the EHRs did not bear the responsibility of obtaining the access token on behalf of the CDS Service, each CDS Service would need to not only obtain the token themselves, but also determine a performant manner to optimize token (re)use when their service is invoked for the same user/patient.
Cross-Origin Resource Sharing
Cross-origin resource sharing (CORS) is web security mechanism that is built into browsers. In short, CORS allows servers to control how browsers access resources on the server, including the accessible HTTP response headers. CORS is only honored by web browsers and as such, is a client-side security mechanism.
For CDS Services, implementing CORS is required if your CDS Service is to be called from a web browser. As the CDS Hooks Sandbox is a browser application, you must implement CORS to test your CDS Service in the CDS Hooks Sandbox.
You should carefully consider how you support CORS, but a quick starting point for testing would be to ensure your CDS Service returns the following HTTP headers:
Header | Value |
---|---|
Access-Control-Allow-Credentials | true |
Access-Control-Allow-Methods | GET, POST, OPTIONS |
Access-Control-Allow-Origin | * |
Access-Control-Expose-Headers | Origin, Accept, Content-Location, Location, X-Requested-With |
Hook Catalog
Pre-defined CDS hooks
We describe a set of hooks to support common use cases out of the box. But this is not a closed set; anyone can define new hooks to address new use cases. To propose a new hooks please add it to the proposed hooks page of our wiki.
Note that each hook (e.g. medication-prescribe
) represents something the user is doing in the EHR; various hooks might respond to the same hook (e.g. a “price check” service and a “prior authorization” service might both respond to medication-prescribe
).
Note also that each hook name in our official catalog is a simple string. If
you want to define custom hooks without submitting them to the catalog, then
you should use a URI (e.g.
http://my-organization/custom-hooks/patient-transmogrify
).
medication-prescribe
{
"context": {
"resourceType": "MedicationOrder",
"medicationCodeableConcept": {
"...": "<snipped for brevity>"
}
}
}
Purpose: Inform an external CDS service that the user is in the process of prescribing a new medication, and solicit feedback about the prescription-in-progress.
Contextual data: The set of proposed medication prescriptions. using the
FHIR MedicationOrder
resource. See example in the sidebar.
order-review
{
"context": {
"resourceType": "DiagnosticOrder",
"...": "<snipped for brevity>"
}
}
Purpose: Inform an external CDS service that the user is in the process of reviewing a set of orders (sometimes known as a “shopping cart”), and solicit feedback about the orders being reviewed.
Contextual data: The set of orders being reviewed on-screen, represented using a combination of MedicationOrder, DiagnosticOrder, DeviceUseRequest, ReferralRequest, and ProcedureRequest. See example in the sidebar.
patient-view
Purpose: Inform an external CDS service that the user has just opened a new patient record and is viewing a summary screen or “face sheet”, and solicit feedback about this patient.
Contextual data: None required beyond default context.
Community
Get Involved
There are several ways in which you can get involved with the CDS Hooks community.
- Ask a question and participate in discussions via the CDS Hooks Google Group
- Chat via Zulip at https://chat.fhir.org in the cds-hooks stream
- Contribute to the code and documentation on Github
- Check out the CDS services available for testing and contribute your own.
CDS Hooks Sprint Program
Objectives
- Promote creation of clinical-grade service integrations (EHRs + CDS Services)
- Gain implementation experience with real-world systems
- Refine the spec, balancing ease of use, flexibility, and stability
- Drive toward pilot deployments with the ability to measure results
Want to help build our test infrastructure?
Examples
CDC Guideline for Prescribing Opioids for Chronic Pain
CDS Service Request
The example illustrates a prescription for Acetaminophen/Hydrocodone Bitartrate for a patient that already has a prescription for Oxycodone Hydrochloride:
{
"hookInstance": "d1577c69-dfbe-44ad-ba6d-3e05e953b2ea",
"fhirServer": "http://fhir.example.com",
"hook": "medication-prescribe",
"user": "Practitioner/example",
"context": [
{
"resourceType": "MedicationOrder",
"id": "medrx001",
"dateWritten": "2017-05-05",
"status": "draft",
"patient": {
"reference": "Patient/example"
},
"medicationCodeableConcept": {
"coding": [
{
"system": "http://www.nlm.nih.gov/research/umls/rxnorm",
"code": "857001",
"display": "Acetaminophen 325 MG / Hydrocodone Bitartrate 10 MG Oral Tablet"
}
]
},
"dosageInstruction": [
{
"text": "Take 1 tablet Oral every 4 hours as needed",
"timing": {
"repeat": {
"frequency": 6,
"frequencyMax": 6,
"period": 1,
"unit": "d"
}
},
"asNeededBoolean": true,
"doseQuantity": {
"value": 10,
"unit": "mg",
"system": "http://unitsofmeasure.org",
"code": "mg"
}
}
]
}
],
"patient": "Patient/example",
"prefetch": {
"medication": {
"response": {
"status": "200 OK"
},
"resource": {
"resourceType": "MedicationOrder",
"id": "medrx002",
"dateWritten": "2017-04-25",
"status": "active",
"patient": {
"reference": "Patient/example"
},
"medicationCodeableConcept": {
"coding": [
{
"system": "http://www.nlm.nih.gov/research/umls/rxnorm",
"code": "1049621",
"display": "Oxycodone Hydrochloride 5 MG Oral Tablet"
}
]
},
"dosageInstruction": [
{
"text": "Take 1 tablet by mouth every 4 hours as needed for pain.",
"timing": {
"repeat": {
"frequency": 6,
"period": 1,
"periodUnits": "d"
}
},
"asNeededBoolean": true,
"doseQuantity": {
"value": 5,
"unit": "mg",
"system": "http://unitsofmeasure.org",
"code": "mg"
}
}
]
}
}
}
}
This example illustrates the use of the CDS Hooks medication-prescribe
hook to implement Recommendation #5 from the CDC guideline for prescribing opioids for chronic pain.
This example is taken from the Opioid Prescribing Support Implementation Guide, developed in partnership with the Centers for Disease Control and Prevention (CDC).
CDS Service Response
The opioid guideline request results in the following response that indicates the patient is at high risk for opioid overdose according to the CDC guidelines, and the dosage should be tapered to less than 50 MME. Links are provided to the guideline, as well as to the MME conversion tables provided by CDC.
{
"summary": "High risk for opioid overdose - taper now",
"indicator": "warning",
"links": [
{
"label": "CDC guideline for prescribing opioids for chronic pain",
"type": "absolute",
"url": "https://guidelines.gov/summaries/summary/50153/cdc-guideline-for-prescribing-opioids-for-chronic-pain---united-states-2016#420"
},
{
"label": "MME Conversion Tables",
"type": "absolute",
"url": "https://www.cdc.gov/drugoverdose/pdf/calculating_total_daily_dose-a.pdf"
}
],
"detail": "Total morphine milligram equivalent (MME) is 125mg. Taper to less than 50."
}
Radiology Appropriateness
CDS Service Request
This example illustrates the use of the CDS Hooks
order-review
hook to implement Radiology Appropriateness scoring.
{
"hookInstance": "d1577c69-dfbe-44ad-ba6d-3e05e953b2ea",
"fhirServer": "http://fhir.example.com",
"hook": "order-review",
"user": "Practitioner/example",
"context": [
{
"resourceType": "ProcedureRequest",
"id": "procedure-request-1",
"status": "draft",
"intent": "proposal",
"priority": "routine",
"code": {
"coding": [{
"system": "http://www.ama-assn.org/go/cpt",
"code": "70450",
"display": "CT, head, wo iv contrast"
}]
},
"subject": {
"reference": "Patient/example"
},
"requester": {
"agent": {
"reference": "Practitioner/exampmle"
}
}
}
],
"patient": "Patient/example"
}
CDS Service Response
The appropriateness score is communicated via an update of the procedure request that adds an extension element to indicate the appropriateness rating.
{
"cards": [
{
"summary": "Usually appropriate",
"indicator": "info",
"detail": "The requested procedure is usually appropriate for the given indications.",
"suggestions": [
{
"label": "The appropriateness score for this procedure given these indications is 9, usually appropriate.",
"actions": [{
"type": "update",
"description": "Update the order to record the appropriateness score.",
"resource": {
"resourceType": "ProcedureRequest",
"id": "procedure-request-1",
"extension": [
{
"url": "http://hl7.org/fhir/us/qicore/StructureDefinition/procedurerequest-appropriatenessScore",
"valueDecimal": "9"
}
],
"status": "draft",
"intent": "proposal",
"priority": "routine",
"code": {
"coding": [{
"system": "http://www.ama-assn.org/go/cpt",
"code": "70450",
"display": "CT, head, wo iv contrast"
}]
},
"subject": {
"reference": "Patient/example"
},
"requester": {
"agent": {
"reference": "Practitioner/exampmle"
}
}
}
}]
}
]
}
]
}