FHIR2 Backlog - Implement the Immunization Resource

Hey everyone,

I have created a backlog item for implementing the Immunization Resource as necessary core resource in here → FM2-185

At minimum we should support:

  • patient
  • encounter
  • status
  • vaccineCode
  • occurence
  • location
  • manufacturer
  • lotNumber
  • expirationDate
  • performer
  • protocolApplied

Note: protocolApplied is necessary to record the Dose and Booster series For example, please refer here → Immunization example for Protocol Applied

Please share your thoughts

tagging: @mksd @ibacher @rrameshbtech @mddubey @vasanth2019 @sivareddy

1 Like

Hello everyone,

I am summarising all the chat from the card FM2-185 so we can confirm the openmrs data model of the immunization based on the above FHIR model.

This is how we see things as of now:

  • Status => An obs with Text Value (Fixed as completed for now)
  • Vaccine code => An obs for CIEL:984
  • Occurence => An obs for CIEL:1410
  • Location => EncounterLocation
  • Manufacturer => An obs for CIEL:1419
  • LotNumber => An obs for CIEL:1420
  • ExpirationDate => An obs for CIEL:165907
  • Performer => EncounterProvider with specific Role

We have few questions:

  • We wanted to capture doseNumber/booster which is provided as part of protocolApplied.doseNumber[x]. Would it be a good idea to have two different obs to store doseNumber(a numeric value) and boosterInfo(a text value)?

  • In our implementation, we have a use case of capturing other vaccine, in case the given vaccine is not present in the selection list. How should we incorporate this in the openmrs data-model? Does this look like a case non-coded-vaccine obs(text value)? We are thinking it would be similar to condition(coded/non-coded) and diagnosis(coded/non-coded)?

Please let us know your thoughts.

CC: @mksd @ibacher @rrameshbtech @vasanth2019 @sivareddy @kinle @vasanth2019 @akanter


Hi @andrew, @burke, @jdick, @jteich, what’s your take on the below?

In short how do we record vaccinations for a vaccine that is not (yet) in the system?

1 Like

The status is meant to be coded as one of the immunization status codes:

  • completed
  • entered-in-error
  • not-done

@ibacher, how do you suggest we go about this?

I don’t know why I ask, it will just be a question with those three coded answers embedded I guess.

So my intention was to leave Status just as a fixed value of completed until there is some way in the model to track the status of an immunization. If we want to add an Obs to track this, then, yes, we should have a concept set that corresponds to those statuses.

We probably need an obs to store that; otherwise we can’t really populate it. @akanter is there a CIEL concept which could correlate to dose number?

I think the CIEL:1418 is supposed to do that for dose number. However we are not quite sure if there is something for the booster Information.

I think you have to consider several things: 1) dosing of a vaccine may not be valid (due to many reasons) and therefore just because something is labeled as OPT3 it might not be the third valid dose. That is why we use sequence number separately from the concept. 2) Whether something is considered a booster or not depends on formulation of the vaccine, so I am not sure that it matters what the reason is for the vaccine, but rather only when it was given, and its content. 3) Vaccines frequently have different components and whether a child has completed a particular series may depend on analyzing several different vaccine types administered. Hence the requirement that vaccines be coded with CVX codes. Having a free text entry is really not a good idea. 4) If you give someone a place to record something in free text, and they can’t bother to figure out which of the coded options is the correct one, they will use the free text version.

1 Like

@akanter Does this mean we should support multiple doses of the same sequence number? (There are no data constraint reasons we can’t; I just want to make sure I understand the data we need to maintain).

I am just saying the recording the sequence number is independent of what the true sequence number is. It is possible that someone could try to record the same sequence number more than once. However, the true sequence number should be calculated anyway. It would be interesting to tease our what sort of UI validation you want to include here.

I see. Does this sequence number align with the definition of a FHIR dose number (see here), that is the “Nominal position in a series” or should we be storing that information differently. I.e., if I want to track “this (should be) the third valid dose” how should I capture that?

It would be fairly easy to enforce “this sequence number is the next in the sequence for a given patient and vaccine”, but I’m a bit worried that “sequence number” is something other than it’s relation to a dosing series.

Ugh. This is actually complicated over talk. I wonder if it is worth having a call to reconcile with some experts. I can ping some of the folks from the NY EzVac group and see what they think.

I think that would be a good idea to hash this out a bit.

