Skip to content

Immunization Profile

Canonical../StructureDefinition/nexus-emr-core-immunization
Statusdraft · 1.26.0
BaseImmunization (constraint)
SourceFSH · JSON

Nexus EMR profile for Immunization resources, aligned with CA-Core+ and CII requirements. Records details about vaccines administered to a patient.

FSH

The authored source of truth: resources/StructureDefinition-nexus-emr-core-immunization.fsh (edit on GitHub). One FSH file may define several related artifacts.

// NexusEmrCoreImmunization.fsh 

Alias: $NexusEmrCoreImmunization = ../StructureDefinition/nexus-emr-core-immunization


// Profile Definition
Profile:        NexusEmrCoreImmunization
Parent:         Immunization
Id:             nexus-emr-core-immunization
Title:          "Immunization Profile"
Description:    "Nexus EMR profile for Immunization resources, aligned with CA-Core+ and CII requirements. Records details about vaccines administered to a patient."
* ^url = $NexusEmrCoreImmunization // Canonical URL from alias
* ^status = #draft
* insert NoResourceNoteExtension

// THE effective date for this profile (see the EffectiveDate / NoEffectiveDate
// RuleSets): one date concept per resource, declared here rather than guessed.
* insert EffectiveDate([[occurrenceDateTime]])

// Salient fields — the reader/render surface (see SalientField + the Salient RuleSet).
* insert Salient(vaccine_code, vaccineCode, coded)
* insert Salient(status, status, code)
* insert Salient(occurrence, [[occurrenceDateTime]], dateTime)
* insert Salient(site, site, coded)
* insert Salient(route, route, coded)
* insert Salient(dose_quantity, doseQuantity, quantity)

// SMART on FHIR exposure -- this resource is reachable by an app, and these are the
// permissions each operation costs (see SmartExposed + the SmartExposed RuleSet).
* insert SmartExposed(immunization)

// --- Element Constraints ---

* . ^short      = "Nexus EMR Immunization Core"
* . ^definition = """
  Represents vaccination records and immunization history, capturing key details
  like vaccine product, administration date, status, and site using standard
  Nexus EMR references. Uses instance-specific identifiers for tracking
  immunization records across systems.
  """

* meta.source 0..1 MS
* meta.source ^short = "Identifies EMR instance & pipeline version the resource came from"
* meta.source ^definition = "A URI that identifies the EMR pipeline and version from which this resource originated.  This tells you which EMR instance (recommend using the instance identifier), and the version of the pipeline code/transformations."

* text 0..1 MS // Narrative element with human-readable summary
* text ^short = "Narrative"
* text ^definition = "A human-readable narrative summary of the NexusEmrCoreImmunization resource for human interpretation."

// Identifier: A business identifier is recommended but not required (any system). MS.
* identifier 0..* MS
* identifier.system 1..1
* identifier.value 1..1

* identifier ^short = "Business identifier(s) for immunization"
* identifier ^definition = """
Unique identifiers for the immunization.

A business identifier is recommended but not required (identifier is 0..*). Where available, carry the source-system identifier using the most appropriate system URI. No specific canonical system is mandated.
"""
* insert SourceIdentifier(identifier)


// Status: completed | entered-in-error | not-done
* status 1..1 MS
* status ^short = "completed | entered-in-error | not-done"
* status ^definition = "Indicates the current status of the immunization event."
* status ^comment = "Will generally be 'completed' for administered vaccines."
* status from http://hl7.org/fhir/ValueSet/immunization-status (required)

