Profiling a FHIR Careplan for Immunizations: part 2

I’ve been trying to figure out a good way of showing a profiled resource. In the previous post, we had a paragraph on each element we wanted to change, but that isn’t the easiest to understand. I do know that the core team is looking at ways of doing this – and I think it’s going to be a focus of the next connectathon, but in the mean time here’s an option to consider – a simple table.

It’s not complete – for example it’s doesn’t (yet) show ValueSet bindings or types of Resource in Resource references, but it’s a start…

The key to the table:

  • Path is the path to the element or extension
  • Source is whether it comes from the core resource unaltered (core), has been altered by the profile (prof) or is an extension (ext).
  • Multiplicity, datatype and description are obvious.

Still not completely happy with the display as yet (and it only just fits in the space on this blog) – will continue working on it. The actual profile can be found here, (it’s the Blaze  server that Richard (one of my Orion Health colleagues) wrote – the validation is truly brutal – if Blaze accepts it, then I know it’s good!). Again a work in progress, and I’ll update as we go.

Oh – by the way – if you do view the profile, be aware that at the moment the SSL certificate is self signed, and some clients aren’t happy with that. We are getting a certificate from a recognized CA, but the wheels grind slowly…

Path Source Mult DataType Description
CarePlan core 1..1 Resource Healthcare plan for patient
-> profileDescription ext 0..1 string Describes the Protocol
-> profileUri ext 0..1 string The URI that points to the description of the profile
CarePlan.identifier core 0..* Identifier External Ids for this plan
CarePlan.patient prof 1..1 Resource Reference Who care plan is for
-> dob ext 0..1 date Date of Birth
CarePlan.status core 1..1 code planned | active | completed
CarePlan.period core 0..1 Period Time period plan covers
CarePlan.modified prof 1..1 dateTime When last updated
CarePlan.concern prof 0..0 Resource Reference Health issues this plan addresses
CarePlan.participant core 0..1 Who’s involved in plan?
CarePlan.participant.role core 0..1 Codeable Concept Type of involvement
CarePlan.participant.member core 1..1 Resource Reference Who is involved
CarePlan.goal core 0..1 Desired outcome of plan
-> disease ext 0..1 string Allows a goal to specify the disease it is protecting against
CarePlan.goal.description core 1..1 string What’s the desired outcome?
CarePlan.goal.status prof 0..0 code in progress | achieved | sustaining | cancelled
CarePlan.goal.notes core 0..1 string Comments about the goal
CarePlan.goal.concern prof 0..0 Resource Reference Health issues this goal addresses
CarePlan.activity core 0..1 Action to occur as part of plan
-> immunization ext 0..1 Resource Reference If a completed activity, then references the immunization record
CarePlan.activity.goal core 0..* idref Goals this activity relates to
CarePlan.activity.status core 0..1 code not started | scheduled | in progress | on hold | completed | cancelled
CarePlan.activity.prohibited core 1..1 boolean Do NOT do
CarePlan.activity.actionResulting prof 0..0 Resource Reference Appointments, orders, etc.
CarePlan.activity.notes core 0..1 string Comments about the activity
CarePlan.activity.detail prof 0..0 Resource Reference Activity details defined in specific resource
CarePlan.activity.simple core 0..1 Activity details summarised here
CarePlan.activity.simple.category core 1..1 code diet | drug | encounter | observation | procedure | supply | other
CarePlan.activity.simple.code prof 1..1 Codeable Concept Detail type of activity
-> numberOfDoses ext 0..1 integer Total number of doses for this vaccine
-> doseSequence ext 0..1 integer Sequence number of this vaccine in the series
CarePlan.activity.simple.timing[x] core 0..1 Schedule When activity is to occur
CarePlan.activity.simple.location prof 0..0 Resource Reference Where it should happen
CarePlan.activity.simple.performer core 0..* Resource Reference Who’s responsible?
CarePlan.activity.simple.product prof 0..0 Resource Reference What’s administered/supplied
CarePlan.activity.simple.dailyAmount prof 0..0 Quantity How much consumed/day?
CarePlan.activity.simple.quantity prof 0..0 Quantity How much is administered/supplied/consumed
CarePlan.activity.simple.details core 0..1 string Extra info on activity occurrence
CarePlan.notes core 0..1 string Comments about the plan