@mksd @kinle @jdick Who should be involved with such a discussion?

@akanter you mean that a tetanus vaccine regular dose and tetanus vaccine booster would in fact be defined by two different concepts? Two concepts coded with different CVX codes? And it is by looking at the CVX code that we in fact know whether it is a dose or booster?

If yes, is this an international standard? I’m asking because it seems to be from the CDC.

It looks like we have a use case that will require to be flexible at runtime. Can we use an ‘other vaccine’ concept and, instead of introducing an ‘IMMUNIZATIONS NON-CODED’ obs (cfr here), we would introduce an ‘additional details’ field (“à la conditions”, see here)?

Hi @burke, @ibacher and @jdick, we had a call this morning with @mddubey and @vasanth2019 and we came up with a couple findings and conclusions, see below.


1. Immunizations belong at the patient level

For the sake of this first iteration in OpenMRS 3.0, immunizations are simply represented as being at the patient level (like allergies or conditions). In short the UX is such that the user views/creates/edits immunizations from the patient summary, without really being concerned as to which visit or encounter those immunizations are linked to.

It doesn’t mean that they are not linked to an encounter, they are behind the scenes, but that’s largely not relevant in the upcoming UX and should therefore not be of much concern to the user. The way those immunizations are linked to a visit and an encounter is handled by our specific implementation of the FHIR resource (that is still to come btw):

  • When creating a new immunization, it is linked to the visit that was set for the patient summary. By default it should be the current visit if there is such a unique thing. Our idea anyway is that the POST content must actually provide the visit UUID as the Immunization.encounter.
    What happens next is really internal cuisine based on a backend config:

    • Either a new immunization encounter is created each time as part of that specified visit,
    • Or a single immunization encounter is reused as part of that specified visit.
    • If the visit UUID points to a past visit then that’s how retrospective data entry is done. But again, the user might not even realise that they are doing so since from a UX standpoint all this looks like it’s happening at the patient level anyway.
  • Editing an immunization is straightforward, we go right to it through its UUID and edit it (the original obs group is voided and a new one is attached to the original encounter). By the way that means that the FHIR immunization UUID is in fact the UUID of the OpenMRS obs construct saved behind the scenes.

  • Viewing immunizations is an action that answers the question: “give me all the immunizations for this patient”. The only search parameter that the FHIR resource will support is thus a patient reference.

2. Immunizations sequences definitions as a front-end config (for v1 at least)

  • The config will point to a concept set that lists all vaccines that should be part of the immunizations UX.
  • The config will also provide a way to define immunization sequences for each vaccine, such as for example:
    • Dose 1
    • Dose 2
    • Booster 1

Example (WIP):

  "immunizationsConfig": {
    "vaccinesConceptSet": "CIEL:984",
    "sequencesDefinition": [
            "vaccineConceptUuid": "820BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
            "sequences": [
                    "sequenceLabel": "Dose 1",
                    "sequenceNumber": "1"
                    "sequenceLabel": "Dose 2",
                    "sequenceNumber": "2"
                    "sequenceLabel": "Booster 1",
                    "sequenceNumber": "11"

Following the above config, all vaccines from CIEL:984 would be used and a specific sequence is defined only for ‘POLIO VACCINATION, ORAL’ (820BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB).

Cc @bistenes @ddesimone

1 Like

I’m not sure I quite understand what’s being said here. It’s not too important, but we should ensure that the UUID is stable.

Otherwise, I think this is fine.

Is the intention here to only allow sequences defined by concept UUIDs?

@ibacher sorry I meant the FHIR immunization resource identifier. We will use the OpenMRS UUID of the obs construct as the FHIR immunization resource identifier. It’s indeed stable and that makes a one to one mapping.

Is this ok in your opinion (when creating immunizations):


Yes, that’s the correct use of the FHIR encounter resource. For consistencies sake, we should probably modify the existing encounter resource to also return Visits, at least by UUID.

Thanks for confirming.

(Thanks also for going through my post btw :wink: )

I’m not sure how @mddubey is fetching the concept set and what is available about its members. If the concept mappings are available for each of the vaccines, then we can certainly allow both UUIDs and same as mappings.

Hello @ibacher, @mksd.

The default concept response only provides uuid, name and link for the set-members, so it is not possible to use mappings. To overcome this we could always go for full representation(?v=full) in which case the full representation for each property is provided in nested fashion.