// Status reason: R4's OWN element for why a dose was not given, described here because it is the
// element that carries the fact -- and because the nearest-looking alternative carries a different
// one. A `data-absent-reason` on `occurrence[x]` says the DATE is unknown, which is a statement
// about an administration that happened; a dose that was refused, deferred or contraindicated never
// happened at all, and that is what this element and `status = not-done` say together.
* statusReason 0..1 MS
* statusReason ^short = "Why the dose was not given (with status = not-done)"
* statusReason ^definition = """
Why this dose was not administered: patient objection, a contraindication, an out-of-stock product,
a deferral.

Read with `status`. This element is meaningful only where `status` is `not-done`; a `completed`
immunization carrying a status reason is contradicting itself. Conversely, a `not-done` immunization
SHOULD carry one -- a record that says only that a dose was not given tells a later reader nothing
about whether to offer it again.
"""
* statusReason ^comment = "This is the element a patient refusal lands on for an Immunization, which is why `Immunization` carries no prevention-status extension: `status = not-done` plus a patient-objection status reason expresses refusal natively, where an Observation needs the extension to say the same thing. The binding is R4's own (`immunization-status-reason`, example strength) and is not narrowed here; carry the source system's own coding alongside where the value was mapped."
* statusReason from http://hl7.org/fhir/ValueSet/immunization-status-reason (example)
// Vaccine Code: Vaccine product administered. MS per CA-Core.
* vaccineCode 1..1 MS
* vaccineCode ^short = "Vaccine product with raw code support"
* insert SourceCoding(vaccineCode)
* vaccineCode ^definition = """
Vaccine product administered or to be administered.

REQUIRED: text element for human readability
RECOMMENDED: a Canadian vaccine vocabulary where a coded value is available -- the National
Vaccine Catalogue (NVC), whose generic-agent, tradename and antigen value sets are published over
SNOMED CT CA, or SNOMED CT itself
ACCEPTED: CVX on a record imported from a US source, carried as received rather than rewritten
OPTIONAL: Raw EMR codes for source system preservation

CVX is the US CDC's vaccine vocabulary and is not used in Canada's immunization stack; Ontario,
OntarioMD and PHAC all work from NVC or SNOMED CT. A record that already carries a CVX code is
still valid here -- this element is unbound, and rewriting a code received from a sending system
would lose what that system meant.

The text element ensures human readability even when coded values are not recognized.
"""
* vaccineCode.text 1..1 MS
* vaccineCode.text ^short = "The vaccine as written, in human-readable form"
* vaccineCode.text ^comment = "Required."
* vaccineCode.text ^definition = "Plain text representation of the vaccine product, required for all entries"


// Patient: Who received the vaccine. MS per CA-Core. Use identifier.
* patient 1..1 MS
* patient ^short = "Who was immunized"
* patient ^definition = "The patient who received or is to receive the immunization. Must reference using Nexus EMR patient identifier."
* patient ^comment = "References should use identifier-based references following Nexus EMR patterns."
* patient only Reference(NexusEmrCorePatient)

// Encounter: Context of administration. Optional but MS per CII.
* encounter 0..1 MS
* encounter ^short = "Encounter when immunization was given"
* encounter ^definition = "The encounter during which this immunization was administered. Optional but provides important clinical context."
* encounter ^comment = "Linking to encounter helps establish the clinical context of the immunization."
* encounter only Reference(NexusEmrCoreEncounter)

// Occurrence[x]: Date/time administered. MS per CA-Core.
* occurrence[x] 1..1 MS
* occurrence[x] ^short = "When vaccine was administered"
* occurrence[x] ^definition = "The date/time when the vaccine was administered to the patient."
* occurrence[x] ^comment = "This is the administration date, not when the immunization was recorded."
* occurrence[x] only dateTime
* occurrence[x].extension contains $DataAbsentReasonExtUrl named dataAbsentReason 0..1 MS

// Site: Body site where vaccine was administered
* site 0..1 MS
* site ^short = "Body site where vaccine was administered"
* site ^definition = "The anatomic site where the vaccine was administered (e.g., left arm, right thigh)."
* site ^comment = "Useful for tracking injection site reactions and following protocols."
* site ^short = "Body site with raw code support"
* site ^definition = """
Body site where vaccine was administered.

RECOMMENDED: SNOMED CT or other standard terminology when available
OPTIONAL: Raw EMR codes for source system preservation
"""


