NAV
hidden ruby

HealtheIntent API Workshop

Course Objective

These sessions will teach you how to consume Population Health concepts using the Cerner HealtheIntent APIs.

Click here to take you to the HealtheIntent slide deck.

Prerequisites

Introduction to HealtheIntent APIs

URI Structure

HealtheIntent is a multitenant platform and it follows a URI pattern.

https://{client}.{solution}.healtheintent.com

For this workshop we will be using the cernerdemo client. So the URI structure will look like:

https://cernerdemo.{solution}.healtheintent.com

To make calls to your production client, you would replace cernerdemo with your client mnemonic.

Authentication and Authorization

HealtheIntent‘s APIs support two different mechanisms for authentication.

For detailed documentation on how to authenticate against the HealtheIntent APIs, please see the documentation we have for Authentication.

Authentication using a Bearer Token

For this workshop bearer token authentication should be used. To create the Authorization header simply take the bearer token value, prefix it with the word Bearer, and place into the Authorization header.

Authorization: Bearer <bearer token value>

For use with ruby, this value can be stored in a variable.

auth_header = 'Bearer <bearer token Value>'

Exercise 1

Create an Authorization header using the given (available in the slide deck PDF) bearer token.

Note We will be disabling this account at the end of this workshop.

Make the following API call using curl to confirm the authorization header is correct:

curl -H 'Authorization: \<auth_header\>' https://cernerdemo.record.healtheintent.com/api/populations/1424e81d-8cea-4d6b-b140-d6630b684a58/people

A JSON response body should be returned.

Note The Authorization header created as part of this exercise will be used in the later exercises.

Authorization

The system account we will be using has already been created. It has permissions to access HealtheRecord, HealtheRegistries, HCC and Data Syndication APIs.

Note We will be disabling this account at the end of this workshop. To request a new system account to pursue further development against our platform, follow the instructions here.

Request Headers

The following headers must be set before you can call the HealtheIntent APIs:

HealtheRecord

The Cerner longitudinal record, HealtheRecord, is designed to provide an organized, coherent representation of the aggregated clinical data for a member. We will use HealtheRecord to demonstrate how to use the APIs.

Detailed API documentation for HealtheRecord can be found here.

We will be using the httparty gem as our HTTP client for the purpose of this workshop.

This API is used for searching among the people in a population. You can also refine your search using query parameters.

Person Search API: GET /api/populations/{population_id}/people

We will be using the following population ID for all our exercises: 1424e81d-8cea-4d6b-b140-d6630b684a58

Exercise 2

query_parameters = {
  'date_of_birth' => '1972-01-22',
  'q' => 'butler'
}

response = HTTParty.get('https://cernerdemo.record.healtheintent.com/api/populations/1424e81d-8cea-4d6b-b140-d6630b684a58/people', headers: {'Accept' => 'application/json', 'Authorization' => <auth_header>}, query: query_parameters)

puts JSON.pretty_generate(response)

We need to find the person ID for Tammy Butler, whose birth date is 1972-01-22. You will need to use the Authorization header created in the previous exercise.

The unformatted response object is hard to read, so let’s make it look pretty by using pretty_generate!

Click here for the answer Person ID for Tammy Butler is 6faf92f6-f3d3-4e0e-b633-48a45df383db

Person Demographics

We just found the person_id for Tammy Butler. Now let’s learn a little more about her using the Person Demographics API.

Person Demographics API: GET /api/populations/{population_id}/people/{person_id}

Exercise 3

response = HTTParty.get('https://cernerdemo.record.healtheintent.com/api/populations/1424e81d-8cea-4d6b-b140-d6630b684a58/people/6faf92f6-f3d3-4e0e-b633-48a45df383db', headers: {'Accept' => 'application/json', 'Authorization' => <auth_header>})

puts JSON.pretty_generate(response)

Find out Tammy Butler’s insurance plan names and how long has she been on each plan.

