2.3.0 CDC Opioid Prescribing Guidelines - Implementation Documentation

This documentation is provided as a resource for developers interested in implementing the CDC opioid prescribing guidelines. It is assumed that the developer will have a basic understanding of CQL, FHIR, a FHIR API (like HAPI), and the CDS Hooks API.

  • CQL Evaluation Engine - the developer will need access to a service that enables the translation and execution of CQL libraries.
  • FHIR Server - it is preferred for the developer to have access to edit resource providers and operations within the server. Although not necessary, the server should have the PlanDefinition $apply operation implemented, especially if a "pure" FHIR implementation is desired. The examples in this guide use the HAPI FHIR JPA Server, but the logic should translate to other FHIR-conformant APIs like the .NET API.
  • CDS Hooks API - the developer should be familiar with the CDS Hooks API and services. The discovery and medication-prescribe hook are the aspects of the API that are used in this context.
  • JSON Parser - CDS Hooks requests and responses are issued in JSON. A basic understanding of JSON format and a good JSON parsing library like json-simple or gson will make life easier.

For those without access to a FHIR server, please refer to the links below to learn how to set one up:

If spinning up a FHIR server is beyond the scope of your implementation, there are several publicly available FHIR servers. However, be forewarned that these servers may or may not have the operations implemented that will be discussed in this guide.

The following flowchart provides a summary of the operations required to implement the Opioid Guidelines:

As depicted in the flowchart, the Opioid Guidelines are triggered by a CDS Hooks request issued by the EHR. More precisely, the request would be triggered by a user within the EHR authoring a new prescription for a medication containing opioid ingredients. In a production environment, this request would be in real-time, which carries performance requirements that will be discussed later in this documentation.

The following is an example CDS Hooks medication-prescribe request:

{
  "hookInstance": "d1577c69-dfbe-44ad-ba6d-3e05e953b2ea",
  "fhirServer": "http://fhirtest.uhn.ca/baseDstu2",
  "hook": "medication-prescribe",
  "user": "Practitioner/example",
  "context": [
    {
      "resourceType": "MedicationOrder",
      "id": "medrx001",
      "dateWritten": "2017-05-05",
      "status": "draft",
      "patient": {
        "reference": "Patient/Patient-12214"
      },
      "medicationCodeableConcept": {
        "coding": [
          {
            "system": "http://www.nlm.nih.gov/research/umls/rxnorm",
            "code": "197696"
          }
        ]
      },
      "dosageInstruction": [
        {
          "text": "Take 40mg three times daily",
          "timing": {
            "repeat": {
              "frequency": 3,
              "frequencyMax": 3,
              "period": 1,
              "unit": "d"
            }
          },
          "asNeededBoolean": false,
          "doseQuantity": {
            "value": 40,
            "unit": "mg",
            "system": "http://unitsofmeasure.org",
            "code": "mg"
          }
        }
      ],
      "dispenseRequest": {
        "quantity": {
          "value": 3000,
          "unit": "mg",
          "system": "http://unitsofmeasure.org",
          "code": "mg"
        }
      }
    }
  ],
  "patient": "Patient/Patient-12214",
  "prefetch": {
    "medication": {
      "response": {
        "status": "200 OK"
      },
      "resource": {
        "resourceType": "MedicationOrder",
        "id": "medrx002",
        "dateWritten": "2017-04-25",
        "status": "active",
        "patient": {
          "reference": "Patient/Patient-12214"
        },
        "medicationCodeableConcept": {
          "coding": [
            {
              "system": "http://www.nlm.nih.gov/research/umls/rxnorm",
              "code": "199789"
            }
          ]
        },
        "dosageInstruction": [
          {
            "text": "Take 50mg twice daily",
            "timing": {
              "repeat": {
                "frequency": 2,
                "period": 1,
                "periodUnits": "d"
              }
            },
            "asNeededBoolean": false,
            "doseQuantity": {
              "value": 55,
              "unit": "mg",
              "system": "http://unitsofmeasure.org",
              "code": "mg"
            }
          }
        ],
        "dispenseRequest": {
          "quantity": {
            "value": 3000,
            "unit": "mg",
            "system": "http://unitsofmeasure.org",
            "code": "mg"
          }
        }
      }
    }
  }
}
  

First, the request needs to be parsed into a JSON object. Second, the request needs to be validated. During the validation step, the following should be checked:

  • The specified hook corresponds to the service called. For example, an error would be given if the EHR called the patient-view hook on a medication-prescribe service.
  • The request MUST provide context resource(s). Additionally, the resources must be relevant to the service. For example, a request to a medication-prescribe service with anything other than a MedicationOrder/MedicationRequest FHIR resource should result in an error.
  • The patient, user, and fhirServer should be specified. Although, if the request contains prefetch resources, then this may be absent from the request.
  • If the request does not contain prefetch resources, then the patient, user, and fhirServer MUST be given. Refer to the "Prefetch" section for more information.
  • To keep this guide simple, the oauth, redirect, and encounter request attributes will be ignored. Refer to the CDS Hooks documentation for more information on these attributes.

