CDS Hooks, published by Clinical Decision Support WG. This guide is not an authorized publication; it is the continuous build for version 2.0.1 built by the FHIR (HL7® FHIR® Standard) CI Build. This version is based on the current content of https://github.com/HL7/cds-hooks/ and changes regularly. See the Directory of published versions
Official URL: https://cds-hooks.hl7.org/ImplementationGuide/hl7.fhir.uv.cds-hooks | Version: 2.0.1 | |||
Draft as of 2024-11-08 | Computable Name: CDSHooks |
Continuous Improvement Build
This is the continuous integration, community release of the CDS Hooks specification. All stable releases are available at https://cds-hooks.hl7.org.
CDS Hooks is the copyright of HL7 International and Boston Children's Hospital. The CDS Hooks specification is licensed under a Creative Commons Attribution 4.0 International License. All source code for CDS Hooks is available at https://github.com/cds-hooks.
This specification describes a "hook"-based pattern for invoking decision support from within a clinician's workflow. The API supports:
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. Further, the key word "CONDITIONAL" indicates that a particular item is either REQUIRED or OPTIONAL, based upon another item.
All data exchanged through production RESTful APIs MUST be sent and received as JSON (JavaScript Object Notation) structures and are transmitted over HTTPS. See Security and Safety section.
Null and empty JSON elements
If a JSON attribute is defined as OPTIONAL, and does not have a value, implementers MUST omit it. For instance, OPTIONAL JSON string and array attributes are omitted rather than having a null or empty value.
Unless otherwise specified, JSON attribute values SHALL NOT be null or empty, so null
, ""
, []
, or {}
are prohibited. If a JSON attribute is defined with as OPTIONAL, and does not have a value, implementers SHALL omit it.
This specification describes a "hook"-based pattern for invoking decision support from within a clinician's workflow. The API supports:
The main concepts of the specification are Services, CDS Clients, and Cards.
A CDS Service is a service that provides recommendations and guidance through the RESTful APIs described by this specification. The primary APIs are Discovery, which allows a CDS Developer to publish the types of CDS Services it provides. The Service API that CDS Clients use to request decision support. The Feedback API through which services learn the outcomes of their recommendations and guidance.
A CDS Client is an Electronic Health Record (EHR), or other clinical information system that uses decision support by calling CDS Services at specific points in the application's workflow called hooks. Each hook defines the hook context (contextual information available within the CDS Client and specific to the workflow) that is provided as part of the request. Each service advertises which hooks it supports and what prefetch data (information needed by the CDS Service to determine what decision support should be presented) it requires. In addition, CDS Clients typically provide the FHIR resource server location and associated authorization information as part of the request to enable services to request additional information.
Decision support is then returned to the CDS Client in the form of cards, which the CDS Client MAY display to the end-user as part of their workflow. Cards may be informational, or they may provide suggestions that the user may accept or reject they may provide a link to additional information or even launch a SMART app when additional user interaction is required.
A CDS Service is discoverable via a stable endpoint by CDS Clients. The Discovery endpoint includes 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 exposes its discovery endpoint at:
{baseURL}/cds-services
The Discovery endpoint SHALL always be available at {baseUrl}/cds-services
. For example, if the baseUrl
is https://example.com, the CDS Client can retrieve the list of CDS Services by invoking:
GET https://example.com/cds-services
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. |
If your CDS server hosts no CDS Services, the discovery endpoint should return a 200 HTTP response with an empty array of 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 the CDS Client to perform 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. |
usageRequirements |
OPTIONAL | string | Human-friendly description of any preconditions for the use of this CDS Service. |
Note that a CDS server can host multiple entries of CDS service with the same id
for different hook
s. This allows a service to update its advice based on changes in workflow as discussed in update stale guidance.
Code | Description |
---|---|
200 OK |
A successful response. |
CDS Services MAY return other HTTP statuses, specifically 4xx and 5xx HTTP error codes.
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/{{context.patientId}}"
}
},
{
"hook": "order-select",
"title": "Order Echo CDS Service",
"description": "An example of a CDS Service that simply echoes the order(s) being placed",
"id": "order-echo",
"prefetch": {
"patient": "Patient/{{context.patientId}}",
"medications": "MedicationRequest?patient={{context.patientId}}"
}
},
{
"hook": "order-sign",
"title": "Pharmacogenomics CDS Service",
"description": "An example of a more advanced, precision medicine CDS Service",
"id": "pgx-on-order-sign",
"usageRequirements": "Note: functionality of this CDS Service is degraded without access to a FHIR Restful API as part of CDS recommendation generation."
}
]
}
A CDS Client SHALL call a CDS Service by POST
ing 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}
. CDS Clients may add additional requirements for the triggering of a hook, based upon the user, workflow, CDS Service or other reasons (e.g. if the service is provided by a payer, the patient has active coverage with that payer). See Trusting CDS Services for additional considerations.
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 universally unique identifier (UUID) for this particular hook call (see more information below). |
fhirServer |
CONDITIONAL | URL | The base URL of the CDS Client's FHIR server. If fhirAuthorization is provided, this field is REQUIRED. The scheme MUST be https when production data is exchanged. |
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 id of the Patient being viewed. For details, see the Hooks specific specification page (example: patient-view). |
prefetch |
OPTIONAL | object | The FHIR data that was prefetched by the CDS Client (see more information below). |
While working in the CDS Client, 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.
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",
"fhirAuthorization": {
"access_token": "some-opaque-fhir-access-token",
"token_type": "Bearer",
"expires_in": 300,
"scope": "user/Patient.read user/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
}
}
}
CDS Services require specific FHIR resources in order to compute the recommendations the CDS Client requests. If real-world performance were no issue, a CDS Client could launch a CDS Service passing only context data (such as the current user and patient ids), and the CDS Service could obtain authorization to access the CDS Client's FHIR API, retrieving any resources required via FHIR read or search interactions. Given that CDS Services SHOULD respond quickly (on the order of 500 ms.), this specification defines mechanisms that allow a CDS Service to request and obtain FHIR resources more efficiently.
Two optional methods are provided. In the first method, FHIR resources MAY be obtained by passing "prefetched" data from the CDS Client to the CDS Service in the service call. If data is to be prefetched, the CDS Service registers a set of "prefetch templates" with the CDS Client, as described in the Prefetch Template section below. These "prefetch templates" are defined in the CDS Service discovery response. The FHIR resources are passed as key-value pairs, with each key matching a key described in the discovery response, and each value being a FHIR resource. Note that in the case of searches, this resource may be a searchset
Bundle.
The second method enables the CDS Service to retrieve FHIR resources for itself, without the need to request and obtain its own authorization. If the CDS Client decides to have the CDS Service fetch its own FHIR resources, the CDS Client 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 CDS Client's FHIR server to obtain the required resources. Some CDS Clients MAY pass prefetched data, along with a bearer token for the CDS Service to use if additional resources are required.
Each CDS Client SHOULD decide which approach, or combination, is preferred, based on performance considerations and assessment of attendant security and safety risks. CDS Services should be capable of accessing FHIR resources via either prefetch or from the CDS Client's FHIR server. For more detail, see the FHIR Resource Access section below.
Similarly, each CDS Client will decide what FHIR resources to authorize and to prefetch, based on the CDS Service discovery response's "prefetch" request and on the provider's assessment of the "minimum necessary." The CDS Client 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.
A prefetch template is a FHIR read
or search
request that describes relevant data needed by the CDS Service. For example, the following is a prefetch template for hemoglobin A1c observations:
Observation?patient={{context.patientId}}&code=4548-4&_count=1&sort:desc=date
To allow for prefetch templates that are dependent on the workflow context, prefetch templates may include references to context using prefetch tokens. In the above example, {{context.patientId}}
is a prefetch token.
The prefetch
field of a CDS Service discovery response defines the set of prefetch templates for that service, providing a prefetch key for each one that is used to identify the prefetch data in the CDS request. For example:
{
"prefetch": {
"hemoglobin-a1c": "Observation?patient={{context.patientId}}&code=4548-4&_count=1&sort:desc=date"
}
}
In this prefetch
, hemoglobin-a1c
is the prefetch key for this prefetch template. For a complete worked example, see below.
A CDS Client MAY choose to honor zero, some, or all of the desired prefetch templates, and is free to choose the most appropriate source for these data. For example:
The CDS Client SHALL only provide access to resources that are within the user's authorized scope.
As part of preparing the request, a CDS Client processes each prefetch template it intends to satisfy by replacing the prefetch tokens in the prefetch template to construct a relative FHIR request URL. This specification is not prescriptive about how this request is actually processed. The relative URL may be appended to the base URL for the CDS Client's FHIR server and directly invoked, or the CDS Client may use internal infrastructure to satisfy the request in the same way that invoking against the FHIR server would.
Regardless of how the CDS Client 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 CDS Client's FHIR server using the parameterized prefetch template.
Note that this means that CDS services will receive only the information they have requested and are authorized to receive. Prefetch data for other services registered to the same hook MUST NOT be provided. In other words, services SHALL only receive the data they requested in their prefetch.
The resulting response is passed along to the CDS Service using the prefetch
parameter (see below.
Note that a CDS Client MAY paginate prefetch results. The intent of allowing pagination is to ensure that prefetch queries that may be too large for a single payload can still be retrieved by the service. The decision to paginate and the size of pages is entirely at the CDS Client's discretion. CDS Clients are encouraged to only use pagination when absolutely necessary, keeping performance and user experience in mind.
The CDS Client MUST NOT send any prefetch template key that it chooses not to satisfy. If the CDS Client encounters errors prefetching the requested data, OperationOutcome(s) SHOULD be used to communicate those errors to prevent the CDS Service from incurring an unneeded follow-up query. CDS Clients MUST omit the prefetch key if relevant details cannot be provided (e.g. intermittent connectivity issues). CDS Services SHOULD check any prefetched data for the existence of OperationOutcomes. If the CDS Client has no data to populate a template prefetch key, the prefetch template key MUST have a value of null. In case the prefetch url is a single-resource request, the search result may be null, otherwise it is a search and could be a bundle with zero entries.
It is the CDS Service's responsibility to check prefetched data against its template to determine what requests were satisfied (if any) and to programmatically 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.
A prefetch token is a placeholder in a prefetch template that is replaced by information from the hook's context to construct the FHIR URL used to request the prefetch data.
Prefetch tokens MUST be delimited by {{
and }}
, and MUST contain only the qualified path to a hook context field or one of the following user identifiers: userPractitionerId
, userPractitionerRoleId
, userPatientId
, or userRelatedPersonId
.
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 example, {{context.medication.id}}
is not a valid prefetch token because it attempts to access the id
field of the medication
field.
A prefetch template enables a CDS Service to learn more about the current user through a FHIR read, like so:
{
"prefetch": {
"user": "{{context.userId}}"
}
}
or though a FHIR search:
{
"prefetch": {
"user": "PractitionerRole?_id={{userPractitionerRoleId}}&_include=PractitionerRole:practitioner"
}
}
A prefetch template may include any of the following prefetch tokens:
Token | Description |
---|---|
{{userPractitionerId}} |
FHIR id of the Practitioner resource corresponding to the current user. |
{{userPractitionerRoleId}} |
FHIR id of the PractitionerRole resource corresponding to the current user. |
{{userPatientId}} |
FHIR id of the Patient resource corresponding to the current user. |
{{userRelatedPersonId}} |
FHIR id of the RelatedPerson resource corresponding to the current user. |
No single FHIR resource represents a user, rather Practitioner and PractitionerRole may be jointly used to represent a provider, and Patient or RelatedPerson are used to represent a patient or their proxy. Hook definitions typically define a context.userId
field and corresponding prefetch token.
To reduce the implementation burden on CDS Clients that support CDS Services, this specification RECOMMENDS that prefetch queries only use a subset of the full functionality available in the FHIR specification. When using this subset, valid prefetch templates MUST only make use of:
Patient
, Practitioner
, or Encounter
)patient={{context.patientId}}
patient={{context.patientId}}
)code=4548-4
) and optionally the :in
modifier (no other modifiers for token parameters)date
, dateTime
, instant
, or Period
types only, and using only the prefixes eq
, lt
, gt
, ge
, le
_count
parameter to limit the number of results returned_sort
parameter to allow for most recent and first queriesOften 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 id of the patient as a token so that the CDS Client 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:
{
"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.
For example, given the following context that contains amongst other things, a MedicationRequest FHIR resource:
{
"context": {
"encounterId": "456",
"draftOrders": {
"resourceType": "Bundle",
"entry": [ {
"resource": {
"resourceType": "MedicationRequest",
"id": "123",
"status": "draft",
"intent": "order",
"medicationCodeableConcept": {
"coding": [ {
"system": "http://www.nlm.nih.gov/research/umls/rxnorm",
"code": "617993",
"display": "Amoxicillin 120 MG/ML / clavulanate potassium 8.58 MG/ML Oral Suspension"
}]},
"subject": {
"reference": "Patient/1288992"
}
}
}
]
}
}
}
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 MedicationRequest.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 MedicationRequest.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 medicationRequestId
field:
{
"context": {
"medicationRequestId": "123",
"encounterId": "456",
"draftOrders": {
"resourceType": "Bundle",
"entry": [ {
"resource": {
"resourceType": "MedicationRequest",
"id": "123",
"status": "draft",
"intent": "order",
"medicationCodeableConcept": {
"coding": [ {
"system": "http://www.nlm.nih.gov/research/umls/rxnorm",
"code": "617993",
"display": "Amoxicillin 120 MG/ML / clavulanate potassium 8.58 MG/ML Oral Suspension"
}]},
"subject": {
"reference": "Patient/1288992"
}
}
}
]
}
}
}
{
"prefetch": {
"patient": "Patient/{{context.patientId}}",
"hemoglobin-a1c": "Observation?patient={{context.patientId}}&code=4548-4&_count=1&sort:desc=date",
"diabetes-type2": "Condition?patient={{context.patientId}}&code=44054006&category=problem-list-item&status=active",
"user": "PractitionerRole?_id={{userPractitionerRoleId}}"
}
}
Here is an example prefetch field from a CDS Service discovery endpoint. The goal is to know, at call time:
Key | Description |
---|---|
patient |
Patient demographics. |
hemoglobin-a1c |
Most recent Hemoglobin A1c reading for this patient. |
diabetes-type2 |
If the patient has an active condition of diabetes mellitus on their problem list. |
user |
Information on the current user. |
{
"prefetch": {
"patient": {
"resourceType": "Patient",
"gender": "male",
"birthDate": "1974-12-25",
"...": "<snipped for brevity>"
},
"hemoglobin-a1c": {
"resourceType": "Bundle",
"type": "searchset",
"entry": [
{
"resource": {
"resourceType": "Observation",
"code": {
"coding": [
{
"system": "http://loinc.org",
"code": "4548-4",
"display": "Hemoglobin A1c"
}
]
},
"...": "<snipped for brevity>"
}
}
]
},
"user": "123"
}
}
The CDS Hooks request is augmented to include two prefetch values, where the dictionary
keys match the request keys (patient
and hemoglobin-a1c
in this case).
Note that the missing diabetes-type2
key indicates that either the CDS Client 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 the FHIR resource representing the user from the FHIR server (if required).
If the CDS Client provides both fhirServer
and fhirAuthorization
request parameters, the CDS Service MAY use the FHIR server to obtain any FHIR resources for which it's authorized, beyond those provided by the CDS Client 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 CDS Client's Authorization server using the SMART launch workflow, as described in SMART App Launch Implementation Guide.
Like SMART on FHIR, CDS Hooks requires that CDS Services present a valid access token to the FHIR server with each API call. Thus, a CDS Service requires an access token before communicating with the CDS Client'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 CDS Client wants to provide the CDS Service direct access to FHIR resources, the CDS Client creates or obtains 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 CDS Client remains in control of providing 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 CDS Client MUST be limited to the same restrictions and authorizations afforded the current user. As such, the access token SHALL be scoped to:
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 CDS Client 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 CDS Client's authorization server. |
'patient` | CONDITIONAL | string | If the granted SMART scopes include patient scopes (i.e. "patient/"), the access token is restricted to a specific patient. This field SHOULD be populated to identify the FHIR id of that patient. |
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. CDS Clients SHOULD revoke an issued access token upon the completion of the CDS Hooks request/response to limit the validity period of the token.
Below is an example fhirAuthorization
parameter:
{
"fhirAuthorization": {
"access_token": "some-opaque-fhir-access-token",
"token_type": "Bearer",
"expires_in": 300,
"scope": "user/Patient.read user/Observation.read",
"subject": "cds-service4"
}
}
For successful responses, CDS Services SHALL respond with a 200 HTTP response with an object containing a cards
array and optionally a systemActions
array as described below.
Each card contains decision support guidance from the CDS Service. Cards are intended for display to an end user. The data format of a card defines a very minimal set of required attributes with several more optional attributes to suit a variety of use cases, such as: narrative informational decision support, actionable suggestions to modify data, and links to SMART apps.
Note that because the CDS client may be invoking multiple services from the same hook, there may be multiple responses related to the same information. This specification does not address these scenarios specifically; both CDS Services and CDS Clients should consider the implications of multiple CDS Services in their integrations and are invited to consider card attributes when determining prioritization and presentation options.
Code | Description |
---|---|
200 OK |
A successful response. |
412 Precondition Failed |
The CDS Service is unable to retrieve the necessary FHIR data to execute its decision support, either through a prefetch request or directly calling the FHIR server. |
CDS Services MAY return other HTTP statuses, specifically 4xx and 5xx HTTP error codes.
Field | Optionality | Type | Description |
---|---|---|---|
cards |
REQUIRED | array of Cards | 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 CDS Client decides how to display cards, but this specification recommends displaying suggestions using buttons, and links using underlined text. |
systemActions |
OPTIONAL | array of Actions | An array of Actions that the CDS Service proposes to auto-apply. Each action follows the schema of a card-based suggestion.action . The CDS Client decides whether to auto-apply actions. |
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, for example:
{
"cards": []
}
Clients SHOULD remove cards
returned by previous invocations of a hook
to a service with the same id
when a new hook
is triggered (see update stale guidance).
Each Card is described by the following attributes.
Field | Optionality | Type | Description |
---|---|---|---|
uuid |
OPTIONAL | string | Unique identifier of the card. MAY be used for auditing and logging cards and SHALL be included in any subsequent calls to the CDS service's feedback endpoint. |
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 CDS Client 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 CDS Client 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 a medication currently being prescribed, for the order-sign activity). If suggestions are present, selectionBehavior MUST also be provided. |
selectionBehavior |
CONDITIONAL | string | Describes the intended selection behavior of the suggestions in the card. Allowed values are: at-most-one , indicating that the user may choose none or at most one of the suggestions; any , indicating that the end user may choose any number of suggestions including none of them and all of them. CDS Clients that do not understand the value MUST treat the card as an error. |
overrideReasons |
OPTIONAL | array of Coding | Override reasons can be selected by the end user when overriding a card without taking the suggested recommendations. The CDS service MAY return a list of override reasons to the CDS client. If override reasons are present, the CDS Service MUST populate a display value for each reason's Coding. The CDS Client SHOULD present these reasons to the clinician when they dismiss a card. A CDS Client MAY augment the override reasons presented to the user with its own reasons. |
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. |
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 card.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. The CDS Client may ignore or scale the image during display as appropriate for user experience. |
topic |
OPTIONAL | Coding | A topic describes the content of the card by providing a high-level categorization that can be useful for filtering, searching or ordered display of related cards in the CDS client's UI. This specification does not prescribe a standard set of topics. |
Below is an example source
parameter:
{
"source": {
"label": "Zika Virus Management",
"url": "https://example.com/cdc-zika-virus-mgmt",
"icon": "https://example.com/cdc-zika-virus-mgmt/100.png",
"topic": {
"system": "http://example.org/cds-services/fhir/CodeSystem/topics",
"code": "12345",
"display": "Mosquito born virus"
}
}
}
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 CDS Client might render this as the text on a button tied to this suggestion). |
uuid |
OPTIONAL | string | Unique identifier, used for auditing and logging suggestions. |
isRecommended |
OPTIONAL | boolean | When there are multiple suggestions, allows a service to indicate that a specific suggestion is recommended from all the available suggestions on the card. CDS Hooks clients may choose to influence their UI based on this value, such as pre-selecting, or highlighting recommended suggestions. Multiple suggestions MAY be recommended, if card.selectionBehavior is any . |
actions |
OPTIONAL | array of Actions | 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. When a suggestion contains multiple actions, the actions SHOULD be processed as per FHIR's rules for processing transactions with the CDS Client's fhirServer as the base url for the inferred full URL of the transaction bundle entries. (Specifically, deletes happen first, then creates, then updates). |
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 |
CONDITIONAL | object | A FHIR resource. When the type attribute is create , the resource attribute SHALL contain a new FHIR resource to be created. For update , this holds the updated resource in its entirety and not just the changed fields. Use of this field to communicate a string of a FHIR id for delete suggestions is DEPRECATED and resourceId SHOULD be used instead. |
resourceId |
CONDITIONAL | string | A relative reference to the relevant resource. SHOULD be provided when the type attribute is delete . |
The following example illustrates a create action:
{
"type": "create",
"description": "Create a prescription for Acetaminophen 250 MG",
"resource": {
"resourceType": "MedicationRequest",
"id": "medrx001",
"...": "<snipped for brevity>"
}
}
The following example illustrates an update action:
{
"type": "update",
"description": "Update the order to record the appropriateness score",
"resource": {
"resourceType": "ServiceRequest",
"id": "procedure-request-1",
"...": "<snipped for brevity>"
}
}
The following example illustrates a delete action:
{
"type": "delete",
"description": "Remove the inappropriate order",
"resourceId": "ServiceRequest/procedure-request-1"
}
overrideReasons is an array of Coding that captures a codified set of reasons an end user may select from as the rejection reason when rejecting the advice presented in the card. When using the coding object to represent a reason, CDS Services MUST provide a human readable text in the display property and CDS Clients MAY incorporate it into their user interface.
This specification does not prescribe a standard set of override reasons; implementers are encouraged to submit suggestions for standardization.
{
"overrideReasons": [
{
"code": "reason-code-provided-by-service",
"system": "http://example.org/cds-services/fhir/CodeSystem/override-reasons",
"display": "Patient refused"
},
{
"code": "12354",
"system": "http://example.org/cds-services/fhir/CodeSystem/override-reasons",
"display": "Contraindicated"
}
]
}
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 CDS Client 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 CDS Client 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. CDS Client support for appContext requires additional coordination with the authorization server that is not described or specified in CDS Hooks nor SMART. |
autolaunchable |
OPTIONAL | boolean | This field serves as a hint to the CDS Client suggesting this link be immediately launched, without displaying the card and without manual user interaction. Note that CDS Hooks cards which contain links with this field set to true, may not be shown to the user. Sufficiently advanced CDS Clients may support automatically launching multiple links or multiple cards. Implementer guidance is requested to determine if the specification should preclude these advanced scenarios. |
autolaunchable
and user experienceThe intent of this optional feature is to improve individual user experience by removing the otherwise unnecessary click of a link by the user. Appropriate support of this feature includes guardrails from both the CDS Service developer and the CDS Client, as well as additional local control by the organization using the service.
The CDS Client ultimately determines if a link can be automatically launched, taking into consideration user interface needs, workflow considerations, or even absence of support for this optional feature. If a CDS Hooks response contains guidance in addition to an autolaunchable link, it's the CDS Service's responsibility to ensure that any decision support that exists in the CDS Hooks response's card(s) is communicated via the launched app.
A systemAction
is the same Action which may be returned in a suggestion, but is instead returned alongside the array of cards. A systemAction
is not presented to the user within a card, but rather may be auto-applied without user intervention.
{
"cards": [],
"systemActions": [
{
"type": "update",
"resource": {
"resourceType": "ServiceRequest",
"id": "example-MRI-59879846",
"...": "<snipped for brevity"
}
}
]
}
Example response
{
"cards": [
{
"uuid": "4e0a3a1e-3283-4575-ab82-028d55fe2719",
"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"
},
"overrideReasons": [
{
"code": "reason-code-provided-by-service",
"system": "http://example.org/cds-services/fhir/CodeSystem/override-reasons",
"display": "Patient refused"
},
{
"code": "12354",
"system": "http://example.org/cds-services/fhir/CodeSystem/override-reasons",
"display": "Contraindicated"
}
]
}
]
}
Example response using
autolaunchable
{
"cards": [
{
"uuid": "4e0a3a1e-3283-4575-ab82-028d55fe2719",
"summary": "Lung cancer screening shared decision making",
"detail": "Patient is a current smoker with a 20 pack/year history. Consider advising patient to complete lung cancer screening. The Lung Cancer Screening Shared Decision Making App (LCSSDM) has been proven to increase patient followthrough for screening.",
"source": {
"label": "Lung Cancer Screening Shared Decision Making App",
"url": "https://example.com/LCS",
"icon": "https://example.com/img/icon-100px.png"
},
"links": [
{
"label": "Github",
"url": "https://github.com",
"type": "absolute",
"autolaunchable": true
}
]
}
]
}
Once a CDS Hooks Service responds to a hook by returning a card, the service has no further interaction with the CDS Client. The acceptance of a suggestion or rejection of a card is valuable information to enable a service to improve its behavior towards the goal of the end-user having a positive and meaningful experience with the CDS. A feedback endpoint enables suggestion tracking & analytics. A CDS Service MAY support a feedback endpoint; a CDS Client SHOULD be capable of sending feedback.
Upon receiving a card, a user may accept its suggestions, ignore it entirely, or dismiss it with or without an override reason. Note that while one or more suggestions can be accepted, an entire card is either ignored or overridden.
Typically, an end user may only accept (a suggestion), or override a card once; however, a card once ignored could later be acted upon. CDS Hooks does not specify the UI behavior of CDS Clients, including the persistence of cards. CDS Clients should faithfully report each of these distinct end-user interactions as feedback.
A CDS Client provides feedback by POSTing a JSON document. The feedback endpoint can be constructed from the CDS Service endpoint and a path segment of "feedback" as {baseUrl}/cds-services/{service.id}/feedback. The request to the feedback endpoint SHALL be an object containing an array.
Field | Description |
---|---|
feedback |
array of Feedback |
Each Feedback SHALL be described by the following attributes.
Field | Optionality | Type | Description |
---|---|---|---|
card |
REQUIRED | string | The card.uuid from the CDS Hooks response. Uniquely identifies the card. |
outcome |
REQUIRED | string | A value of accepted or overridden . |
acceptedSuggestions |
CONDITIONAL | array | An array of json objects identifying one or more of the user's AcceptedSuggestions. Required for accepted outcomes. |
overrideReason |
OPTIONAL | OverrideReason | A json object capturing the override reason as a Coding as well as any comments entered by the user. |
outcomeTimestamp |
REQUIRED | string | ISO8601 representation of the date and time in Coordinated Universal Time (UTC) when action was taken on the card, as profiled in section 5.6 of RFC3339. e.g. 1985-04-12T23:20:50.52Z |
The CDS Client can inform the service when one or more suggestions were accepted by POSTing a simple JSON object. The CDS Client authenticates to the CDS service as described in Trusting CDS Clients.
Upon the user accepting a suggestion (perhaps when she clicks a displayed label (e.g., button) from a "suggestion" card), the CDS Client informs the service by posting the card and suggestion uuid
s to the CDS Service's feedback endpoint with an outcome of accepted
.
To enable a positive clinical experience, the feedback endpoint may be called for multiple hook instances or multiple cards at the same time or even multiple times for a card or suggestion. Depending upon the UI and workflow of the CDS Client, a CDS Service may receive feedback for the same card instance multiple times.
Each AcceptedSuggestion is described by the following attributes.
Field | Optionality | Type | Description |
---|---|---|---|
id |
REQUIRED | string | The card.suggestion.uuid from the CDS Hooks response. Uniquely identifies the suggestion that was accepted. |
{
"feedback": [
{
"card": "4e0a3a1e-3283-4575-ab82-028d55fe2719",
"outcome": "accepted",
"acceptedSuggestions": [
{
"id": "e56e1945-20b3-4393-8503-a1a20fd73152"
}
],
"outcomeTimestamp": "2021-12-11T10:05:31Z"
}
]
}
If either the card or the suggestion has no uuid
, the CDS Client does not send a notification.
If the end-user doesn't interact with the CDS Service's card at all, the card is ignored. In this case, the CDS Client does not inform the CDS Service of the rejected guidance. Even with a card.uuid
, a suggestion.uuid
, and an available feedback service, the service is not informed (in part, because it may later be acted upon).
A CDS Client may enable the end user to override guidance without providing an explicit reason for doing so. The CDS Client can inform the service when a card was dismissed by specifying an outcome of overridden
without providing an overrideReason
. This may occur, for example, when the end user viewed the card and dismissed it without providing a reason why.
POST {baseUrl}/cds-services/{serviceId}/feedback
{
"feedback": [
{
"card": "f6b95768-b1c8-40dc-8385-bf3504b82ffb",
"outcome": "overridden",
"outcomeTimestamp": "2020-12-11T00:00:00Z"
}
]
}
The value of "card" above is the uuid from card.uuid
.
A CDS Client can inform the service when a card was rejected by POSTing an outcome of overridden
along with an overrideReason
to the service's feedback endpoint. The CDS Client may enable the clinician to provide an additional overrideReason
or to supplement the overrideReason
with a free text comment, supplied to the CDS Service in overrideReason.userComment
.
Each OverrideReason is described by the following attributes, in the feedback POST to the CDS Service.
Field | Optionality | Type | Description |
---|---|---|---|
reason |
CONDITIONAL | Coding | The Coding object representing the override reason selected by the end user. Required if user selected an override reason from the list of reasons provided in the Card (instead of only leaving a userComment). |
userComment |
OPTIONAL | string | The CDS Client may enable the clinician to further explain why the card was rejected with free text. That user comment may be communicated to the CDS Service as a userComment . |
POST {baseUrl}/cds-services/{serviceId}/feedback
{
"feedback":[
{
"card":"9368d37b-283f-44a0-93ea-547cebab93ed",
"outcome":"overridden",
"overrideReason":{
"reason":{
"code":"d7ecf885",
"system":"https://example.com/cds-hooks/override-reason-system"
},
"userComment":"A comment entered by the clinician."
},
"outcomeTimestamp":"2020-12-11T00:00:00Z"
}
]
}
All data exchanged through the RESTful APIs 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.
Security and safety risks associated with the CDS Hooks API include:
CDS Hooks defines a security model that addresses these risks by assuring that the identities of both the CDS Service and the CDS Client are authenticated to each other; by protecting confidential information and privileged authorizations shared between a CDS Client and a CDS Service; by recommending means of assuring data freshness; and by incorporating business mechanisms through which trust is established and maintained between a CDS Client and a CDS Service. As with any access to protected patient information, systems should ensure that they have appropriate authorization and audit mechanisms in place to support transparency of use of the data. For more information, refer to Security Best Practices.
Prior to enabling CDS Clients to request decision support from any CDS Service, the CDS Client vendor and/or provider organization is expected to perform due diligence on the CDS Service provider. Each CDS Client 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 CDS Client vendor/provider SHOULD maintain an "allow list" (and/or "deny list") of the CDS Services it has vetted, and the Card links that have been deemed safe to display from within the CDS Client context. Each provider organization is expected to work with its CDS Client 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 CDS Client 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, the CDS Services used and the hooks that will trigger them, data elements that will be made available through an access token passed by the CDS Client, and steps the CDS Service MUST take to protect patient data and access tokens. The CDS Service can be registered with the CDS Client's authorization server, in part to define the FHIR resources that the CDS Service has authorization to access. These business arrangements are documented in the service agreement.
Every interaction between a CDS Client and a CDS Service is initiated by the CDS Client 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 CDS Client and the CDS Service. Both the Discovery endpoint and individual CDS Service endpoints are TLS secured.
The CDS Client's FHIR server, using information provided by the authorization server, is responsible for enforcing restrictions on the information available to the CDS Service. Regardless of whether FHIR resources are prefetched or retrieved from the FHIR server, the CDS Client SHALL deny access to a requested resource if it is outside the user's authorized scope. If a CDS Client is satisfying prefetch requests from a CDS Service or sends a non-null fhirAuthorization
object to a CDS Service so that it can call the FHIR server, the CDS Service MUST be pre-registered with the authorization server protecting access to the FHIR server. Pre-registration includes registering a client identifier, and agreeing upon the scope of FHIR access that is minimally necessary to provide the clinical decision support required. This specification does not address how the CDS Client, authorization server, and CDS Service perform this pre-registration.
The service agreement negotiated between the CDS Client vendor/provider and the CDS Service provider will include obligations the CDS Client 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. In the absence of mutual TLS, only the CDS Service endpoint will be authenticated because the CDS Client initiates the TLS channel set-up. To enable the CDS Service to authenticate the identity of the CDS Client, CDS Hooks uses digitally signed JSON web tokens (JWT) (rfc7519). CDS Services SHOULD require authentication if invoking the service poses any risk of exposing sensitive data to the caller.
Each time a CDS Client transmits a request to a CDS Service which requires authentication, the request MUST include an Authorization
header presenting the JWT as a “Bearer” token:
Authorization: Bearer
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 CDS Client by the CDS Service.
The CDS Client 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 CDS Client'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 CDS Client, 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 CDS Client. (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 limit the CDS Clients they trust by maintaining an allowlist of iss
and jku
urls.
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 CDS Client 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 CDS Client 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 CDS Client MUST make its public key, expressed as a JSON Web Key (JWK), available 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 CDS Client 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 CDS Client and CDS Service SHALL communicate the JWK Set out-of-band.
The cryptographic signing algorithm of JWT is indicated in the alg
header field. JSON Web Algorithms (rfc7518) defines several cryptographic algorithms for use in signing JWTs and should be referenced by CDS Hooks implementers.
JWTs SHALL NOT be signed using the none
algorithm, referred to in rfc7518 as unsecured JSON Web Signatures, as the lack of a cryptographic signature does not provide any integrity protection. Such JWTs could not be used by a CDS Service to identity the CDS Client preventing an establishment of trust.
JWTs SHALL NOT be signed using any symmetric algorithm as these algorithms require the CDS Client and CDS Service to share a private key in order to verify the signature. For example, all HMAC based algorithms rely upon a shared private key and thus SHALL NOT be used to sign a JWT.
When choosing an algorithm to sign their JWTs, CDS Clients SHOULD consider not only the algorithms (and key sizes) that are recommended within the security industry, but also how well those algorithms are supported in the various programming languages and libraries that may be used by CDS Services.
At publication time of this specification, both ES384 and RS384 are RECOMMENDED for their regard within the larger security industry, strength, and support across popular programming languages and libraries. However, stronger and better algorithms are continually being introduced due to new threats, weaknesses, and increases in computing power. CDS Clients SHOULD continually re-evaluate their choice of an algorithm based upon these ever changing conditions.
CDS Services SHOULD consider the algorithms they understand and trust based upon their tolerance for risk.
An example JSON web token header, payload, and JWK set:
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/",
"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 key is used by the CDS Client 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:
Authorization: Bearer eyJhbGciOiJFUzM4NCIsInR5cCI6IkpXVCIsImtpZCI6ImV4YW1wbGUta2lkIiwiamt1IjoiaHR0cHM6Ly9maGlyLWVoci5leGFtcGxlLmNvbS9qd2tfdXJpIn0.eyJpc3MiOiJodHRwczovL2ZoaXItZWhyLmV4YW1wbGUuY29tLyIsImF1ZCI6Imh0dHBzOi8vY2RzLmV4YW1wbGUub3JnL2Nkcy1zZXJ2aWNlcy9zb21lLXNlcnZpY2UiLCJleHAiOjE0MjI1Njg4NjAsImlhdCI6MTMxMTI4MDk3MCwianRpIjoiZWUyMmIwMjEtZTFiNy00NjExLWJhNWItOGVlYzZhMzNhYzFlIiwidGVuYW50IjoiMmRkZDZjM2EtOGU5YS00NGM2LWEzMDUtNTIxMTFhZDMwMmEyIn0.d1WfLjGRKlcWB94l9do4cM8REXeYJLL6SGUBO8VHZhfM8mwKYP70EMxJ67War4TQblEpaQrp11wx5p7oPFm2ETYgCicS84vXWEIYTdjooZdooCSDf2L8-i4awdoUwiEb
Cross-origin resource sharing (CORS) is a World Wide Web Consortium (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 which the browser is currently accessing. CORS is a client-side security mechanism with well-documented security risks.
CDS Services and browser-based CDS Clients 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.
In the case that CDS Hooks cards are persisted, clients should take care to ensure that stale guidance does not negatively impact patient care.
CDS Services can update their previously returned guidance by returning a new set of cards
when the service is invoked based on a different hook
. CDS Services indicate this intent by providing multiple CDS Services with the same id
in discovery. Clients are recommended to remove cards
returned by a previous invocation with the new cards
.
STU NOTE: We are seeking implementer feedback on how best to balance the needs of performance for implementations with the critical patient safety issues raised by the potential for stale guidance.
Note that CDS Services will need to negotiate with CDS Clients to ensure that hooks that are required to ensure patient safety are supported by the CDS Client.
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. Extension structures SHOULD use a strategy for naming that ensures global uniqueness, such as reverse-domain-name notation, as in the examples below. The intention here is that anything that has broad ranging value across the community enough to be a standardized extension has broad ranging value enough to be a first class citizen rather than an extension in CDS Hooks.
STU Note: We seek implementer feedback on whether the recommendation to use namespace-based unique naming in the extension specification should be made mandatory or that we consider adding a mandatory field to extensions that indicates the source/type of the extension (as is done with FHIR).
For example, an extension on a request could look like this:
{
"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",
"com.cds-hooks.sandbox.myextension-practitionerspecialty": "gastroenterology"
}
}
As another example, an extension defined on the discovery response could look like this:
{
"services": [
{
"title": "Example CDS Service Discovery",
"hook": "patient-view",
"id": "patientview",
"prefetch": {
"patient": "Patient/"
},
"description": "clinical decision support for patient view",
"extension": {
"com.example.clientConformance": "http://hooks.example.org/fhir/102/Conformance/patientview"
}
}
]
}
CDS Hooks leverages json data types throughout. This section defines data structures re-used across the specification.
The Coding data type captures the concept of a code. A code is understood only when the given code, code-system, and a optionally a human readable display are available. This coding type is a standalone data type in CDS Hooks modeled after a trimmed down version of the FHIR Coding data type.
Field | Optionality | Type | Description |
---|---|---|---|
code |
REQUIRED | string | The code for what is being represented |
system |
REQUIRED | string | The codesystem for this code . |
display |
CONDITIONAL | string | A short, human-readable label to display. REQUIRED for Override Reasons provided by the CDS Service, OPTIONAL for Topic. |
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 for community feedback. Hooks are versioned, and mature according to the Hook Maturity Model.
Note that each hook (e.g. order-select
) represents something the user is doing in the CDS Client 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 order-select
).
Any user workflow or action within a CDS Client will naturally include contextual information such as the current user and patient. CDS Hooks refers to this information as context and allows each hook to define the information that is available in the context. Because CDS Hooks is intended to support usage within any CDS Client, this context can contain both required and optional data, depending on the capabilities of individual CDS Clients. However, the context information is intended to be relevant to most CDS Services subscribing to the hook.
For example, consider a simple patient-view
hook that is invoked whenever the user views a patient's information within the CDS Client. At this point in the workflow, the contextual information would include at least the current user and the patient that is being viewed. The hook declares this as context
, and passes it to the CDS Service as part of the request in the context
field:
"context":{
"userId" : "PractitionerRole/123",
"patientId" : "1288992"
}
Prefetch data, on the other hand, is defined by CDS Services as a way to allow the CDS Client to provide the data that a CDS Service needs as part of the initial request to the service. When the context data relates to a FHIR resource, it is important not to conflate context and prefetch. For instance, in the hook described above for opening a patient's chart, the hook context includes the id of the patient whose chart is being opened, not the full patient FHIR resource. In this case, the FHIR id of the patient is appropriate as the CDS Services may not be interested in details from the patient resource but instead other data related to this patient. Therefore, including the full patient resource in context would be unnecessary. Alternatively, a CDS Service may need the full patient resource in certain scenarios, in which case they can fetch it as needed from the FHIR server or request it to be prefetched using a prefetch template in their discovery response, such as:
"prefetch": {
"patientToGreet": "Patient/"
}
See the section on prefetch tokens for more information on how contextual information can be used to parameterize prefetch templates.
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 CDS Client (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 specified in the hook definition to guide developers on the information available at the point in the workflow when the hook is triggered. Prefetch data is defined by each CDS Service because it is specific to the information that service needs in order to process.
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 order-sign
(not sign-order
).
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 among implementers.
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:
REQUIRED
or OPTIONAL
Yes
or No
, indicating whether this field can be tokenized in a prefetch template.|
)The table below illustrates a sample hook context 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. |
For context fields that may contain multiple FHIR resources, the field SHOULD be defined as a FHIR Bundle, rather than as an array of FHIR resources. For example, multiple FHIR resources are necessary to describe all of the orders under review in the order-sign
hook's draftOrders
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, imagine a hook, order-select
that is invoked when a user selects a medication during an order workflow. The context data for this hook would contain draft FHIR resources representing the medications that have been selected for ordering. In this case, the CDS Client 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.
Hook creators SHOULD include examples of the context.
"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.
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.
Hook maturity | 3 - Considered
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 by the CDS Hooks Project Management Committee. |
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: <ul><ol>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.</ol><ol>Clearly differentiate the hook from similar hooks or other standards to help an implementer determine if the hook is correct for their scenario.</ol><ol>Explicitly document example scenarios when the hook should not be used.</ol></ul> |
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 |
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.
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.
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.
To enable tracking of changes to hook definitions, each hook MUST include a version indicator, expressed as a string.
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 CDS Client) cannot be changed. CDS Clients 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 | Major |
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.
Note that the intent of this table is to outline possible breaking changes. The authors have attempted to enumerate these types of changes exhaustively, but as new types of breaking changes are identified, this list will be updated.
As each hook progresses through a process of being defined, tested, implemented, used in production environments, and balloted, the hook's formal maturity level increases. Each hook has its own maturity level, which MUST be defined in the hook's definition and correspond to the Hook Maturity Model.
hookMaturity | 0 - Draft
Changes made to a hook's definition 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:
For example:
Version | Description |
---|---|
1.1 | Added new context variable |
1.0.1 | Clarified context variable usage |
1.0 | Initial Release |