Click here for the answer Her first plan name is Healthe and she has been on the plan since 2015-09-06. Her second plan is HealtheYou and she has been on the plan since 2014-01-01.

Medications

Time to check out the medications currently prescribed to Tammy.

Medication Groups API: GET /api/populations/{population_id}/people/{person_id}/medication_groups

Exercise 4

response = HTTParty.get('https://cernerdemo.record.healtheintent.com/api/populations/1424e81d-8cea-4d6b-b140-d6630b684a58/people/6faf92f6-f3d3-4e0e-b633-48a45df383db/medication_groups', headers: {'Accept' => 'application/json', 'Authorization' => <auth_header>})

puts JSON.pretty_generate(response)

Find out all the medications that Tammy has been prescribed. How many medications has she been prescribed?

Click here for the answer 15

Allergies

Before we prescribe any new medications to Tammy, let’s check out what she is allergic to.

Allergen Groups API: GET /api/populations/{population_id}/people/{person_id}/allergen_groups

Exercise 5

response = HTTParty.get('https://cernerdemo.record.healtheintent.com/api/populations/1424e81d-8cea-4d6b-b140-d6630b684a58/people/6faf92f6-f3d3-4e0e-b633-48a45df383db/allergen_groups', headers: {'Accept' => 'application/json', 'Authorization' => <auth_header>})

puts JSON.pretty_generate(response)

Find out if Tammy is allergic to anything.

Click here for the answer Tammy is allergic to nuts and penicillin.

Conditions

Now it’s time to see all the conditions Tammy has. We can use the Condition Groups API to find them.

Condition Groups API: GET /api/populations/{population_id}/people/{person_id}/condition_groups

Exercise 6

response = HTTParty.get('https://cernerdemo.record.healtheintent.com/api/populations/1424e81d-8cea-4d6b-b140-d6630b684a58/people/6faf92f6-f3d3-4e0e-b633-48a45df383db/condition_groups', headers: {'Accept' => 'application/json', 'Authorization' => <auth_header>})

puts JSON.pretty_generate(response)

Take a look at all the conditions Tammy has. How many conditions does she have?

Click here for the answer She has 12 conditions.

Procedures

Let’s check out all the procedures we know about for Tammy. The Procedure Groups API consolidates procedures for easier viewing.

Procedure Groups API: GET /api/populations/{population_id}/people/{person_id}/procedure_groups

Exercise 7

response = HTTParty.get('https://cernerdemo.record.healtheintent.com/api/populations/1424e81d-8cea-4d6b-b140-d6630b684a58/people/6faf92f6-f3d3-4e0e-b633-48a45df383db/procedure_groups', headers: {'Accept' => 'application/json', 'Authorization' => <auth_header>})

puts JSON.pretty_generate(response)

Find out all the procedures Tammy has undergone.

Click here for the answer Tammy has undergone ‘Abdomen Ultrasound’, ‘Foot Exam’, and ‘Laparoscopy’.

Bonus Questions

Exercise 8

response = HTTParty.get('https://cernerdemo.record.healtheintent.com/api/populations/1424e81d-8cea-4d6b-b140-d6630b684a58/people/6faf92f6-f3d3-4e0e-b633-48a45df383db/vaccine_groups', headers: {'Accept' => 'application/json', 'Authorization' => <auth_header>})

response = HTTParty.get('https://cernerdemo.record.healtheintent.com/api/populations/1424e81d-8cea-4d6b-b140-d6630b684a58/people/6faf92f6-f3d3-4e0e-b633-48a45df383db/vaccine_groups/F8BD9284FBC0082BE043902F47AAB2CA/immunizations', headers: {'Accept' => 'application/json', 'Authorization' => <auth_header>})

puts JSON.pretty_generate(response)

Find out when Tammy was vaccinated against polio.

Hint You will need to call “/api/populations/{population_id}/people/{person_id}/vaccine_groups” first to figure out the “vaccine_group_id” for polio.