NOTE: The CDS services should be prepared to handle different versions of FHIR resources (DSTU, DSTU2, and STU3).

The prefetch attribute of a CDS Hooks request is provided to boost the performance of the requested CDS service. The prefetch is defined in the CDS Hooks Discovery request and populated by the EHR before the actual request to the service. If the request does not contain prefetch resources and requires them for more comprehensive processing, then the CDS service will be required to retrieve them. This is achieved by constructing a request to the fhirServer for the resources using the patient, user, and service called as context. For example, if the EHR calls a medication-prescribe service without prefetch resources, the service MUST make the following call to the fhirServer specified in the request (DSTU2 format):

{ServerBase}/MedicationOrder?patient={$patient}&status=active

It is highly recommended that the EHR request contain the prefetch resources, especially if there is a performance requirement.

This implementation uses the PlanDefinition $apply operation to fulfill the requirements of the medication-prescribe service. However, the PlanDefinition resource was introduced in the STU3 release. Therefore, any resources provided by the request or retrieved by the service must be converted into STU3 resources. It is common for FHIR APIs to provide a mechanism to convert resources from one version to another. However, if the API you are using does not provide resource version conversion, then the service will need to handle the conversion.

Regarding the Opioid Guidelines medication-prescribe service, the conversion from DSTU2 to STU3 is rather simple. The medicationCodeableConcept, status, and dosageInstruction are the only attributes that are accessed in this service. Therefore, they are the only attributes that need to be converted. However, depth of conversion is implementation-specific. For example, if your implementation returns a suggestion, which is a modified resource that conforms to certain requirements, then you would want to convert all the attributes to prevent data loss. If your service is returning info cards on the other hand, then attributes that aren't used by the service can be excluded.

For example, take the following MedicationOrder:

{
  "resourceType": "MedicationOrder",
  "id": "medrx001",
  "identifier": [
    {
      "use": "official",
      "system": "http://www.bmc.nl/portal/prescriptions",
      "value": "12345"
    }
  ],
  "dateWritten": "2015-01-15",
  "status": "active",
  "patient": {
    "reference": "Patient/f001",
    "display": "Eve Everywoman"
  },
  "prescriber": {
    "reference": "Practitioner/f007",
    "display": "Patrick Pump"
  },
  "encounter": {
    "reference": "Encounter/f002",
    "display": "encounter who leads to this priscription"
  },
  "reasonCodeableConcept": {
    "coding": [
      {
        "system": "http://snomed.info/sct",
        "code": "65363002",
        "display": "Otitis Media"
      }
    ]
  },
  "medicationCodeableConcept": {
    "coding": [
      {
        "code": "197696",
        "system": "http://www.nlm.nih.gov/research/umls/rxnorm"
      }
    ]
  },
  "dosageInstruction": [
    {
      "text": "Take 5ml three times daily",
      "additionalInstructions": {
        "coding": [
          {
            "system": "http://snomed.info/sct",
            "code": "311504000",
            "display": "With or after food"
          }
        ]
      },
      "timing": {
        "repeat": {
          "frequency": 3,
          "period": 1,
          "periodUnits": "d"
        }
      },
      "asNeededBoolean": false,
      "siteCodeableConcept": {
        "coding": [
          {
            "system": "http://snomed.info/sct",
            "code": "181220002",
            "display": "Entire oral cavity"
          }
        ]
      },
      "route": {
        "coding": [
          {
            "system": "http://snomed.info/sct",
            "code": "26643006",
            "display": "Oral Route"
          }
        ]
      },
      "doseQuantity": {
        "value": 5,
        "unit": "mL",
        "system": "http://unitsofmeasure.org",
        "code": "mL"
      }
    }
  ],
  "dispenseRequest": {
    "medicationReference": {
      "reference": "Medication/MedicationExample3"
    },
    "validityPeriod": {
      "start": "2015-01-15",
      "end": "2016-01-15"
    },
    "numberOfRepeatsAllowed": 2,
    "quantity": {
      "value": 100,
      "unit": "mL",
      "system": "http://unitsofmeasure.org",
      "code": "mL"
    },
    "expectedSupplyDuration": {
      "value": 10,
      "unit": "days",
      "system": "http://unitsofmeasure.org",
      "code": "d"
    }
  },
  "substitution": {
    "type": {
      "coding": [
        {
          "system": "http://hl7.org/fhir",
          "code": "G",
          "display": "Generic Composition"
        }
      ]
    },
    "reason": {
      "coding": [
        {
          "system": "http://hl7.org/fhir",
          "code": "FP",
          "display": "formulary policy"
        }
      ]
    }
  }
}
  