Profiling a FHIR Careplan for Immunizations

After the previous discussion on how to represent an Immunization plan, I had intended to talk about how to represent the Immunization protocol (or schedule), and how to apply that to a patients’ plan, but (as so often happens) I got a bit side-tracked, and thought we could first think about how we might create a Profile against the CarePlan resource to represent an immunization plan.

Now, we’ve talked about FHIR profiles before so I’m not going to repeat that here, other than to say that it is the profile that takes a standard resource (or resources) and makes them fit more precisely to a particular Use Case of some sort, so profiling CarePlan to support an Immunization plan seems a perfect fit!

There’s actually quite a lot to do to get a good profile (and I’m sure I haven’t got it completely correct), but at a high level, once you have your use cases you go through the resource and:

  • Alter multiplicity of elements (e.g. to make an optional element required or not used. Remember you can’t make a required element optional though)
  • Specify a particular terminology or codeset for a binding (and often you’ll define a ValueSet to hold the list of allowable codes)
  • Add a new element (as an extension) where it’s needed by the Use Case but not defined in the base resource

(There’s other things you can do as well, but that’s enough for our current needs).

Overall design

Here’s the structure of the Care Plan resource (Sorry about the image quality – its a screen scraping from my computer – here’s the original)

Screen Shot 2014-05-27 at 1.48.31 pm

How we’ll structure our profile overall is to use the goal element to list the diseases that this plan is intended to protect against (using an extension), then optionally link the activities back to the goals/diseases that are covered by that vaccine. That way we can say which activities contribute to protection against which disease. We’ll make this all optional, as not everyone will want to go to this level of detail (and, in truth, is probably a bit overkill). (btw – Another way of doing the same thing would be to add extensions to the activity directly to indicate the diseases being covered)

The activity will generally be the administration of a vaccine, however we’ll keep it a bit flexible to allow for, say, educational sessions as part of the plan. We’ll know which activity is an vaccination because the activity.simple.category will have the value ‘drug’.

We only need to profile the CarePlan resource for this Use Case.

For simplicity, we’ll put all the extensions in the same profile – in real life we might try to re-use existing extensions or to make some of ours a bit more general. This will really need common registries of profiles, which the FHIR team are working on at the moment…


So the following section lists the changes (in a textual format) that we’ve going to make to the CarePlan resource to allow it to hold the information required to represent an immunization plan. Each entry has the path to the element, followed by the multiplicity (which may not be the same as that in the base resource). If the entry is an extension (and remember that extensions can be at any level in a resource) then the path will end in a #{extensionDef code} and the data type will be displayed. Thus:

Careplan.goal#disease (0..1) CodeableConcept

means that this extension is against the goal element of the CarePlan resource, that it’s optional and there can only be one, and that the datatype is CodeableConcept.

If the values of the element (extension or base element) come from a ValueSet, then we’ll place the name of the ValueSet in square brackets at the end. Eg

Careplan.Activity.simple.code (1..1) [immunizationVaccines]

means that the value of the code (which is required in this profile) comes from the valueset whose name is immunizationVaccines. (Actually, this should really be a complete URI, but we’ll keep it simple to avoid cluttering the page). Note that this layout is just the one we’re using for this post – the formal representation will be the profile resource and maybe other artifacts that the core team is working on.  (Actually, I think the next connectathon is going to focus on profiles and conformance)

So, here we go. To minimize space, we’ll only include entries that are different to the base resource, or where there’s a particular comment to make.

We’ll also do this in 2 posts. This one will be more of a ‘design’ session – what we’re going to do and why. The next post will build the actual profile.

Items in the Profile

Careplan#protocolDescription (0..1) string

