Observation Profile¶
| Canonical | ../StructureDefinition/nexus-emr-core-observation |
|---|---|
| Status | draft · 1.26.0 |
| Base | Observation (constraint) |
| Source | FSH · JSON |
Nexus EMR profile for the Observation resource, based on CA-Core. Represents measurements, tests, and simple assertions originating from source EMRs for aggregation.
FSH
The authored source of truth: resources/StructureDefinition-nexus-emr-core-observation.fsh (edit on GitHub). One FSH file may define several related artifacts.
// NexusEmrCoreObservation.fsh
Alias: $NexusEmrCoreObservation = ../StructureDefinition/nexus-emr-core-observation
// Profile Definition
Profile: NexusEmrCoreObservation
Parent: Observation
Id: nexus-emr-core-observation
Title: "Observation Profile"
Description: "Nexus EMR profile for the Observation resource, based on CA-Core. Represents measurements, tests, and simple assertions originating from source EMRs for aggregation."
* ^url = $NexusEmrCoreObservation
* ^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([[effectiveDateTime | effectivePeriod.start]])
// Salient fields — the reader/render surface (see SalientField + the Salient RuleSet).
* insert Salient(code, code, coded)
* insert Salient(value, value, quantity)
// value[x] is polymorphic: the `value` salient above renders a Quantity ("133 mmol/L"); this union
// covers the scalar non-Quantity results (coded/string/boolean/integer/dateTime/period/time — e.g. a
// COVID PCR "Positive", an LMP date) so they are not silently dropped. Both chart as `value`; only
// one value[x] is ever present. valueRange/valueRatio/component[] are NOT covered — they need a
// value+unit display primitive the value-expression compiler does not yet have (tracked follow-up).
* insert SalientAs(value_text, [[valueString | valueCodeableConcept.text | valueCodeableConcept.coding.first().display | valueBoolean | valueInteger | valueDateTime | valuePeriod.start | valueTime]], string, value)
// `date` reuses the CURATED `date` accessor (which reads effectiveDateTime), so its expression
// names no Observation element and prose cannot be inherited -- the only salient field in the IG
// where that is true. Authored here instead.
* insert Salient(date, date, dateTime)
* insert SalientShort([[Clinically relevant time of the observation]])
* insert SalientComment([[Reads Observation.effective[x]: effectiveDateTime, or the start of effectivePeriod. This is the time the observation APPLIES to, not when it was recorded -- use meta.lastUpdated or Observation.issued for that.]])
* insert Salient(status, status, code)
* insert Salient(interpretation, interpretation, coded)
// 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(observation)
// --- Element Constraints (Mapping to Requirements R2-R12) ---
* . ^short = "Nexus EMR Observation Core"
* . ^definition = """
Represents clinical measurements, findings, and results, capturing key details
like observation type, value, status, and effective time using standard
Nexus EMR references. Uses instance-specific identifiers for tracking
observations 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 NexusEmrCoreObservation resource for human interpretation."
// Identifier (R2): Enforce standard Nexus EMR Identifier using slicing.
* identifier 0..* MS
* identifier.system 1..1
* identifier.value 1..1
* identifier ^short = "Business identifier(s) for observation"
* identifier ^definition = """
Unique identifiers for the observation.
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 (R3): Mandatory, MS (Inherited from CA-Core).
* status 1..1 MS
* status ^short = "Where the result is in its lifecycle"
* status ^definition = "The status of the result value. Indicates whether the observation is complete and verified."
* status ^comment = "This is a modifier element. Preliminary results should be interpreted with caution."
* status from http://hl7.org/fhir/ValueSet/observation-status (required)
// Prevention/screening status: the worked prevention examples carry this on the
// Observation root; wired in explicitly (2026-08-06) — it was previously declared but
// never referenced by any profile.
* extension contains PreventionStatusExtension named preventionStatus 0..1
* extension[preventionStatus] ^short = "Prevention/screening status (completed externally, refused, ...)"
// Review state (FHIR-29): for results that arrive as standalone Observations (no report
// carrier). ABSENT = unreviewed; same contract as DiagnosticReport's slice.
* extension contains NexusEmrReviewed named reviewed 0..1
* extension[reviewed] ^short = "Review acknowledgement (absent = unreviewed)"
// The two free-text prevention fields R4 gives no element for. Each is a FIELD with its own editing
// lifecycle -- one box, edited and cleared on its own -- which is why each has its own address
// rather than a repeat of `note`. See the note element below for why that distinction is enforced.
* extension contains PreventionResultDetail named preventionResultDetail 0..1
* extension[preventionResultDetail] ^short = "Detail accompanying a screening result (independent of value[x])"
* extension contains PreventionInstructions named preventionInstructions 0..1
* extension[preventionInstructions] ^short = "Instructions given to the patient for this prevention"
// Category (R4): Mandatory, MS (Inherited from CA-Core).
* category 1..1 MS
* category ^short = "Classification of type of observation"
* category ^definition = "A code that classifies the general type of observation being made. Common categories include vital-signs, laboratory, imaging, survey, exam, therapy, and activity."
* category ^comment = "This profile constrains category to exactly one (1..1) -- carry the observation's standard clinical category (e.g. vital-signs | laboratory | imaging | exam | social-history | survey). A prevention Observation is NOT distinguished by its category: it uses its normal clinical category and is marked as a prevention by a coding under the nexus-emr-prevention-catalog NamingSystem on Observation.code.coding."
* category from http://hl7.org/fhir/ValueSet/observation-category (required)
// Code (R5): Mandatory, MS.
* code 1..1 MS
* code ^short = "Observation type with raw code support"
* code ^definition = """
Type of observation (e.g., vital sign, laboratory test, imaging observation).
REQUIRED: text element for human readability
RECOMMENDED: LOINC code when available
OPTIONAL: Raw EMR observation codes for source system preservation
Nexus EMR does not mandate a specific coding system for observations.
The text element ensures human readability even when coded values are not recognized.
Where this code was mapped from a source system's own vocabulary, carry the raw coding alongside the mapped one, flagged `userSelected = true`. See [Carrying the raw code](../guide/patterns/raw-codes.md).
"""
* code.text 1..1 MS
* code.text ^short = "Human-readable observation type (required)"
* code.text ^definition = "Plain text representation of the observation type, required for all entries"
// Slicing on the coding array for observation codes
* code.coding ^slicing.discriminator.type = #pattern
* code.coding ^slicing.discriminator.path = "system"
* code.coding ^slicing.rules = #open
* code.coding ^slicing.description = "Slice by code system"
* code.coding contains
loinc 0..1 MS
// LOINC slice (optional but strongly recommended)
* code.coding[loinc] ^short = "LOINC observation code"
* code.coding[loinc] ^definition = "LOINC code for the observation type when available"
* code.coding[loinc].system = "http://loinc.org" (exactly)
* code.coding[loinc].system ^short = "LOINC system URI"
* code.coding[loinc].code 1..1
* code.coding[loinc].code ^short = "LOINC code"
* code.coding[loinc].display 0..1
// Subject (R6): Mandatory, MS (Inherited). Use identifier.
* subject 1..1 MS
* subject ^short = "Who and/or what the observation is about"
* subject ^definition = "The patient, group, device, or location that this observation is about. For Nexus EMR, this must be a patient referenced by identifier."
* subject ^comment = "References should use identifier-based references following Nexus EMR patterns."
* subject only Reference(NexusEmrCorePatient)
// Encounter: Optional link. MS per CA-Core. Use identifier.
* encounter 0..1 MS
* encounter ^short = "Healthcare event during which observation was made"
* encounter ^definition = "The healthcare encounter during which this observation was made. Optional but provides important clinical context."
* encounter ^comment = "Linking to encounter helps establish the clinical context and timing of the observation."
* encounter only Reference(NexusEmrCoreEncounter)
// Performer: who did it. THE HOME FOR AN OUTSIDE PROVIDER, which is why it is described here.
// A prevention performed elsewhere names a provider this system holds no record of, and R4 already
// has the mechanism: a Reference carrying `display` and no `reference`. Nothing needs inventing,
// and nothing about that provider belongs in `note`.
* performer 0..* MS
* performer ^short = "Who performed the observation; repeat for an outside performer"
* performer ^definition = """
Who was responsible for the observation.
A performer this system holds a record for is referenced normally, with `reference` and a `display`
carrying the readable name. A performer it does NOT hold a record for -- a clinician at an outside
clinic who performed a screening reported back to us -- is a **display-only** reference: `display`
carries the name as recorded and `reference` is absent. That is base R4's own shape for naming a
party with nothing to resolve, and it is the shape to use rather than any encoding inside free text.
The element repeats, so a record may name both: the provider here who owns the entry, and the
outside provider who performed it. A consumer distinguishes them by whether `reference` is present,
never by position.
"""
* performer ^comment = "Where an Observation records a prevention performed outside this clinic, the outside performer named here is the companion to a prevention status of `completed-externally`; the status says it happened elsewhere and this says who. Neither is derived from the other, and either may be present alone."
// Effective[x] (R7): Mandatory, MS.
* effective[x] 1..1 MS
* effective[x] ^short = "Clinically relevant time/time-period for observation"
* effective[x] ^definition = "The time or time-period the observed value is asserted as being true. For biological subjects this is usually the time specimen was obtained."
* effective[x] ^comment = "For observations with a single point in time, use effectiveDateTime. For observations over a period, use effectivePeriod."
* effective[x] only dateTime or Period
// Value[x] (R9): Optional, MS (Inherited).
* value[x] 0..1 MS
* value[x] ^short = "Actual observation result"
* value[x] ^definition = "The actual value of the observation. The data type varies based on the nature of the observation."
* value[x] ^comment = "Use the appropriate data type: Quantity for measurements, CodeableConcept for coded results, string for text results, etc."
* value[x] only Quantity or CodeableConcept or string or boolean or integer or Range or Ratio or SampledData or time or dateTime or Period
// Component: Optional nested observations. MS per CA-Core.
* component 0..* MS
* component ^short = "Component observations"
* component ^definition = "Used when an observation has multiple component observations. For example, blood pressure has systolic and diastolic components."
* component ^comment = "Each component observation stands alone but is interpreted in the context of the overall observation."
* component.code 1..1 MS
* component.code ^short = "Type of component observation"
* component.value[x] 0..1 MS
* component.value[x] ^short = "Component observation result"
// Interpretation: High, low, normal, etc.
* interpretation 0..* MS
* interpretation ^short = "High, low, normal, etc."
* interpretation ^definition = "A categorical assessment of an observation value. Common interpretations include high, low, normal, abnormal, critical."
* interpretation ^comment = "Multiple interpretations may be provided for different aspects of the result."
* interpretation from http://hl7.org/fhir/ValueSet/observation-interpretation (extensible)
// Reference Range: Provides guide for interpretation
* referenceRange 0..* MS
* referenceRange ^short = "Reference range for observation"
* referenceRange ^definition = "Provides guide for interpretation of the observation value. May include age, gender, or other factors."
* referenceRange ^comment = "Reference ranges help determine if values are normal or abnormal for the specific patient."
* referenceRange.low 0..1 MS
* referenceRange.high 0..1 MS
* referenceRange.text 0..1 MS
// Note: THE RESOURCE'S CLINICAL COMMENTS, and nothing else. Unprofiled until now, which left every
// reader to guess both what the element was for and whether its attribution could be relied on.
//
// PLAIN ANNOTATION, DELIBERATELY -- NOT THE TASK NOTE PROFILE. `Task.note` is typed to Task Note
// because a task is a conversation handed between parties and a turn with no author is a statement
// in the record with nobody behind it. A comment on a result is a different animal: a remark about
// the record, not a turn addressed to someone, and requiring an author and a time on every one of
// them would make unattributed comments -- including every comment arriving from a source system
// that never carried the fields -- non-conformant for attribution nothing reads. The Resource Note
// extension is likewise not used here: it exists for resources R4 gives NO note element, and this
// resource has one.
* note 0..* MS
* note ^short = "Clinical comments on the observation -- one whole comment per repeat"
* note ^definition = """
Free-text clinical comment about this observation: 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 -- one
box, edited and cleared on its own -- gets its own address. On this resource the worked examples are
the [prevention result detail](../fhir/StructureDefinition-nexus-prevention-result-detail.md)
and the [prevention instructions](../fhir/StructureDefinition-nexus-prevention-instructions.md),
both of which are extensions for exactly this reason.
"""
* 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. Where both are absent, the comment is attributable only to the resource's own provenance."