Skip to content

CDS Services

1.0 Draft

This is the draft of the 1.0 release of the CDS Hooks specification. We are currently working towards a 1.0 release and would love your feedback and proposed changes. Look at our current issue list and get involved!

Overview

The CDS Hooks specification describes the RESTful APIs and interactions between EHRs and CDS Services. All data exchanged through the RESTful APIs MUST be sent and received as JSON structures, and MUST be transmitted over channels secured using the Hypertext Transfer Protocol (HTTP) over Transport Layer Security (TLS), also known as HTTPS and defined in RFC2818.

Conformance Language

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this specification are to be interpreted as described in RFC2119.

Limitations

This specification has the following limitations.

  • The specification provides options for providing a CDS Service access to the EHR's FHIR resources through use of either prefetch and/or a bearer access token to be presented to the FHIR Server. Decisions regarding which approach to take, and details regarding how the choice is implemented, are left up to the EHR vendor/provider, as architectural considerations will come into play.
  • The specification requires that each CDS Service provider be registered (client_id, key-pair identifier) with the EHR Authorization Server, but does not dictate how registration is accomplished (e.g., dynamic vs. manual).
  • The specification acknowledges that each EHR vendor/provider will need to vet CDS Service providers and suggests that vendors/providers create a "white list" of providers deemed "safe" and/or a "black list" of providers deemed "unsafe." Similarly, an EHR vendor/provider MAY assess the safety of active links embedded in CDS Hooks Cards returned to the EHR. The specification does not suggest how such vetting is accomplished nor suggest factors to consider, as these judgments are dependent upon each EHR vendor's or provider's existing vetting processes and risk-management policy.

Open API Specification

An Open API Specification (formally known as the Swagger Specification) interface of CDS Hooks is available. The open source Open API Specification defines a programming language-agnostic method to describe a RESTful API like CDS Hooks. The Open API Specification is used by many projects and is supported by CDS Hooks for the benefit of implementers.

CDS Hooks implementers are not required to use the Open API Specification interface of CDS Hooks. You can download the API specification and view it online via the Swagger Editor.

Discovery

Developers of CDS Services SHALL provide a stable endpoint for allowing EHRs to discover available CDS Services, including information such as a description of the CDS Service, when it should be invoked, and any data that is requested to be prefetched.

A CDS Service provider SHALL expose its Discovery endpoint at"

1
{baseURL}/cds-services

HTTP Request

The discovery endpoint SHALL always be available at {baseUrl}/cds-services. For example, if the baseUrl is https://example.com, the EHR MAY invoke:

GET https://example.com/cds-services

Response

The response to the discovery endpoint SHALL be an object containing a list of CDS Services.

Field Description
services array. An array of CDS Services.

Each CDS Service SHALL be described by the following attributes.

Field Optionality Type Description
hook REQUIRED string The hook this service should be invoked on. See Hooks.
title RECOMMENDED string The human-friendly name of this service.
description REQUIRED string The description of this service.
id REQUIRED string The {id} portion of the URL to this service which is available at
{baseUrl}/cds-services/{id}
prefetch OPTIONAL object An object containing key/value pairs of FHIR queries that this service is requesting that 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.
See Prefetch Template.

HTTP Status Codes

Code Description
200 OK A successful response.

CDS Services MAY return other HTTP statuses, specifically 4xx and 5xx HTTP error codes.

Discovery Example

1
curl "https://example.com/cds-services"

The above command returns JSON structured like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
  "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/{{context.patientId}}"
      }
    },
    {
      "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/{{context.patientId}}",
        "medications": "MedicationRequest?patient={{context.patientId}}"
      }
    }
  ]
}

Calling a CDS Service

HTTP Request

An EHR SHALL call a CDS Service by POSTing a JSON document to the service as described in this section. The CDS Service endpoint can be constructed from the CDS Service base URL and an individual service id as {baseUrl}/cds-services/{service.id}. The request SHALL include a JSON POST body with the following input fields:

Field Optionality Type Description
hook REQUIRED string The hook that triggered this CDS Service call. See Hooks.
hookInstance REQUIRED string A UUID for this particular hook call (see more information below).
fhirServer OPTIONAL URL The base URL EHR's FHIR server. If fhirAuthorization is provided, this field is REQUIRED. The scheme should be https
fhirAuthorization OPTIONAL object A structure holding an OAuth 2.0 bearer access token granting the CDS Service access to FHIR resources, along with supplemental information relating to the token. See the FHIR Resource Access section for more information.
context REQUIRED object Hook-specific contextual data that the CDS service will need.
For example, with the patient-view hook this will include the FHIR identifier of the Patient being viewed. For details, see the Hooks specification page.
prefetch OPTIONAL 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 actions in series or in parallel. For example, a clinician might prescribe two drugs in a row; each prescription action would be assigned a unique hookInstance. This allows a CDS Service to uniquely identify each hook invocation.

Note: the hookInstance is globally unique and should contain enough entropy to be un-guessable.

Example