An extension where we can describe the protocol that this plan was drawn from.

Careplan#protocolUri (0..1) uri

A direct link to the protocol (if known).

CarePlan.patient (1..1)

You have to have a patient in our plan…

Careplan.patient#dob (0..1) date

The patients Date of Birth. This is a bit naughty as you should really retrieve the Patient resource and look up the Date of Birth from there. The reason to include it here as an extension is to allow us to be able to determine if an immunization is overdue just on the information in the plan. In reality, you probably woudn’t do this but, hey, its my blog and I’ll de-normalise if I want to

Careplan.period (0..1)

This would be the age range over which the immunization plan was active – eg from birth to 18 years

Careplan.modified (1..1)

This should be updated when the plan itself is changed – ie a new activity is added, to the status of an existing activity changed. The multiplicity is changed to make it required.

Careplan.concern (0..0)

As this plan is specifically about immunizations, the concern is not needed – i.e. the whole point is to avoid the patient getting those conditions. Change the multiplicity to 0..0 to remove it.

Careplan.goal#disease (0..1) codeableconcept

We’ll use the goal to list the diseases that this plan is intended to protect against, then optionally link the activities back to the goal. That way we can say which activities (immunizations) are protecting against which diseases. Note that there’s only a single disease per goal, but multiple goals per plan.

Careplan.goal.status (0..0)

The overall status of the plan is at the careplan level, not the goal.

Careplan.goal.concern (0..0)

Same notes as for careplan.concern.

Careplan.Activity#immunization (0..1) ResourceReference

An extension that references the Immunization reference. Should be populated when the vaccine is either given or offered and declined.

Careplan.Activity.goal (0..*)

To allow us to link this activity back to the goals (the diseases this vaccination is intended to protect against) if we want to.

Careplan.Activity.status (0..1)

Really the only codes that makes sense here are ‘completed’ and ‘cancelled’. However, this is a fixed code set in FHIR so we can’t change it. If due then we can either leave it blank, or we could use the value ‘not started’ or ‘scheduled’

Careplan.Activity.actionResulting (0..0)

Don’t want to record this in the plan

Careplan.Activity.detail (0..0)

The careplan.activity.notes can capture the level of detail that may be required, so this is not required.

Careplan.Activity.simple.category (1..1)

Fix the value as ‘drug’. We might want to suggest that ‘immunization’ be added as an option to the base resource.

Careplan.Activity.simple.code (1..1) [immunizationVaccines]

This will be the vaccine that is to be given so is mandatory. The value is drawn from the immunizationVaccines ValueSet

Careplan.Activity.simple.code#numberOfDoses (0..1) integer

The number of doses of this vaccine that are going to be given altogether

Careplan.Activity.simple.code#doseSequence (0..1) integer

The sequence number for this dose

Careplan.Activity.simple.timing (1..1) Period

Fix to use a period and make required. The ‘start’ property is required. ‘end’ is optional. This will be the period over which the vaccine must be given to be effective.

Careplan.Activity.simple.location (0..0)

No need to specify location in the plan

Careplan.Activity.Simple.product (0..0)

This information is in the code

Careplan.Activity.simple.dailyAmount (0..0)

 Not needed

Careplan.Activity.simple.quantity (0..0)

Not needed

So now that we have an idea about what our profile is going to look like, we can build the profile itself. That will be the topic of the next post!


Using FHIR to record Immunizations

At the last Working Group Meeting we agreed that it would be a good idea to think about holding a ‘clinical connectathon’ where the focus of the event was on how we can use FHIR to meet real clinical scenarios, rather than the more technical, developer based focus of the current connectathons.

The idea is that as we progress through the ‘maturity’ stages of FHIR towards a normative standard, we want to be really sure that it’s fit for purpose, so having an event that takes clinical scenarios, and then ensuring that the standard supports those scenarios makes a lot of sense.