Click here for the answer The answer is ‘2009-01-05’ and ‘2010-04-12’.

Exercise 9

response = HTTParty.get('https://cernerdemo.record.healtheintent.com/api/populations/1424e81d-8cea-4d6b-b140-d6630b684a58/people/6faf92f6-f3d3-4e0e-b633-48a45df383db/provider_relationships', headers: {'Accept' => 'application/json', 'Authorization' => <auth_header>})

Find out the total number of healthcare providers for Tammy.

Hint You will have to use the provider_relationships API.

Click here for the answer The answer is 20.

HealtheRegistries

A registry is an aggregation of an organization’s patients who share particular clinical characteristics.

Detailed API documentation for HealtheRegistries can be found here

Person Registries Summaries

Person Registries summary lets you fetch a person’s registry data using the HealtheIntent person id.

Person Registries summary API: GET api/populations/{population_id}/people/{person_id}/registries

Exercise 10

require 'httparty'

# psssst... remember to create the authorization header

response = HTTParty.get('https://cernerdemo.registries.healtheintent.com/api/populations/1424e81d-8cea-4d6b-b140-d6630b684a58/people/6faf92f6-f3d3-4e0e-b633-48a45df383db/registries',headers: {'Accept' => 'application/json', 'Authorization' => <auth_header>})

puts JSON.pretty_generate(response)

MARA risk score is a metric that is used to gauge a patient’s cost of care to the hospital. Average risk score for a person is 1. The cost of care will be more expensive for a patient with a higher risk score.

Using Tammy Butler’s person_id from the previous exercise fetch her registry. What is the MARA score for Tammy?

Click here for the answer The MARA score is 0.15

Exercise 11

query_parameters = {fields: 'mara_total_risk_score'}

response = HTTParty.get('https://cernerdemo.registries.healtheintent.com/api/populations/1424e81d-8cea-4d6b-b140-d6630b684a58/people/6faf92f6-f3d3-4e0e-b633-48a45df383db/registries',headers: {'Accept' => 'application/json', 'Authorization' => <auth_header>}, query: query_parameters)

puts JSON.pretty_generate(response)

Now using the query parameters filter the response so that you only get the MARA score.

Hint Use the “fields” query parameter to filter the mara_total_risk_score field.

Exercise 12

query_parameters = { measures_status_filter: 'due' }

response = HTTParty.get('https://cernerdemo.registries.healtheintent.com/api/populations/1424e81d-8cea-4d6b-b140-d6630b684a58/people/6faf92f6-f3d3-4e0e-b633-48a45df383db/registries',headers: {'Accept' => 'application/json', 'Authorization' => <auth_header>}, query: query_parameters)

puts JSON.pretty_generate(response)

As Tammy’s primary care provider you want to know all the measures that are due.

Fetch her registry and find out all the measures that are due under copd.

Hint Use the “measures_status_filter” query parameter to filter only those measures that are due.

Click here for the answer These are the measures that are due are “Influenza vaccination current season”, “spirometry evaluation” and “tobacco use screening and cessation”.

Person Measure Component Group

Supporting facts for a measure can be fetched using the component group API.

Person Measure Component Group API: GET api/populations/{population_id}/people/{person_id}/registries/{program_id}/measures/{fqn}/componentGroup

fqn is the fully qualified name of the measure (its identifier); an example of this is cernerstandard.adultwellness.org2014.clinical/body-mass-index. Also, program_id is the HealtheIntent program id to which the corresponding measure belongs to; an example of this is cernerdemo.adultwellness.org.clinical.cernerdemo-adult-wellness.

Exercise 13

response = HTTParty.get('https://cernerdemo.registries.healtheintent.com/api/populations/1424e81d-8cea-4d6b-b140-d6630b684a58/people/6faf92f6-f3d3-4e0e-b633-48a45df383db/registries/cernerdemo.diabetes.org.clinical.cernerdemo-diabetes/measures/cernerstandard.diabetesmellitus.org2014.clinical%2Fbp-less-than-140-80/componentGroup' ,headers: {'Accept' => 'application/json', 'Authorization' => <auth_header>})