1
2
3
4
5
curl
  -X POST \
  -H 'Content-type: application/json' \
  --data @hook-details-see-example-below
  "https://example.com/cds-services/static-patient-greeter"
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{
   "hookInstance" : "d1577c69-dfbe-44ad-ba6d-3e05e953b2ea",
   "fhirServer" : "http://hooks.smarthealthit.org:9080",
   "hook" : "patient-view",
   "fhirAuthorization" : {
     "access_token" : "some-opaque-fhir-access-token",
     "token_type" : "Bearer",
     "expires_in" : 300,
     "scope" : "patient/Patient.read patient/Observation.read",
     "subject" : "cds-service4"
   },
   "context" : {
       "userId" : "Practitioner/example",
       "patientId" : "1288992",
       "encounterId" : "89284"
   },
   "prefetch" : {
      "patientToGreet" : {
         "resourceType" : "Patient",
         "gender" : "male",
         "birthDate" : "1925-12-23",
         "id" : "1288992",
         "active" : true
      }
   }
}

Providing FHIR Resources to a CDS Service

Each CDS Service will require specific FHIR resources in order to compute the recommendations the EHR requests. If real-world performance were no issue, an EHR could launch a CDS Service passing only context data, and the CDS Service could then request authorization for FHIR resources as they were needed, and then retrieve the resources from the EHR's FHIR server. Given that CDS Services SHOULD respond quickly (on the order of 500 ms.), this specification defines a process to allow a CDS Service to request and obtain FHIR resources efficiently.

Two optional methods are provided. First, FHIR resources MAY be obtained by passing "prefetched" data from the EHR to the CDS Service in the service call. FHIR resources requested in the CDS Service desciption are passed as key-value pairs, with each key matching a key described in the CDS Service description, and each value being a FHIR resource. If data are to be prefetched, the CDS Service registers a set of "prefetch templates" with the EHR, as described in the Prefetch Template section below.

The second method enables the CDS Service to retrieve FHIR resources for itself, but to do so more efficiently than if it were required to request and obtain its own authorization. If the EHR decides to have the CDS Service fetch its own FHIR resources, the EHR obtains and passes directly to the CDS Service a bearer token issued for the CDS Service's use in executing FHIR API calls against the EHR FHIR server to obtain the required resources. Some EHRs MAY pass prefetched data, along with a bearer token for the CDS Service to use if additional resources are required. Each EHR SHOULD decide which approach, or combination, is preferred, based on performance considerations and assessment of attendant security and safety risks. For more detail, see the FHIR Resource Access section below.

Similarly, each EHR will decide what FHIR resources to authorize and to prefetch, based on the CDS Service description's "prefetch" request and on the provider's assessment of the "minimum necessary." The EHR provider and the CDS Service provider will negotiate the set of FHIR resources to be provided, and how these data will be provided, as part of their service agreement.

Prefetch Template

The prefetch template is a dictionary of read and search requests to supply relevant data. In order to allow for prefetch templates that are dependent upon the particular CDS Service request, prefetch tokens or variables MAY be defined.

Prefetch tokens MUST be delimited by {{ and }}, MUST be named based upon the field they correspond to, and MUST have a primitive value.

Individual hooks specify which of their context fields can be used as prefetch tokens. Only root-level fields with a primitive value within the context object are eligible to be used as prefetch tokens.

For instance, given a hook of example-hook with the following context in which the patientId and medicationId fields are both denoted as prefix tokens:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
"context": {
  "patientId": "123",
  "medicationId": "456",
  "medication": {
    "id": "456",
    "code": {
      ... // FHIR CodeableConcept
    }
  }
}

The prefetch tokens defined by this example-hook would be {{context.patientId}} and {{context.medicationId}}. Note that the context.medication.id field is not eligible to be a prefetch token as it is not a root-level field of the context object.

An EHR MAY choose to honor some or all of the desired prefetch templates, and is free to choose the most appropriate source for these data. 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.

The EHR SHALL deny access if the requested resource is outside the user's authorized context.

Regardless of how the EHR satisfies the prefetch templates (if at all), the prefetched data given to the CDS Service MUST be equivalent to the data the CDS Service would receive if it were making its own call to the EHR FHIR server using the parameterized prefetch template.

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; in which case the prefetch template key SHOULD NOT be sent. Similarly, if the EHR encounters an error while prefetching any data, the prefetch template key SHOULD NOT be sent to the CDS Service. If the EHR has no data to populate a template prefetch key, the prefetch template key MUST have a value of null. It is the CDS Service's responsibility to check prefetched data against its template to determine what requests were satisfied (if any) and to manually retrieve any additional necessary data. If the CDS Service is unable to obtain required data because it cannot access the FHIR server and the request did not contain the necessary prefetch keys, the service SHALL respond with an HTTP 412 Precondition Failed status code.

Example prefetch templates

1
2
3
4
5
6
7
{
  "prefetch": {
    "p": "Patient/{{context.patientId}}",
    "a1c": "Observation?patient={{context.patientId}}&code=4548-4&_count=1&sort:desc=date",
    "u": "Practitioner/{{context.userId}}"
  }
}

