Observation

Overview

Observation is the most used resource in clinical FHIR. Lab results, vital signs, social history, clinical findings, screening assessment scores, SDOH data, and patient-reported outcomes all map to Observation. Its breadth is intentional — it is a general-purpose clinical measurement resource — but that breadth creates real implementation risk.

The single most consequential decision when creating an Observation is the category field. Category determines which profile validates the resource. A lab result without category: laboratory will not be found by queries that search for lab results. A vital sign with the wrong category will not conform to the US Core Vital Signs profile family. Getting category right is not optional.

Observation reached normative status in R4. The core structure is stable. R5 adds some improvements (notably bodyStructure and triggeredBy), but the R4 model is what the industry is built on.


Category Codes and the Profiles They Select

Category codeSystemProfile applies
laboratoryobservation-categoryUS Core Laboratory Result Observation
vital-signsobservation-categoryUS Core Vital Signs (and the full profile family)
social-historyobservation-categoryUS Core Smoking Status, US Core SDOH
surveyobservation-categoryPRO instruments, screening assessments
examobservation-categoryClinical examination findings
imagingobservation-categoryImaging-related measurements
procedureobservation-categoryProcedure-related findings
activityobservation-categoryPhysical activity, exercise

The system URI for all of these is http://terminology.hl7.org/CodeSystem/observation-category.

Category is not a free-text field. It drives profile selection by validators, query filtering by client systems, and display routing in EHR portals. A validator checking a US Core Laboratory Result profile will look for category containing a coding with code laboratory in the above system. If it’s absent or uses a different system URI, validation fails.

US Core allows multiple categories on a single Observation. An SDOH Observation may have both social-history and a SDOH-specific category code from a second system.


Key Elements

status

The lifecycle of a result:

  • registered — ordered, not yet resulted
  • preliminary — partial or preliminary result available
  • final — completed, not expected to change
  • amended — result was changed after being final
  • corrected — error was corrected after being final
  • cancelled — never completed; no result
  • entered-in-error — the result record itself was entered in error

status is required. The most common mistake is simply omitting it. A receiving system that doesn’t see status: final cannot know whether to act on the result.

For amendment and correction workflows, FHIR does not require creating a new Observation — the original resource is updated with status: amended or status: corrected. But many implementations create a new Observation and mark the old one entered-in-error. Know which pattern your trading partners use.

code

What was measured. LOINC is the standard for laboratory tests and vital signs. SNOMED CT is used for clinical findings and examination results.

code is the identity of the measurement. It is distinct from the value: code says “this is a serum potassium measurement”; the value[x] says “the result is 3.8 mEq/L.”

For laboratory observations, there is an additional complication: the LOINC code for the ordered test and the LOINC code for the measured result may differ. A test order for “Basic Metabolic Panel” (LOINC 51990-0) produces individual component results each with their own LOINC codes. The Observation’s code should be the code for the measured result, not the order.

subject

Typically a reference to Patient. Can also be Group, Device, or Location for non-clinical observations. US Core profiles require subject to reference a US Core Patient.

effective[x]

When the measurement was made. The choice of variant matters:

  • effectiveDateTime — a single point in time. Use for discrete test results, most lab values.
  • effectivePeriod — a start and end. Use for measurements collected over a period (24-hour urine, continuous monitor average).
  • effectiveTiming — recurring observations with a schedule. Rare.
  • effectiveInstant — millisecond precision. Use for streaming data, high-frequency monitoring.

If the measurement was collected at a specific time, use effectiveDateTime. If it represents a collection window, use effectivePeriod. The wrong choice creates downstream confusion about when the result applies.

issued

The date/time the result was issued into the system (transcription or transmission time). Distinct from effective[x]. effective is when the measurement was taken; issued is when the system recorded it. Both are useful for audit and sync purposes.

performer

Who performed the observation. References to Practitioner, PractitionerRole, Organization, CareTeam, Patient, or RelatedPerson. For lab results, this is typically the laboratory Organization. For vital signs, the nursing staff or device.


value[x] — The Result

The value[x] element is polymorphic. The actual field name changes based on the data type used:

Field nameUse for
valueQuantityNumeric results with a unit (lab values, vitals)
valueCodeableConceptCoded results (positive/negative, blood type)
valueStringFree-text results — use sparingly, prefer coded
valueBooleanTrue/false findings
valueIntegerInteger counts
valuePeriodTime window results
valueRangeResults expressed as a range
valueRatioRatio results (titers)
valueSampledDataWaveform or sampled data (ECG, SpO2 stream)