// Route: How vaccine entered body
* route 0..1 MS
* route ^short = "How vaccine entered body"
* route ^definition = "The path by which the vaccine was taken into the body (e.g., intramuscular, subcutaneous, oral)."
* route ^comment = "The route affects absorption and effectiveness of the vaccine."
* route ^short = "Administration route with raw code support"
* route ^definition = """
The path by which the vaccine product is taken into the body.

RECOMMENDED: SNOMED CT or other standard terminology when available
OPTIONAL: Raw EMR codes for source system preservation
"""


// Performer: Who administered the vaccine
// Performer: 0..*, matching base R4, and THE HOME FOR AN OUTSIDE PROVIDER.
// One immunization can legitimately name two parties -- the provider in this clinic who owns the
// entry, and an outside provider who actually administered the dose -- and an outside provider is
// one this system holds no record of. R4 already has the mechanism for naming a party with nothing
// to resolve: a Reference carrying `display` and no `reference`. A single-valued performer left
// that second party with nowhere to go, and nothing about a provider belongs in free text.
* performer 0..* MS
* performer ^short = "Who performed the immunization; repeat for an outside performer"
* performer ^definition = """
Who administered the vaccine.

A performer this system holds a record for is referenced normally, with `actor.reference` and an
`actor.display` carrying the readable name. A performer it does NOT hold a record for -- a clinician
at an outside clinic whose administration is being recorded here -- is a **display-only** reference:
`actor.display` carries the name as recorded and `actor.reference` is absent.

The element repeats, so a record may name both. A consumer distinguishes them by whether
`actor.reference` is present, never by position.
"""
* performer ^comment = "A display-only performer has no `actor.reference` to resolve and therefore does not conform to the referenced profiles below -- that is intended, and is the R4 shape for naming a party that has no resource. `function` is available to say in what capacity each performer acted and is not constrained here."
* performer.actor 1..1 MS
* performer.actor ^short = "Individual who administered the vaccine (display-only when outside this system)"
* performer.actor ^definition = "The practitioner or organization who performed the immunization. Display-only -- `display` set, `reference` absent -- where the performer has no record in this system."
* performer.actor only Reference(NexusEmrCorePractitionerRole or NexusEmrCorePractitioner)

// Lot Number: Required by CII (O3). Optional but MS.
* lotNumber 0..1 MS

* expirationDate 0..1 MS

* doseQuantity 0..1 MS

// Instructions given to the patient: a FIELD with its own editing lifecycle -- one box, edited and
// cleared on its own -- which is why it has its own address rather than a repeat of `note`.
* extension contains PreventionInstructions named preventionInstructions 0..1
* extension[preventionInstructions] ^short = "Instructions given to the patient for this immunization"

// Note: THE RESOURCE'S CLINICAL COMMENTS, and nothing else.
* note 0..* MS
* note ^short = "Clinical comments on the immunization -- one whole comment per repeat"
* note ^definition = """
Free-text clinical comment about this immunization: what a person wanted to say about it that no
other element carries.

**One whole comment per repeat.** `note.text` is the comment in full, and a consumer renders it
verbatim.

**`note.text` is not a carrier for other fields.** A consumer MUST NOT parse it for prefixes,
markers, keys or any other in-band convention, and a producer MUST NOT encode in it a value that
belongs to an element or an extension of its own. Anything with its own editing lifecycle gets its
own address: the instructions given to the patient are the
[prevention instructions](../fhir/StructureDefinition-nexus-prevention-instructions.md)
extension, an outside administering provider is a display-only `performer`, and the reason a dose
was not given is `statusReason`.
"""
* note ^comment = "Attribution is OPTIONAL here, unlike on a task. `author[x]` and `time` keep their base R4 cardinality: a producer that has them SHOULD write them, a consumer displays them when present, and no consumer may require them -- comments reaching this element from a source system routinely carry neither. This is why the element is a plain `Annotation` rather than the [Task Note](../fhir/StructureDefinition-nexus-emr-task-note.md) profile, which requires both because a task's turns must each say who said it."