Here is an example prefetch field 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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
{
  "prefetch": {
    "p":{
      "resourceType": "Patient",
      "gender": "male",
      "birthDate": "1974-12-25",
      "...": "<snipped for brevity>"
    },
    "a1c": {
      "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 and Practitioner)
  • type level search interactions
  • Patient references (e.g. patient={{context.patientId}})
  • 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, or Period types only, and using only the prefixes eq, 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

FHIR Resource Access

If the EHR provides both fhirServer and fhirAuthorization request parameters, the CDS Service MAY use the EHR's FHIR server to obtain any FHIR resources it requires beyond those provided by the EHR as prefetched data. This is similar to the approach used by SMART on FHIR wherein the SMART app requests and ultimately obtains an access token from the EHR's Authorization server using the SMART launch workflow, as described in SMART App Authorization Guide.

Like SMART on FHIR, CDS Hooks requires that clients present a valid access token to the FHIR server with each API call. Thus, a CDS Service MUST be able to obtain an access token before communicating with the EHR's FHIR resource server. While CDS Hooks shares the underlying technical framework and standards as SMART on FHIR, the CDS Hooks workflow MUST accommodate the automated, low-latency delivery of an access token to the CDS service.

With CDS Hooks, if the EHR wants to provide the CDS Service direct access to FHIR resources, the EHR creates an access token prior to invoking the CDS Service, passing this token to the CDS Service as part of the service call. This approach remains compatible with OAuth 2.0's bearer token protocol while minimizing the number of HTTPS round-trips and the service invocation latency. The EHR remains in control of creating an access token that is associated with the specific CDS Service, user, and context of the invocation. As the CDS Service executes on behalf of a user, the data to which the CDS Service is given access by the EHR MUST be limited to the same restrictions and authorizations afforded the current user. As such, the access token SHALL be scoped to:

  • The CDS Service being invoked
  • The current user

Passing the Access Token to the CDS Service

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 as specified below. If the EHR chooses not to pass along an access token, the fhirAuthorization parameter is omitted.

Field Optionality Type Description
access_token REQUIRED string This is the OAuth 2.0 access token that provides access to the FHIR server.
token_type REQUIRED string Fixed value: Bearer
expires_in REQUIRED integer The lifetime in seconds of the access token.
scope REQUIRED string The scopes the access token grants the CDS Service.
subject REQUIRED string The OAuth 2.0 client identifier of the CDS Service, as registered with the EHR's authorization server.

The scopes granted to the CDS Service via the scope field are defined by the SMART on FHIR specification.

The expires_in value is established by the authorization server and SHOULD BE very short lived, as the access token MUST be treated as a transient value by the CDS Service.

Below is an example fhirAuthorization parameter:

1
2
3
4
5
6
7
8
9
{
  "fhirAuthorization" : {
    "access_token" : "some-opaque-fhir-access-token",
    "token_type" : "Bearer",
    "expires_in" : 300,
    "scope" : "patient/Patient.read patient/Observation.read",
    "subject" : "cds-service4"
  }
}

CDS Service Response

Card Array

Field Optionality Type Description
cards REQUIRED 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 this specification recommends displaying suggestions using buttons, and links using underlined text.

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.

Response when no decision support is necessary for the user

1
2
3
{
  "cards": []
}

Card Attributes

Each Card is described by the following attributes.

Field Optionality Type Description
summary REQUIRED string One-sentence, <140-character summary message for display to the user inside of this card.
detail OPTIONAL string Optional detailed information to display; if provided MUST be 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 REQUIRED string Urgency/importance of what this card conveys. Allowed values, in order of increasing urgency, are: info, warning, critical. The EHR MAY use this field to help make UI display decisions such as sort order or coloring.
source REQUIRED 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 OPTIONAL array of Suggestions Allows 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). If suggestions are present, selectionBehavior MUST also be provided.
selectionBehavior OPTIONAL string Describes the intended selection behavior of the suggestions in the card. Currently, the only allowed value is at-most-one, indicating that the user may choose none or at most one of the suggestions. Future versions of the specification may expand this behavior, so EHRs that do not understand the value MUST treat the card as an error. EHRs MUST support the value of at-most-one.
links OPTIONAL array of Links Allows 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.

Source

The Source is described by the following attributes.

Field Optionality Type Description
label REQUIRED 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 OPTIONAL 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 OPTIONAL URL An absolute URL to an icon for the source of this card. The icon returned by this URL SHOULD be a 100x100 pixel PNG image without any transparent regions.

Suggestion

Each Suggestion is described by the following attributes.

Field Optionality Type Description
label REQUIRED 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 OPTIONAL string Unique identifier, used for auditing and logging suggestions.
actions OPTIONAL 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.
Action

Each Action is described by the following attributes.

Field Optionality Type Description
type REQUIRED string The type of action being performed. Allowed values are: create, update, delete.
description REQUIRED string Human-readable description of the suggested action MAY be presented to the end-user.
resource OPTIONAL object Depending upon the type attribute, a new resource or the id of a resource. For a type of create, the resource attribute contains a new FHIR resource to apply within the current activity (e.g. for medication-prescribe, this holds the updated prescription as proposed by the suggestion). For delete, this is the id of any resource to remove (e.g. for the order-review activity, this would provide a way to remove an order from the pending list). In activities like medication-prescribe where only one "content" resource is ever relevant, this field MAY be omitted. For update, this holds the updated resource 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).

Each Link is described by the following attributes.

Field Optionality Type Description
label REQUIRED 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 REQUIRED 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.
type REQUIRED 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.
appContext OPTIONAL string An optional field that allows the CDS Service to share information from the CDS card with a subsequently launched SMART app. The appContext field should only be valued if the link type is smart and is not valid for absolute links. The appContext field and value will be sent to the SMART app as part of the OAuth 2.0 access token response, alongside the other SMART launch parameters when the SMART app is launched. Note that appContext could be escaped JSON, base64 encoded XML, or even a simple string, so long as the SMART app can recognize it.

Example

Example response

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
{
  "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",
          "appContext": "{\"session\":3456356,\"settings\":{\"module\":4235}}"
        }
      ]
    },
    {
      "summary": "Another card",
      "indicator": "warning",
      "source": {
        "label": "Static CDS Service Example"
      }
    }
  ]
}