valueQuantity is the most common for laboratory and vital sign results. It must include UCUM as the unit system when the unit is a standard UCUM unit:

"valueQuantity": {
  "value": 3.8,
  "unit": "mEq/L",
  "system": "http://unitsofmeasure.org",
  "code": "meq/L"
}

unit is the human-readable display string. code is the UCUM code. system must be http://unitsofmeasure.org for US Core conformance.

valueCodeableConcept is correct when the result is a code, not a number. Blood type, organism identification, qualitative results (positive/negative/indeterminate) — these are coded results. Using valueString for “Positive” when there’s a coded value for positivity is a common mistake.


dataAbsentReason

When a result was expected but is absent, dataAbsentReason explains why. Do not omit value[x] without providing dataAbsentReason. The two elements are mutually exclusive — you either have a value or an absence reason, not both.

Common codes from http://terminology.hl7.org/CodeSystem/data-absent-reason:

  • unknown — the value is not known
  • asked-unknown — the patient was asked and did not know
  • refused — the patient refused to provide the result
  • not-performed — the measurement was not performed
  • error — the measurement failed (instrument error, etc.)
  • masked — the value exists but is restricted

A receiving system that sees no value[x] and no dataAbsentReason has no way to distinguish “the value is missing from the data feed” from “the measurement genuinely wasn’t taken.”


interpretation

The lab’s qualitative flag on the result:

"interpretation": [
  {
    "coding": [
      {
        "system": "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation",
        "code": "H",
        "display": "High"
      }
    ]
  }
]

Common codes: N (normal), H (high), L (low), HH (critically high), LL (critically low), A (abnormal), AA (critically abnormal).

interpretation is the lab’s assessment relative to reference ranges. It is not clinical significance — that is a physician judgment. A critically high potassium (HH) is flagged by the lab algorithm; whether it’s clinically actionable depends on patient context.


referenceRange

Normal ranges for the measured value. Can include both a numeric range and textual description:

"referenceRange": [
  {
    "low": { "value": 3.5, "unit": "mEq/L", "system": "http://unitsofmeasure.org", "code": "meq/L" },
    "high": { "value": 5.0, "unit": "mEq/L", "system": "http://unitsofmeasure.org", "code": "meq/L" },
    "appliesTo": [
      {
        "coding": [
          { "system": "http://terminology.hl7.org/CodeSystem/referencerange-meaning", "code": "normal" }
        ]
      }
    ],
    "text": "3.5-5.0 mEq/L"
  }
]

appliesTo can specify that the range applies to a specific age group, gender, or population. Reference ranges are informational — they are not used for validation, only for display and clinical interpretation.


component vs hasMember

This distinction is the most commonly misunderstood structural choice in Observation. Getting it wrong causes downstream query failures.

component: Use when an observation produces multiple values that are not independently meaningful — they are parts of a single measurement that only make sense together.

The canonical example is blood pressure: systolic and diastolic are components of one blood pressure measurement. Neither is meaningful in isolation without the other. They are not separate results; they are facets of a single observation event.

hasMember: Use when a panel or grouper observation points to independently meaningful member observations. Each member is a complete, standalone Observation resource that can be searched, retrieved, and interpreted on its own.

A CBC with differential is a hasMember panel. The white blood cell count, neutrophil count, lymphocyte count, and hemoglobin are all independently meaningful lab results that clinicians query and alert on individually. The panel Observation groups them; the member Observations are the actual results.

Why it matters: A query for Observation?code=8480-6 (systolic blood pressure) will find systolic blood pressure when it is a component on a blood pressure Observation. It will also find it when it is a standalone Observation. But it will NOT automatically traverse the hasMember reference chain to find components of a panel. Systems that store CBC results as components of a single Observation rather than as hasMember Observations will fail blood count queries from systems that expect individual Observations.


JSON Examples

Laboratory Observation