At the moment we’re at the stage of choosing representative scenario’s and then matching the current FHIR resources against those scenarios to see how they work, and identify gaps. I’ve been given the task of looking at paediatric immunizations, so thought I’d share the thinking here. (Actually I volunteered – after all, how hard can it be? <s>)

So lets set the scope of the discussion.

Most countries have a standard paediatric immunization programme (or protocol) – a set of vaccinations that applies to most children, and specifies the ages at which they are given. This can actually get quite complicated – especially when the programme changes and there is a ‘catch-up’ set of vaccinations required for children partway through the programme.

This programme is applied to a specific child, and can be customized for that child – for example they may be allergic to a particular vaccine or to eggs (which influences what vaccines they can be given). This is, in effect a Care Plan (or at least part of a care plan). If the care plan is altered in this way, then there needs to be some record made of why this was done – both for the patients clinical record, but also important for reporting.

When a vaccine is actually administered, the plan is updated and an entry is made in the clinical record for the patient, which includes such details as the date given, person who gave it, vaccine type and other details such as the manufacturer, lot number, site etc.

In some cases, the childs parent will decline to have their child immunized. Again, the reason for this needs to be recorded in the notes, and the plan updated accordingly (it may be the whole plan that was declined, or just a part of if – a specific vaccine).

So here are the main use cases (excluding the ‘workflow’ use cases – the process of recalling the child for the immunizations):

  • Create the childs initial immunization plan from the standard protocol
  • Administer a vaccination, updating clinical record and plan
  • Record any reactions that occur – updating the plan if required
  • Record that a vaccination was declined – updating the plan and the record
  • Update the plan to reflect personal issues – such as allergies – or additional vaccines required
  • Update the plan to reflect a change in the standard protocol (which will include ‘catch up’ vaccinations)

(I’m sure there are others, but these will suffice for this discussion).

So how does FHIR rate against these requirements?

Let’s start with the simple stuff: recording an immunization.

The Immunization resource is used to record the actual administration of a vaccine (or not, as we shall see). It has elements to record the details of the vaccine given, who gave it and when it was given. One thing it doesn’t have is a reference to the Encounter in which it was given, but that’s easily managed by an extension. There are a number of things worth calling out about this resource:

  • vaccineType is the vaccine given (or refused)
  • refusedIndicator is a required Boolean that lets you record that an immunization was offered but declined by the parent. If true, you can use explanation.refusalReason to record why it was refused.
  • reported lets you record vaccinations that you have been told have occurred, but haven’t given yourself.
  • reaction is an element that allows you to record any adverse reaction that occurs subsequent to giving the vaccine. (Of course, this field would be filled in as an update to the Immunization resource – not at the time of creation – unless you are slow at updating your records or the reaction was particularly quick! You’d create the AdverseReaction resource and then reference it from the immunization)
  • performer and requestor record details of the people administering the vaccination.
  • site, route, doseQuantity – how much was given and where
  • manufacturer, lotNumber, expirationDate – details about the vaccine
  • vaccinationProtocol allows you to record the protocol/s that this immunization is a part of – including where it is in a sequence of vaccinations, and the disease being protected against.

Retrieving a childs immunization history is then simple:

GET [host]/Immunization?subject={patientid}&refused=false

(Note I included the refused in the query to only get back the actual administrations. I could just as easily leave that out and manage client side if I wished to – for example – indicating immunizations that had been declined)

Recording the plan

So recording an immunization is straightforward  – what about the set of immunizations that a child should receive – their ‘immunization plan’?

Looking at the spec, there would appear to be a couple of ways – the ImmunizationRecommendation or  Careplan.

Lets talk about ImmunizationRecommendation first.

FHIR defines this resource intended specifically for holding details about what immunizations a patient should receive (Actually it states “A patient’s point-of-time immunization status and recommendation with optional supporting justification“, so we might be on the wrong track, but let’s run with it for the moment…)

Each resource is specific to a patient, and has one or more recommendation elements (each representing a specific vaccine), each with one or more dateCriterion elements giving the dates on which each dose of the vaccine is due.