Security and Safety

Security and safety risks associated with the CDS Hooks API include:

  1. The risk that confidential information and privileged authorizations transmitted between an EHR and a CDS Service could be surreptitiously intercepted by an attacker;
  2. The risk that an attacker masquerading as a legitimate CDS Service could receive confidential information or privileged authorizations from an EHR, or could provide to an EHR decision support recommendations that could be harmful to a patient;
  3. The risk that an attacker masquerading as a legitimate service-subscribing EHR (i.e., man-in-the-middle) could intercept and possibly alter data exchanged between the two parties.
  4. The risk that a CDS Service could embed dangerous suggestions or links to dangerous apps in Cards returned to an EHR.
  5. The risk that a CDS Hooks browser-based deployment could be victimized by a Cross-Origin Resource Sharing (CORS) attack.
  6. The risk that a CDS Service could return a decision based on outdated patient data, resulting in a safety risk to the patient.

CDS Hooks defines a security model that addresses these risks by assuring that the identities of both the CDS Service and the EHR are authenticated to each other; by protecting confidential information and privileged authorizations shared between an EHR and a CDS Service; by recommending means of assuring data freshness; and by incorporating business mechanisms through which trust is established and maintained between an EHR and a CDS Service.

Trusting CDS Services

Prior to enabling EHRs to request decision support from any CDS Service, the EHR vendor and/or provider organization is expected to perform due diligence on the CDS Service provider. Each EHR vendor/provider is individually responsible for determining the suitability, safety and integrity of the CDS Services it uses, based on the organization's own risk-management strategy. Each EHR vendor/provider SHOULD maintain a "white list" (and/or "black list") of the CDS Services it has vetted, and the Card links that have been deemed safe to display from within the EHR context. Each provider organization is expected to work with its EHR vendor to choose what CDS Services to allow and to negotiate the conditions under which the CDS Services MAY be called.

Once a CDS Service provider is selected, the EHR vendor/provider negotiates the terms under which service will be provided. This negotiation includes agreement on patient data elements that will be prefetched and provided to the CDS Service, data elements that will be made available through an access token passed by the EHR, and steps the CDS Service MUST take to protect patient data and access tokens. To enable the EHR authorization server to authorize CDS Service access to FHIR resources, the CDS Service is registered as a client to the EHR authorization server. These business arrangements are documented in the service agreement.

Every interaction between an EHR and a CDS Service is initiated by the EHR sending a service request to a CDS Service endpoint protected using the Transport Layer Security protocol. Through the TLS protocol the identity of the CDS Service is authenticated, and an encrypted transmission channel is established between the EHR and the CDS Service. Both the Discovery endpoint and individual CDS Service endpoints are TLS secured.

The EHR’s authorization server is responsible for enforcing restrictions on the CDS Services that MAY be called and the scope of the FHIR resources that MAY be prefetched or retrieved from the FHIR server. Therefore, all CDS Services to be called from within an EHR system are pre-registered with the authorization server of that EHR. Pre-registration includes registering a CDS client identifier, and agreeing upon the scope of FHIR access that is minimally necessary to provide the clinical decision support required.

Trusting EHRs

The service agreement negotiated between the EHR vendor/provider and the CDS Service provider will include obligations the EHR vendor/provider commits to the CDS Service provider. Some agreements MAY include the use of mutual TLS, in which both ends of the channel are authenticated.

However, mutual TLS is impractical for many organizations, and because the EHR initiates the TLS channel set-up, only the CDS Service endpoint will be authenticated. To enable the CDS Service to authenticate the identity of the EHR, CDS Hooks uses digitally signed JSON web tokens (JWT).

Each time an EHR transmits a request to a CDS Service, the request MUST include an Authorization header presenting the JWT as a “Bearer” token:

1
Authorization:  Bearer {{JWT}}

Note that this is for every single CDS Service call, whether that be a Discovery call, a single CDS Service invocation, or multiple exchanges relating to a single service. Also note that mutual TLS MAY be used alongside JSON web tokens to establish trust of the EHR by the CDS Service.

The EHR MUST use its private key to digitally sign the JWT, using the JSON Web Signatures (rfc7515) standard.

The JWT header contains the following fields (see rfc7515 section 4.1 for further information on these standard headers):

Field Optionality Type Value
alg REQUIRED string The cryptographic algorithm used to sign this JWT.
kid REQUIRED string The identifier of the key-pair used to sign this JWT. This identifier MUST be unique within the EHR's JWK Set.
typ REQUIRED string Fixed value: JWT
jku OPTIONAL url The URL to the JWK Set containing the public key(s).

The JWT payload contains the following fields:

Field Optionality Type Value
iss REQUIRED string The URI of the issuer of this JWT. Note that the JWT MAY be self-issued by the EHR, or MAY be issued by a third-party identity provider.
aud REQUIRED string or array of string The CDS Service endpoint that is being called by the EHR. (See more details below).
exp REQUIRED number Expiration time integer for this authentication JWT, expressed in seconds since the "Epoch" (1970-01-01T00:00:00Z UTC).
iat REQUIRED number The time at which this JWT was issued, expressed in seconds since the "Epoch" (1970-01-01T00:00:00Z UTC).
jti REQUIRED string A nonce string value that uniquely identifies this authentication JWT (used to protect against replay attacks).
tenant OPTIONAL string An opaque string identifying the healthcare organization that is invoking the CDS Hooks request.

CDS Services SHOULD whitelist the iss, jku and sub fields to only the EHRs they trust.

Per rfc7519, the aud value is either a string or an array of strings. For CDS Hooks, this value MUST be the URL of the CDS Service endpoint being invoked. For example, consider a CDS Service available at a base URL of https://cds.example.org. When the EHR invokes the CDS Service discovery endpoint, the aud value is either "https://cds.example.org/cds-services" or ["https://cds.example.org/cds-services"]. Similarly, when the EHR invokes a particular CDS Service (say, some-service), the aud value is either "https://cds.example.org/cds-services/some-service" or ["https://cds.example.org/cds-services/some-service"].

The EHR MUST make its public key, expressed as a JSON Web Key (JWK) in a JWK Set, as defined by rfc7517. The kid value from the JWT header allows a CDS Service to identify the correct JWK in the JWK Set that can be used to verify the signature.

The EHR MAY make its JWK Set available via a URL identified by the jku header field, as defined by rfc7515 4.1.2. If the jku header field is ommitted, the EHR and CDS Service SHALL communicate the JWK Set out-of-band.

An example JSON web token header, payload, and JWK set:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// JSON Web Token Header
{
  "alg": "ES384",
  "typ": "JWT",
  "kid": "example-kid",
  "jku": "https://fhir-ehr.example.com/jwk_uri"
}

// JSON Web Token Payload
{
  "iss": "https://fhir-ehr.example.com/",
  "sub": "client_id",
  "aud": "https://cds.example.org/cds-services/some-service",
  "exp": 1422568860,
  "iat": 1311280970,
  "jti": "ee22b021-e1b7-4611-ba5b-8eec6a33ac1e",
  "tenant": "2ddd6c3a-8e9a-44c6-a305-52111ad302a2"
}

// JSON Web Key Set (public key)
// This public key is used by the CDS Service to verify the signature of the JWT
{  
  "keys":[  
    {  
      "kty": "EC",
      "use": "sig",
      "crv": "P-384",
      "kid": "example-kid",
      "x": "46SDH7Znh821wblCBglA61sNE9ZrHYKKt3qRtRTmSXyOI_FIGBLWrWa0GPUkDCEk",
      "y": "XMcRuuoGW7CXjQdy-F5i3FeBE0x9hPLdeFdSoDd3ELmx404tLX0VRRcqzAsPhXcI",
      "alg": "ES384"
    }
  ]
}

// JSON Web Key (private key)
// This private is used by the EHR to sign the JWT
{  
  "kty": "EC",
  "d": "SeFXUXda8UomZ8GFUl7HH_Oi15rIbfMcsWj9ecIsDR8kLbqsEz2CGNgwy_IcILxy",
  "use": "sig",
  "crv": "P-384",
  "kid": "example-kid",
  "x": "46SDH7Znh821wblCBglA61sNE9ZrHYKKt3qRtRTmSXyOI_FIGBLWrWa0GPUkDCEk",
  "y": "XMcRuuoGW7CXjQdy-F5i3FeBE0x9hPLdeFdSoDd3ELmx404tLX0VRRcqzAsPhXcI",
  "alg": "ES384"
}

Using the above JWT values and JWKs, the complete JWT as passed in the Authorization HTTP header would be:

1
Authorization: Bearer eyJhbGciOiJFUzM4NCIsInR5cCI6IkpXVCIsImtpZCI6ImV4YW1wbGUta2lkIiwiamt1IjoiaHR0cHM6Ly9maGlyLWVoci5leGFtcGxlLmNvbS9qd2tfdXJpIn0.eyJpc3MiOiJodHRwczovL2ZoaXItZWhyLmV4YW1wbGUuY29tLyIsInN1YiI6ImNsaWVudF9pZCIsImF1ZCI6Imh0dHBzOi8vY2RzLmV4YW1wbGUub3JnL2Nkcy1zZXJ2aWNlcy9zb21lLXNlcnZpY2UiLCJleHAiOjE0MjI1Njg4NjAsImlhdCI6MTMxMTI4MDk3MCwianRpIjoiZWUyMmIwMjEtZTFiNy00NjExLWJhNWItOGVlYzZhMzNhYzFlIiwidGVuYW50IjoiMmRkZDZjM2EtOGU5YS00NGM2LWEzMDUtNTIxMTFhZDMwMmEyIn0.CUFPkplnWd6YGIvzoHolWCQBDsCL8QtTWKGg_QFpS169WrqDGzktRi-_we6-6rVzbjerU27ZKww_SW0-b9RTz-dPJNcqsueMio8r6EqXUXhbLm_ch3XFSbDlGHDl_tqo

Cross-Origin Resource Sharing

Cross-origin resource sharing (CORS) is a W3C standard mechanism that uses additional HTTP headers to enable a web browser to gain permission to access resources from an Internet domain different from that from which the browser is currently accessing. CORS is a client-side security mechanism with well-documented security risks.

CDS Services and browser-based EHRs will require CORS support. A secure implementation guide for CORS is outside of the scope of this CDS Hooks specification. Organizations planning to implement CDS Hooks with CORS support are referred to the Cross-Origin Resource Sharing section of the OWASP HTML5 Security Cheat Sheet.

Extensions

The specification is not prescriptive about support for extensions. However, to support extensions, the specification reserves the name extension and will never define an element with that name, allowing implementations to use it to provide custom behavior and information. The value of an extension element MUST be a pre-coordinated JSON object.

For example, an extension on a request could look like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
{
   "hookInstance" : "d1577c69-dfbe-44ad-ba6d-3e05e953b2ea",
   "fhirServer" : "http://fhir.example.org:9080",
   "hook" : "patient-view",
   "context" : {
       "userId" : "Practitioner/example"
   },
   "extension" : {
      "com.example.timestamp": "2017-11-27T22:13:25Z",
      "myextension-practitionerspecialty" : "gastroenterology"
   }
}

As another example, an extension defined on the discovery response could look like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
{
  "services": [
    {
      "title": "Example CDS Service Discovery",
      "hook": "patient-view",
      "id": "patientview",
      "prefetch": {
        "patient": "Patient/{{context.patientId}}"
      },
      "description": "clinical decision support for patient view",
      "extension": {
          "example-client-conformance": "http://hooks.example.org/fhir/102/Conformance/patientview"
      }
    }
  ]
}

Hooks

Overview

As a specification, CDS Hooks does not prescribe a default or required set of hooks for implementers. Rather, the set of hooks defined here are merely a set of common use cases that were used to aid in the creation of CDS Hooks. The set of hooks defined here are not a closed set; anyone is able to define new hooks to fit their use cases and propose those hooks to the community. New hooks are proposed in a prescribed format using the documentation template by submitting a pull request. Hooks are versioned, and mature according to the Hook Maturity Model.

Note that each hook (e.g. medication-prescribe) represents something the user is doing in the EHR and multiple CDS Services might respond to the same hook (e.g. a "price check" service and a "prior authorization" service might both respond to medication-prescribe).

Hook context and prefetch

What's the difference?

A discrete user workflow or action within the EHR often naturally includes a set of contextual data. This context can contain both required and optional data and is specific to a hook. Additionally, the context data is relevant to most CDS Services subscribing to the hook.

When the context data relates to a FHIR data type, it is important not to conflate context and prefetch. For instance, imagine a hook for opening a patient's chart. This hook should include the FHIR identifier of the patient whose chart is being opened, not the full patient FHIR resource. In this case, the FHIR identifier of the patient is appropriate as CDS Services may not be interested in details about the patient resource but instead other data related to this patient. Or, a CDS Service may only need the full patient resource in certain scenarios. Therefore, including the full patient resource in context would be unnecessary. For CDS Services that want the full patient resource, they can request it to be prefetched or fetch it as needed from the FHIR server.

Consider another hook for when a new patient is being registered. In this case, it would likely be appropriate for the context to contain the full FHIR resource for the patient being registered as the patient may not be yet recorded in the EHR (and thus not available from the FHIR server) and CDS Services using this hook would predominantly be interested in the details of the patient being registered.

Additionally, consider a PGX CDS Service and a Zika screening CDS Service, each of which is subscribed to the same hook. The context data specified by their shared hook should contain data relevant to both CDS Services; however, each service will have other specific data needs that will necessitate disparate prefetch requests. For instance, the PGX CDS Service likely is interested in genomics data whereas the Zika screening CDS Service will want Observations.

In summary, context is data specific to a hook and universally relevant to all CDS Services subscribed to said hook. Prefetch data is unique to individual CDS Services and supplements the data from context.

Prefetch tokens

Often a prefetch template builds on the contextual data associated with the hook. For example, a particular CDS Service might recommend guidance based on a patient's conditions when the chart is opened. The FHIR query to retrieve these conditions might be Condition?patient=123. In order to express this as a prefetch template, the CDS Service must express the FHIR identifier of the patient as a token so that the EHR can replace the token with the appropriate value. When context fields are used as tokens, their token name MUST be context.name-of-the-field. For example, given a context like:

1
2
3
"context" : {
  "patientId": "123"
}

The token name would be {{context.patientId}}. Again using our above conditions example, the complete prefetch template would be Condition?patient={{context.patientId}}.

Only the first level fields in context may be considered for tokens. Hook creators MUST document which fields in the context are supported as tokens. If a context field can be tokenized, the value of the context field MUST be a data type that can placed into a FHIR query (eg, string, number, etc).

For example, given the following context that contains amongst other things, a Patient FHIR resource:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
"context" : {
  "encounterId": "456",
  "patient": {
    "resourceType": "Patient",
    "id": "123",
    "active": true,
    "name": [
      {
        "use": "official",
        "family": "Watts",
        "given": [
          "Wade"
        ]
      }
    ],
    "gender": "male",
    "birthDate": "2024-08-12"
  }
}

Only the encounterId field in this example is eligible to be a prefetch token as it is a first level field and the datatype (string) can be placed into the FHIR query. The Patient.id value in the context is not eligible to be a prefetch token because it is not a first level field. If the hook creator intends for the Patient.id value to be available as a prefetch token, it must be made available as a first level field. Using the aforementioned example, we simply add a new patientId field:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
"context" : {
  "patientId": "123",
  "encounterId": "456",
  "patient": {
    "resourceType": "Patient",
    "id": "123",
    "active": true,
    "name": [
      {
        "use": "official",
        "family": "Watts",
        "given": [
          "Wade"
        ]
      }
    ],
    "gender": "male",
    "birthDate": "2024-08-12"
  }
}

Hook Definition Format

Hooks are defined in the following format.

hook-name-expressed-as-noun-verb

The name of the hook SHOULD succinctly and clearly describe the activity or event. Hook names are unique so hook creators SHOULD take care to ensure newly proposed hooks do not conflict with an existing hook name. Hook creators SHALL name their hook with reverse domain notation (e.g. org.example.patient-transmogrify) if the hook is specific to an organization. Reverse domain notation SHALL not be used by a standard hooks catalog.

When naming hooks, the name should start with the subject (noun) of the hook and be followed by the activity (verb). For example, patient-view (not view-patient) or medication-prescribe (not prescribe-medication).

Workflow

Describe when this hook occurs in a workflow. Hook creators SHOULD include as much detail and clarity as possible to minimize any ambiguity or confusion amongst implementors.

Context

Describe the set of contextual data used by this hook. Only data logically and necessarily associated with the purpose of this hook should be represented in context.

All fields defined by the hook's context MUST be defined in a table where each field is described by the following attributes:

  • Field: The name of the field in the context JSON object.
  • Optionality: A string value of either REQUIRED or OPTIONAL
  • Prefetch Token: A string value of either Yes or No, indicating whether this field can be tokenized in a prefetch template.
  • Type: The type of the field in the context JSON object, expressed as the JSON type, or the name of a FHIR Resource type. Valid types are boolean, string, number, object, array, or the name of a FHIR resource type. When a field can be of multiple types, type names MUST be separated by a pipe (|)
  • Description: A functional description of the context value. If this value can change according to the FHIR version in use, the description SHOULD describe the value for each supported FHIR version.

The below illustrates a sample table.

Field Optionality Prefetch Token Type Description
someField REQUIRED Yes string A clear description of the value of this field.
anotherField OPTIONAL No number A clear description of the value of this field.
someObject REQUIRED No object A clear description of the value of this field.
moreObjects OPTIONAL No array A clear description of the items in this array.
allFHIR OPTIONAL No object A FHIR Bundle of the following FHIR resources using a specific version of FHIR.

FHIR resources in context

When potentially multiple FHIR resources value a single context field, these resources SHOULD be formatted as a FHIR Bundle. For example, multiple FHIR resources are necessary to describe all of the orders under review in the order-review hook's orders field. Hook definitions SHOULD prefer the use of FHIR Bundles over other bespoke data structures.

Often, context is populated with in-progress or in-memory data that may not yet be available from the FHIR server. For example, medication-prescribe and order-review define context fields containing FHIR resources that represent draft resources. In this case, the EHR should only provide these draft resources and not the full set of orders available from its FHIR server. The CDS service MAY pre-fetch or query for FHIR resources with other statuses.

All FHIR resources in context MUST be based on the same FHIR version.

Examples

Hook creators SHOULD include examples of the context.

1
2
3
4
5
6
7
8
9
"context":{
  "someField":"foo",
  "anotherField":123,
  "someObject": {
    "color": "red",
    "version": 1
  },
  "moreObjects":[]
}

If the context contains FHIR data, hook creators SHOULD include examples across multiple versions of FHIR if differences across FHIR versions are possible.

Hook Maturity Model

The intent of the CDS Hooks Maturity Model is to attain broad community engagement and consensus, before a hook is labeled as mature, that the hook is necessary, implementable, and worthwhile to the CDS services and CDS clients that would reasonably be expected to use it. Implementer feedback should drive the maturity of new hooks. Diverse participation in open developer forums and events, such as HL7 FHIR Connectathons, is necessary to achieve significant implementer feedback. The below criteria will be evaluated with these goals in mind.

The Hook maturity levels use the term CDS client to generically refer to the clinical workflow system in which a CDS services returned cards are displayed.

Maturity Level Maturity title Requirements
0 Draft Hook is defined according to the hook definition format.
1 Submitted The above, and … Hook definition is written up as a github pull request using the Hook template and community feedback is solicited on the zulip CDS Hooks stream.
2 Tested The above, and … The hook has been tested and successfully supports interoperability among at least one CDS client and two independent CDS services using semi-realistic data and scenarios (e.g. at a FHIR Connectathon). The github pull request defining the hook is approved and published.
3 Considered The above, and … At least 3 distinct organizations recorded ten distinct implementer comments (including a github issue, tracker item, or comment on the hook definition page), including at least two CDS clients and three independent CDS services. The hook has been tested at two connectathons.
4 Documented The above, and … The author agrees that the artifact is sufficiently stable to require implementer consultation for subsequent non-backward compatible changes. The hook is implemented in the standard CDS Hooks sandbox and multiple prototype projects. The Hook specification SHALL:
      Identify a broad set of example contexts in which the hook may be used with a minimum of three, but as many as 8-10.
      Clearly differentiate the hook from similar hooks or other standards to help an implementer determine if the hook is correct for their scenario.
      Explicitly document example scenarios when the hook should not be used.
5 Mature The above, and ... The hook has been implemented in production in at least two CDS clients and three independent CDS services. An HL7 working group ballots the hook and the hook has passed HL7 STU ballot.
6 Normative The above, and ... the responsible HL7 working group and the CDS working group agree the material is ready to lock down and the hook has passed HL7 normative ballot

Changes to the Definition of a Hook (Hook Versioning)

Each hook MUST include a Metadata table at the beginning of the hook with the specification version and hook version as described in the following sections.

Specification Version

Because hooks are such an integral part of the CDS Hooks specification, hook definitions are associated with specific versions of the specification. The hook definition MUST include the version (or versions) of the CDS Hooks specification that it is defined to work with.

1
specificationVersion | 1.0

Because the specification itself follows semantic versioning, the version specified here is a minimum specification version. In other words, a hook defined to work against 1.0 should continue to work against the 1.1 version of CDS Hooks. However, a hook that specifies 1.1 would not be expected to work in a CDS Hooks 1.0 environment.

Hook Version

To enable tracking of changes to hook definitions, each hook MUST include a version indicator, expressed as a string.

1
hookVersion | 1.0

To help ensure the stability of CDS Hooks implementations, once a hook has been defined (i.e. published with a particular name so that it is available for implementation), breaking changes MUST NOT be made. This means that fields can be added and restrictions relaxed, but fields cannot be changed, and restrictions cannot be tightened.

In particular, the semantics of a hook (i.e. the meaning of the hook from the perspective of the EHR) cannot be changed. EHRs that implement specific hooks are responsible for ensuring the hook is called from the appropriate point in the workflow.

Note that this means that the name of the hook carries major version semantics. That is not to say that the name must include the major version, that is left as a choice to authors of the specification. For example, following version 1.x, the major version MAY be included in the name as "-2", "-3", etc. Eg: patient-view-2, patient-view-3, etc. Clean hook names increase usability. Ideally, an active hook name accurately defines the meaning and workflow of the hook in actual words.

The following types of changes are possible for a hook definition:

Change Version Impact
Clarifications and corrections to documentation that do not impact functionality Patch
Change of prefetch token status of an existing context field Patch
Addition of a new, REQUIRED field to the context Major
Addition of a new, OPTIONAL field to the context Minor
Change of optionality of an existing context field Major
Change of type or cardinality of an existing context field Major
Removal of an existing context field Major
Change of semantics of an existing context field Major
Change of semantics of the hook Major

When a major change is made, the hook definition MUST be published under a new name. When a minor or patch change is made, the hook version MUST be updated. Hook definers MUST use semantic versioning to communicate the impact of changes in an industry standard way.

Change Log

Changes made to a hook MUST be documented in a change log to ensure hook consumers can track what has been changed over the life of a hook. The change log MUST contain the following elements:

  • Version: The version of the change
  • Description: A description of the change and its impact

For example:

Version Description
1.1 Added new context variable
1.0.1 Clarified context variable usage
1.0 Initial Release