{
  "resourceType": "Observation",
  "id": "obs-potassium-001",
  "status": "final",
  "category": [
    {
      "coding": [
        {
          "system": "http://terminology.hl7.org/CodeSystem/observation-category",
          "code": "laboratory",
          "display": "Laboratory"
        }
      ]
    }
  ],
  "code": {
    "coding": [
      {
        "system": "http://loinc.org",
        "code": "2823-3",
        "display": "Potassium [Moles/volume] in Serum or Plasma"
      }
    ],
    "text": "Potassium"
  },
  "subject": {
    "reference": "Patient/patient-001",
    "type": "Patient"
  },
  "effectiveDateTime": "2024-03-15T09:22:00Z",
  "issued": "2024-03-15T10:45:00Z",
  "performer": [
    {
      "reference": "Organization/lab-org-001",
      "display": "Central Hospital Laboratory"
    }
  ],
  "valueQuantity": {
    "value": 3.3,
    "unit": "mEq/L",
    "system": "http://unitsofmeasure.org",
    "code": "meq/L"
  },
  "interpretation": [
    {
      "coding": [
        {
          "system": "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation",
          "code": "L",
          "display": "Low"
        }
      ]
    }
  ],
  "referenceRange": [
    {
      "low": { "value": 3.5, "unit": "mEq/L", "system": "http://unitsofmeasure.org", "code": "meq/L" },
      "high": { "value": 5.0, "unit": "mEq/L", "system": "http://unitsofmeasure.org", "code": "meq/L" },
      "text": "3.5-5.0 mEq/L"
    }
  ]
}

Blood Pressure (component — two values, one observation event)

{
  "resourceType": "Observation",
  "id": "obs-bp-001",
  "status": "final",
  "category": [
    {
      "coding": [
        {
          "system": "http://terminology.hl7.org/CodeSystem/observation-category",
          "code": "vital-signs",
          "display": "Vital Signs"
        }
      ]
    }
  ],
  "code": {
    "coding": [
      {
        "system": "http://loinc.org",
        "code": "55284-4",
        "display": "Blood pressure systolic and diastolic"
      }
    ],
    "text": "Blood pressure"
  },
  "subject": {
    "reference": "Patient/patient-001",
    "type": "Patient"
  },
  "effectiveDateTime": "2024-03-15T08:30:00Z",
  "component": [
    {
      "code": {
        "coding": [
          {
            "system": "http://loinc.org",
            "code": "8480-6",
            "display": "Systolic blood pressure"
          }
        ]
      },
      "valueQuantity": {
        "value": 142,
        "unit": "mmHg",
        "system": "http://unitsofmeasure.org",
        "code": "mm[Hg]"
      },
      "interpretation": [
        {
          "coding": [
            {
              "system": "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation",
              "code": "H",
              "display": "High"
            }
          ]
        }
      ]
    },
    {
      "code": {
        "coding": [
          {
            "system": "http://loinc.org",
            "code": "8462-4",
            "display": "Diastolic blood pressure"
          }
        ]
      },
      "valueQuantity": {
        "value": 88,
        "unit": "mmHg",
        "system": "http://unitsofmeasure.org",
        "code": "mm[Hg]"
      },
      "interpretation": [
        {
          "coding": [
            {
              "system": "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation",
              "code": "N",
              "display": "Normal"
            }
          ]
        }
      ]
    }
  ]
}

CBC Panel (hasMember — independently meaningful results grouped together)

{
  "resourceType": "Observation",
  "id": "obs-cbc-panel",
  "status": "final",
  "category": [
    {
      "coding": [
        {
          "system": "http://terminology.hl7.org/CodeSystem/observation-category",
          "code": "laboratory"
        }
      ]
    }
  ],
  "code": {
    "coding": [
      {
        "system": "http://loinc.org",
        "code": "58410-2",
        "display": "CBC panel - Blood by Automated count"
      }
    ],
    "text": "Complete Blood Count"
  },
  "subject": { "reference": "Patient/patient-001" },
  "effectiveDateTime": "2024-03-15T09:22:00Z",
  "hasMember": [
    { "reference": "Observation/obs-wbc-001", "display": "White Blood Cell Count" },
    { "reference": "Observation/obs-rbc-001", "display": "Red Blood Cell Count" },
    { "reference": "Observation/obs-hgb-001", "display": "Hemoglobin" },
    { "reference": "Observation/obs-hct-001", "display": "Hematocrit" },
    { "reference": "Observation/obs-plt-001", "display": "Platelet Count" }
  ]
}

Each hasMember reference points to a complete, standalone Observation with its own code, valueQuantity, referenceRange, and interpretation. Clients can retrieve individual results without fetching the panel grouper.