If your implementation does not return a suggestion, then the resulting MedicationRequest could be simplified to the following:

{
  "resourceType": "MedicationRequest",
  "id": "medrx001",
  "status": "active",
  "medicationCodeableConcept": {
    "coding": [
      {
        "code": "197696",
        "system": "http://www.nlm.nih.gov/research/umls/rxnorm"
      }
    ]
  },
  "dosageInstruction": [
    {
      "timing": {
        "repeat": {
          "frequency": 3,
          "period": 1,
          "periodUnit": "d"
        }
      },
      "asNeededBoolean": false,
      "doseQuantity": {
        "value": 5,
        "unit": "mL",
        "system": "http://unitsofmeasure.org",
        "code": "mL"
      }
    }
  ]
}
  

Now that the CDS request has been parsed and the resources have been converted to STU3, it is time to apply the Opioid Guidance PlanDefinition. The first step is to prepare the $apply operation parameters.

Most of the standard $apply parameters will not be used. In fact the only standard parameter that will be used for the opioid guidelines is the required patient ID parameter. In addition, the PlanDefinition that this implementation applies needs access to the context and prefetch resources from the CDS Hooks request. Therefore, this particular instance of the $apply operation will need to add an additional "context" parameter. The additional context parameter will be represented as a parameter of parameters. See an example here

The $apply operation, in general, applies a PlanDefinition to a specific context. In the context of the CDC Opioid Guidelines, the application of the PlanDefinition is depicted in the following diagram:

Here is an example

As shown in the flowchart, the $apply operation begins by parsing the input parameters. Then the CDC Opioid Guidelines library is serialized into a library object. The next step is to resolve data providers for FHIR STU3 and OMTK. OMTK, or Opioid Management Terminology Knowledge, is a library that provides MME conversion factors and terminology information for the opioid medications. The OMTK data provider endpoint is a SQLite database and is NOT a FHIR data provider. The next step is to walk through the specified PlanDefinition actions, checking for applicability and an expression reference in the condition. If the PlanDefinition condition is applicable and has an expression reference, set the library "Orders" parameter to the list of MedictionRequest resources (context and prefetch) and evaluate the expression. If the expression evaluates to true, the context prescription violates the opioid guidelines by exceeding the suggested MME and a warning should be returned to the author. If the expression evaluates to false, then the presription is in accordance with the opioid guidelines. No matter the outcome of the expression, a CarePlan resource must be constructed and returned, which is left to the developer to format (see the example link above for an example).

The PlanDefinition $apply operation returns a CarePlan. However, this implementation uses CDS Hooks to handle requests to and responses from the service. Therefore, the service must transform the returned CarePlan into a CDS Hooks response. There are a couple different ways to respond using the CDS Hooks API. The easiest responses are information cards, which can indicate success, a warning, information, or a hard-stop (cease execution). Although, info cards are easy and useful for testing, they are not quite as useful for the EHR user. A better response is called a "suggestion". A suggestion is similar to an info card, but a modified resource is included in the response. The modified resource is the context resource where the attributes have been changed to conform to the specified guidelines. A suggestion is a much more useful response for the user in the EHR as they aren't required to create a new medication order and start this process anew.

As an example, consider the following CarePlan is returned from the PlanDefinition $apply operation:

{
  "resourceType": "CarePlan",
  "status": "active",
  "intent": "order",
  "title": "High risk for opioid overdose - taper now",
  "description": "Total morphine milligram equivalent (MME) is 20200.700mg/d. Taper to less than 50.",
  "activity": [
    {
      "detail": {
        "status": "in-progress",
        "statusReason": "warning"
      }
    }
  ],
  "note": [
    {
      "id": "CDC guideline for prescribing opioids for chronic pain",
      "text": "https://guidelines.gov/summaries/summary/50153/cdc-guideline-for-prescribing-opioids-for-chronic-pain---united-states-2016#420"
    },
    {
      "id": "MME Conversion Tables",
      "text": "https://www.cdc.gov/drugoverdose/pdf/calculating_total_daily_dose-a.pdf"
    }
  ]
}
  

Here is an example of what the CDS Response might look like as an info card:

{
  "summary": "High risk for opioid overdose - taper now",
  "indicator": "warning",
  "links": [
    {
      "label": "CDC guideline for prescribing opioids for chronic pain",
      "type": "absolute",
      "url": "https://guidelines.gov/summaries/summary/50153/cdc-guideline-for-prescribing-opioids-for-chronic-pain---united-states-2016#420"
    },
    {
      "label": "MME Conversion Tables",
      "type": "absolute",
      "url": "https://www.cdc.gov/drugoverdose/pdf/calculating_total_daily_dose-a.pdf"
    }
  ],
  "detail": "Total morphine milligram equivalent (MME) is 20200.700mg/d. Taper to less than 50."
}