Properties to callout:

  • recommendation is the element storing details of a single vaccine.
  • recommendation.vaccineType is the actual vaccine to give
  • recommendation.doseNumber is which dose this is in a series. For example, if the vaccineType is ‘pneumococcal’ and there are 4 doses to give, then this recommendation element version might be the date for the 3rd dose.
  • recommendation.forecastStatus indicates where the child is with respect to the protocol – ie due or overdue.
  • recommendation.protocol has more details about the protocol (though I’m not quite clear about how some of these elements differ from those in the recommendation – eg the Recommendation.doseNumber and Recommendation.protocol.doseSequence.

So you can imagine a sequence as follows:

A new child is born (hurrah!). A new ImmunizationRecommendation resource is created for them from the standard protocol (more about that soon). In the most common use case, the ImmunizationRecommendation would simply be a ‘copy’ of the immunization protocol.

Each time an immunization is given, an immunization resource is created and the ImmunizationRecommendation resource updated by removing the appropriate dateCriterion element. (an alternative would be to use an extension to indicate that the vaccine had been given – this would need to be a modifierExtension as it changes the meaning of the element).

Thus the ImmunizationRecommendation resource for a patient has a number of versions representing updates to the plan (generally when an immunization is given, but also updated when declined.

Now let’s have a look at the careplan.

We’ll model this as a separate careplan resource for immunizations. We could incorporate immunization elements in a single larger plan if we wanted do, but a separate plan with a goal of ‘keeping up to date with immunizations’ does seem a bit cleaner, especially if a patient has multiple plans stored in multiple systems).

Properties to callout:

  • Goal is what we’re trying to achieve with this plan.
  • The activity is where we record the vaccinations that the child should receive – one entry per vaccine dose.
  • Activity.simple has the details:
    • Category (this would have the value ‘drug’, though we might want to add ‘immunization’ to the list of possible values )
    • Code – not sure what we’d use this for – the product would indicate the actual vaccine
    • Timing – when the dose needs to be given.
    • Product – this would be the actual vaccine
    • Quantity – how much to give.

The actual sequence is going to be much the same as that for ImmunizationRecommendation.

A child is born and an immunization careplan created for them from the standard protocol. The plan would have one activity for each dose of a vaccine that is required.

As the child progresses through the plan, immunization resources are created and the plan updated. We can be a bit more creative by using the activity.status code though – setting that value to ‘completed/cancelled’ as an immunization is given or declined. This would mean that the status of each dose is immediately obvious from the plan.

(Alternatively we could delete the activity from the plan as it is completed, but that just doesn’t feel right when we have a status to record the outcome.)

What might be quite nice would be to reference the actual immunization resource from the activity using an extension once it has been given or declined.

We’d probably create a profile against careplan to specify the appropriate coding systems, add extensions (like a reference to the actual immunization) and remove the properties that don’t make sense for immunizations .

So which to choose?

Well, to my mind the careplan seems the logical choice.

  • It is where all other planning activity is going to be recorded (either in a single common plan or a collection of them).
  • It has an element (activity.prohibited) that we can use to indicate that a vaccine should not be given – that we might want to use if there is a serious allergy to a vaccine
  • It keeps a nice history of our activity against the plan – and referencing the actual Immunization would be useful here.
  • The timing element can be a period rather than a fixed date – useful to indicate the range of dates when a vaccine can be given to be effective.
  • ImmunizationRecommendation feels like it is more intended to answer the question ‘What should this child receive now (and are they overdue)?’ or ‘how well is this child doing with respect to their immunization plan’ rather than a formal plan of care for an individual.
  • There are elements in ImmunizationRecommendation that don’t really fit in this context – like recommendation.doseNumber and recommendation.protocol.doseSequence

In fact, after I wrote this post the careplan seemed such an obvious choice that I nearly went back and edited out the ImmunizationRecommendation as an option, but in the end decided to leave it there to record how I came to this conclusion.

So we’ve talked about how to record immunizations and how to store the immunization plan for a child. What we haven’t talked about is how to record the standard immunization protocol and apply that to a child – and we’ve got a few options to consider. This applies equally to changes to the standard protocol, and how that effects the plans of children already receiving immunizations.

We should also think about reporting – most jurisdictions have a requirement to report at a population level the overall status of immunization so we want to be sure we’ve covered that off as well.

However, this post is already over-long so we’ll consider these in the next post.

Comments (as always) welcome!

FHIR Connectathon

Kai has done a neat video of the latest FHIR connectathon. Kinda weird to see yourself on camera though!

Using the mailbox endpoint for Immunization Decision Support

One of the great things about attending a FHIR connectathon is that you get to meet smart people who are doing interesting things with FHIR.

At the recent connectathon in phoenix, I saw some work that Richard Ettema of AEGIS  had done with an Immunization forecasting application (these are the same guys helping out with the HL7 conformance testing pilot).

The idea is that there is an immunization Decision Support service (in this case supplied by Nathan Bunker  ) that can take as input  patient demographics  (it only needs their Gender & Date of Birth) and immunizations that they have already received, and returns a list of the immunizations that they are overdue for – and ones that are due in the future.

Now, this application resonated with me for a couple of reasons.

In a previous life I was a PMS (Practice Management System / EMR) vendor, and I recall than one of the hardest things I had to do was to cope with changes to an immunization protocol. It’s just plain hard dealing with children who are part way through a programme to figure out what ‘catch-up’ vaccines they should receive – and when – plus the new ones they need. I remember at the time thinking ‘there must be a better way than everyone doing the same thing’. I also worried about getting it wrong. What if I made a mistake and children got the wrong immunizations? Who would know?

The second reason was that this is a use of the mailbox endpoint in FHIR – and I simply hadn’t picked up on the value of using a real-time messaging paradigm in this way. The mailbox endpoint takes a bundle with a MessageHeader (or Composition) as the first resource, and whatever resources are required for the particular business process. In the case of a message, the MessageHeader resource has an ‘event’ property that indicates the event that caused the message to be created (and by implication/agreement what the server is expected to do with the message). The server performs whatever processing is appropriate and returns a bundle with the response – in this case a collection of ImmunizationRecommendation resources.

Now, I admit that in many cases there does need to be an agreement between sender and server about what the server responsibility for particular event codes in (this is called ‘trading partner agreements’ in the spec) – though I’m sure that the existing v2 event codes and their meanings will apply –  but this does seem to be a neat way of implementing task orientated services within the standard spec.

I ‘borrowed’ these images from the presentation that Richard gave at the WGM (sorry about the image quality – they are screen grabs).

This one shows the overall principle – using FHIR as a common interface to potentially different Immunization DSS engines

Screen Shot 2014-05-16 at 8.37.16 am

The next image shows a couple of implementation architectures. The top image using a ‘proxy’ to interface between FHIR and the DSS engine, and the bottom being where the DSS engine implements the FHIR interface directly (both were demoed at the WGM).

Screen Shot 2014-05-16 at 8.38.13 am


So go check out the demo – it’s all on-line at

Click the “Mailbox” tab, and select “TCH Forecaster” as the FHIR provider (Nathan Bunker’s forecaster service.)

All forecasts assume the current date for the basis of the results returned.  You need to enter the patient demographics (gender and Date Of Birth) at a minimum.  You can then enter up to three (3) immunizations and the date they were administered. (in reality you’d get this from the PMS of course).

Now click the ‘Forecast’ button next to the provider dropdown.

After a short delay, the results are displayed in the tab panel below. The nicest display is the ‘Display Results’ tab that is most useful for a practitioner – the other tabs show the details of the FHIR response from the DSS service, which is what a PMS vendor would use for internal workflow, displays within the product etc.

And please be nice – this sample is supplied as an example service to the community – it’s not a hardened, production level implementation with sophisticated UI, so you could break it I’m sure if you’re that way inclined (and I’d rather you weren’t reading this blog if you are)…

Thanks Richard and AEGIS!