puts JSON.pretty_generate(response)

Let’s take a look at what Tammy’s last measured blood pressure is. Fetch supporting facts for cernerstandard.diabetesmellitus.org2014.clinical/bp-less-than-140-80 measure from cernerdemo.diabetes.org.clinical.cernerdemo-diabetes program.

Hint The “/” in the measure needs to be changed to “%2F”.

Click here for the answer Blood Pressure 112/60 mmHg.

Bonus questions

Exercise 14

response = HTTParty.get('https://cernerdemo.registries.healtheintent.com/api/populations/1424e81d-8cea-4d6b-b140-d6630b684a58/programs', headers: {'Accept' => 'application/json', 'Authorization' => <auth_header>})

puts JSON.pretty_generate(response)

Retrieve all the programs for the population.

Hint Need to use the Programs API: “GET api/populations/{population_id}/programs”.

Exercise 15

response = HTTParty.get('https://cernerdemo.registries.healtheintent.com/api/populations/1424e81d-8cea-4d6b-b140-d6630b684a58/programs/cernerdemo.asthma.org.clinical.cernerdemo-asthma-care', headers: {'Accept' => 'application/json', 'Authorization' => <auth_header>})

puts JSON.pretty_generate(response)

How many measures does the cernerdemo.asthma.org.clinical.cernerdemo-asthma-care program have?

Hint Need to use the Programs API: “GET /populations/{population_id}/programs/{program_id}” to retrieve a single program.

Click here for the answer cernerdemo.asthma.org.clinical.cernerdemo-asthma-care program has 5 measures.

Exercise 16

request_body = { person_ids: ['ae3a2060-1261-43a3-a42b-013b0d700fca', 'a6facde2-b5b3-41e0-b939-3d9c87b258c9', '6faf92f6-f3d3-4e0e-b633-48a45df383db'],
  program_ids: [ 'cernerdemo.adultwellness.org.clinical.cernerdemo-adult-wellness']
}

query_parameters = { measures_status_filter: 'due' }

response = HTTParty.post('https://cernerdemo.registries.healtheintent.com/api/populations/1424e81d-8cea-4d6b-b140-d6630b684a58/people/registries', headers: {'Authorization' => <auth_header>}, body: request_body, query: query_parameters)

puts JSON.pretty_generate(response)

Stephanie (ae3a2060-1261-43a3-a42b-013b0d700fca), Jason (a6facde2-b5b3-41e0-b939-3d9c87b258c9) and Tammy (6faf92f6-f3d3-4e0e-b633-48a45df383db) are coming in today for a checkup. Fetch their registries and find out what measures are due for their Cerner adult wellness program. The program id for “Cerner adult wellness” is cernerdemo.adultwellness.org.clinical.cernerdemo-adult-wellness.

Hint Need to use the Multiple People registries API: “POST /populations/{population_id}/people/registries” to retrieve multiple registries. You will also have to use the “measures_status_filter” query parameter.

Click here for the answer The measures due for Stephanie, Jason, and Tammy are 11, 9, and 6 respectively.

Hierarchical Condition Categories (HCC)

HCC

Hierarchical Condition Categories (HCCs) are used by Medicare Advantage (MA) plans for reimbursement. The HCC API let’s you fetch HCC defined conditions that are persistent from last year and also conditions that are suspected for a person using the HealtheIntent person id.

Detailed API documentation for HCC can be found here

Exercise 17

response = HTTParty.get('https://cernerdemo.programs.healtheintent.com/api/populations/1424e81d-8cea-4d6b-b140-d6630b684a58/people/6faf92f6-f3d3-4e0e-b633-48a45df383db/hcc_diagnoses', headers: {'Authorization' => <auth_header>})

puts JSON.pretty_generate(response)

The HCC Code attribute is composed of the code number and description.

Tammy Butler got her checkup done. Lets check the code which has a diagnosis code value of J44.9 for Tammy Butler, using her person id from the last exercise (6faf92f6-f3d3-4e0e-b633-48a45df383db).

Hint Need to use the HCC API: “GET /populations/{population_id}/people/{person_id}/hcc_diagnoses” to retrieve HCC diagnoses data of a person.

Click here for the answer HCC111 Chronic Obstructive Pulmonary Disease

Exercise 18

HCC Data can be of two types. Two types are suspected and persistent. The conditions that are suspected will include a stratification field that categorizes the level of suspicion. The possible values of stratification are NOT SUSPECTED, MODERATELY SUSPECTED and HIGHLY SUSPECTED.

query_parameters = { type: 'suspected' }

response = HTTParty.get('https://cernerdemo.programs.healtheintent.com/api/populations/1424e81d-8cea-4d6b-b140-d6630b684a58/data_partitions/8dee150d-505f-4635-b009-1bef63d7cf5a/people/3584536/hcc_diagnoses', headers: {'Authorization' => <auth_header>}, query: query_parameters)

puts JSON.pretty_generate(response)

Use Tammy’s data_partition_person_id 3584536 and data_partition_id 8dee150d-505f-4635-b009-1bef63d7cf5a to find the stratification value of the code HCC96 Specified Heart Arrhythmias?

Hint Need to use the HCC API: “GET /populations/{population_id}/data_partitions/{data_partion_id}/people/{data_partition_person_id}/hcc_diagnoses?type=suspected” to retrieve HCC diagnoses suspected data of a person.

Click here for the answer Highly Suspected

Risk Adjustment Factor (RAF)

Fetches the RAF score data of a person based on population ID and HealtheIntent person ID.

Exercise 19

response = HTTParty.get('https://cernerdemo.programs.healtheintent.com/api/populations/1424e81d-8cea-4d6b-b140-d6630b684a58/people/6faf92f6-f3d3-4e0e-b633-48a45df383db/raf_scores', headers: {'Authorization' => <auth_header>})

puts JSON.pretty_generate(response)

For Tammy Butler with person ID (faf92f6-f3d3-4e0e-b633-48a45df383db), how many types of RAF scores are present?

Hint Need to use the RAF_SCORE API: “GET /populations/{population_id}/people/{person_id}/raf_scores” to retrieve Raf scores of a person.

Click here for the answer 3

Exercise 20

response = HTTParty.get('https://cernerdemo.programs.healtheintent.com/api/populations/1424e81d-8cea-4d6b-b140-d6630b684a58/people/6faf92f6-f3d3-4e0e-b633-48a45df383db/raf_scores', headers: {'Authorization' => <auth_header>})

puts JSON.pretty_generate(response)

For the person ID faf92f6-f3d3-4e0e-b633-48a45df383db, what is potential RAF score value?

Click here for the answer 1.558

Data Syndication

Data syndication facilitates the bulk delivery of HealtheIntent data. It provides direct, low-level, asynchronous access to the information that HealtheIntent solutions create, curate, and operate against. This API is used mainly to populate a data warehouse or other third-party data store for research, reporting, or analytical activities.

Detailed API documentation for Data Syndication can be found here

Listing extracts

The extract history API lists files that are available for download. Optional query parameters allow pagination and date range filtering.

Extraction History API: GET api/extracts

The full API documentation can be found here

Exercise 21

require 'httparty'

# Remember to create the authorization header

response = HTTParty.get('https://cernerdemo.datasyndication.healtheintent.com/api/extracts', headers: {'Authorization' => auth_header, 'Accept' => 'application/json'})

puts JSON.pretty_generate(response)

Retrieve the list of available extracts. You will need to use the Authorization header created in a previous exercise.

Notice that extracts for two populations are available. Data Syndication is configured per population, and all populations that have Data Syndication enabled for the client will show in this listing.

When was the latest extract for population 1424e81d-8cea-4d6b-b140-d6630b684a58 released?

Click here for the answer The answer is 2017-10-05T08:32:47Z. (as of CHC2017)

Exercise 22

require 'httparty'

# Dates must be in either YYYY-MM-DD or YYYY-MM-DDThh:mm:ssZ format
query_parameters = {start_date:"2017-09-20",end_date:"2017-10-01"}

response = HTTParty.get('https://cernerdemo.datasyndication.healtheintent.com/api/extracts', headers: {'Authorization' => auth_header, 'Accept' => 'application/json'}, query: query_parameters)

puts JSON.pretty_generate(response)

Use the start_date and end_date query parameters to get only the extracts between September 20 and October 1 of this year.

How many results are returned?

Click here for the answer The answer is 4.

Exercise 23

limit = 3
start = 0
while true do
  query_parameters = {start: start, limit: limit}
  headers = {'Authorization' => auth_header, 'Accept' => 'application/json'}
  response = HTTParty.get('https://cernerdemo.datasyndication.healtheintent.com/api/extracts', headers: headers, query: query_parameters)
  extracts = JSON.parse(response.body)
  numResults = extracts["extracts"].size
  if numResults > 0 then
    puts 'Results ' + start.to_s + ' - ' + (start+numResults-1).to_s + ':'
    puts JSON.pretty_generate(extracts)
  end
  if numResults != limit then
    break
  end
  start = start + limit
end

By default, the returned list is limited to the most recent 20 extracts. The start and limit query parameters can be used to help page through large lists.

Write a script that uses these query parameters to get the list of all extracts, using a limit of 3.

How many API calls are needed to return the full list?

Click here for the answer It can be done with 3 API calls. (as of CHC2017)

Downloading extracts

This API allows you to download a specific data file or portions of a file.

Download API: GET api/extracts/{id}/download

The full API documentation can be found here

Exercise 24

require 'httparty'

#Note: If trying this in irb, add "; nil" to the end of the following line to avoid printing the full contents of the response
response = HTTParty.get('https://cernerdemo.datasyndication.healtheintent.com/api/extracts/9590/download', headers: { 'Authorization' => auth_header, 'Accept' => 'application/json'})

puts 'Status: ' + response.headers["status"]
puts 'Response Body Size: ' + response.body.size

file = File.open('extract9590.tar.gz','wb')
file.write(response.body)
file.close

Download the extract for population 1424e81d-8cea-4d6b-b140-d6630b684a58 that has the smallest file size and store it to the file system.

Note: If using irb, you can add “; nil” to the end of your API request method call to prevent the full contents of the extracts from being printed to the console.

What is the id of the extract you downloaded?

Click here for the answer The answer is 9590. (as of CHC2017)

What is the size of the file you just downloaded?

Click here for the answer The answer is 172767458 bytes. (as of CHC2017)

Exercise 25

require 'httparty'

response = HTTParty.get('https://cernerdemo.datasyndication.healtheintent.com/api/extracts/9569/download', headers: { 'Authorization' => auth_header, 'Accept' => 'application/json', 'Range' => 'bytes=0-99'})

puts 'Status: ' + response.headers["status"]
puts 'Content Range: ' + response.headers["content-range"]
puts 'Response Body Size: ' + response.body.size

file = File.open('extract9569.tar.gz','wb')
file.write(response.body)
file.close

HTTP byte ranges can be set in the request header to download extracts in smaller segments. This can be useful to handle extracts with large file sizes. See the API docs for full details.

Use the Range header to download the first 100 bytes of the same file from the exercise above, and save to a file.

What response code was returned?

Click here for the answer The answer is 206 Partial Content.

Verify the file size you wrote is 100 bytes.

Exercise 26 (Bonus)

Write a script that downloads the entire extract in 1MB segments, stores each file separately, then reassembles them into a complete extract once all parts are downloaded.

Verify the file you build this way matches the full file you downloaded in the earlier exercise.