Search Parameters

ParameterTypeFinds
codetokenObservations with this LOINC or other code
patientreferenceObservations for this patient
categorytokenObservations with this category code
datedateObservations with effective date matching
statustokenObservations with this status
performerreferenceObservations performed by this resource
value-concepttokenObservations with a coded value matching
value-quantityquantityNumeric values matching (supports prefixes: gt, lt, etc.)
component-codetokenObservations with a component having this code
component-value-quantityquantityObservations with a component value matching
combo-code-value-quantitycompositeComponent or top-level code + quantity value

Typical laboratory query:

GET [base]/Observation?patient=Patient/123&category=laboratory&date=ge2024-01-01&_sort=-date&_count=50

Systolic blood pressure component query:

GET [base]/Observation?patient=Patient/123&component-code=http://loinc.org|8480-6&component-value-quantity=gt140

US Core Profiles

US Core defines a family of Observation profiles. The relevant profile is determined by the category code and, for vital signs, the specific code value:

ProfileCategoryCode
US Core Laboratory Resultlaboratoryany LOINC result code
US Core Vital Signsvital-signspanel LOINC (any)
US Core Body Temperaturevital-signs8310-5
US Core Body Weightvital-signs29463-7
US Core Body Heightvital-signs8302-2
US Core Blood Pressurevital-signs55284-4
US Core BMIvital-signs39156-5
US Core Head Circumferencevital-signs9843-4
US Core Oxygen Saturationvital-signs59408-5
US Core Respiratory Ratevital-signs9279-1
US Core Heart Ratevital-signs8867-4
US Core Smoking Statussocial-history72166-2

US Core Vital Signs requires UCUM units in valueQuantity. US Core Laboratory Result requires LOINC in code for most tests.


R5 Changes

R5 adds:

  • bodyStructure: A reference to a BodyStructure resource, replacing the coded bodySite element for complex anatomy scenarios.
  • triggeredBy: Describes what triggered this Observation — another Observation, a condition, or a protocol step. Useful for reflex testing chains.
  • instantiates[x]: References the protocol or plan that defined this observation.

For R4-based implementations, these additions are informational. The core element model is unchanged.


Implementation Considerations

LOINC test code vs result code. When a laboratory receives an order, the order may have one LOINC code; the result has another. The Observation code should be the result’s LOINC code — what was measured — not the order code. For panels, the panel LOINC goes on the grouper; individual result LOINCs go on the hasMember Observations.

UCUM is required for valueQuantity.system. US Core profiles require http://unitsofmeasure.org as the system for any numeric quantity. Local unit strings without UCUM codes fail validation and break unit conversion logic in receiving systems.

The “everything is an Observation” trap. Observation is flexible enough to hold almost any clinical datum, but that doesn’t mean it should. A diagnosis is a Condition. A procedure that was performed is a Procedure. A prescription is a MedicationRequest. Shoehorning these into Observations because it’s convenient creates data that downstream systems — which query Condition, Procedure, and MedicationRequest specifically — will never find.


Common Mistakes

Missing status. Observation.status is required (cardinality 1..1) and normative validators will reject its absence. Default to final for completed results; use preliminary for partial results; use entered-in-error for corrections that invalidate the original.

Omitting category. Without category, the resource can’t be correctly routed to profile validation, can’t be found by category-filtered queries, and won’t appear in category-specific clinical views. Always include category with the correct system URI.

valueString for coded results. If the result is positive, negative, or indeterminate, use valueCodeableConcept with a coded value from SNOMED CT or a local CodeSystem. valueString: "Positive" is unvalidatable, untranslatable, and fragile to free-text variation.

component for independently meaningful panel results. Blood pressure components are correct as component. CBC results are not — they belong as hasMember Observations. The test: can this result be clinically queried, alerted on, or trended independently? If yes, it should be its own Observation.

Wrong effective[x] type. Using effectiveDateTime for a 24-hour urine collection truncates the measurement period to a single point. Use effectivePeriod with explicit start and end.

Section: fhir Content Type: reference Audience: technical
FHIR Versions:
R4 R5
Published: 11/04/2023 Modified: 03/11/2025 14 min read
Keywords: FHIR Observation laboratory results vital signs LOINC UCUM component vs hasMember US Core Observation valueQuantity
Sources: