Getting Wearable Data into Health Records with FHIR Observation
Last verified October 17, 2026 · 7 min read
Sources checked (4) · October 17, 2026
- hl7.org/fhir/R4/observation.html — R4 version and status, scope and uses, element table, rules obs-6 and obs-7, timing, value types, grouping, LOINC and UCUM examples.
- hl7.org/fhir/R4/observation-vitalsigns.html — The wearables rationale, conformance statement, mandatory elements, code and unit table, blood pressure rule, search requirements, Informative status.
- hl7.org/fhir/uv/phd/ — Personal Health Device FHIR IG version 2.0.0 (STU 2) on R4.
- www.ftc.gov/business-guidance/resources/mobile-health-apps-interactive-tool — Developer acting on behalf of a covered entity likely a business associate.
If a provider or health system wants wearable readings in its record and accepts FHIR, each reading becomes an Observation resource. This page covers what the FHIR R4 specification itself requires of that resource and of its Vital Signs profile, read from HL7's Observation and Vital Signs profile pages on October 17, 2026. R4 is version 4.0.1, a "Mixed Normative and STU" release, and HL7's R4 pages say "The current version which supercedes this version is 5.0.0", so ask the receiving system which version and which profiles it accepts.
Why Observation#
HL7 defines Observation as "Measurements and simple assertions made about a patient, device or other subject." Its listed uses include "Vital signs such as body weight, blood pressure, and temperature" and "Device measurements such as EKG data or Pulse Oximetry data". The Vital Signs profile names the wearable case directly: a single vocabulary is needed "Particularly with the use of wearables by patients where they want to/need to share information from those devices."
In R4 the Observation resource is "Normative (from v4.0.0)", while the Vital Signs profile page gives its standards status as Informative.
What every Observation must carry#
HL7 notes that "At its simplest, a resource instance can consist of only a code and a value, and status flag." In R4's element table, status and code are the two mandatory elements:
| Element | Cardinality | What it holds |
|---|---|---|
| status | 1..1 | registered, preliminary, final, amended and further codes, from a required code list |
| code | 1..1 | What was measured; LOINC is the example binding |
| subject | 0..1 | A Patient, Group, Device or Location |
| effective[x] | 0..1 | The clinically relevant time or period, as a dateTime, Period, Timing or instant |
| issued | 0..1 | When this version was made available |
| value[x] | 0..1 | The result, as a Quantity, CodeableConcept, string, boolean, integer, Range, Ratio, SampledData, time, dateTime or Period |
| dataAbsentReason | 0..1 | Why the result is missing |
| device | 0..1 | The measuring Device or DeviceMetric |
| component | 0..* | Results read together, such as systolic and diastolic pressure |
| derivedFrom and hasMember | 0..* | The observations a value was made from, or the members a panel groups |
Two rules apply to every Observation. Rule obs-6 says "dataAbsentReason SHALL only be present if Observation.value[x] is not present", and rule obs-7 says that if Observation.code matches a component's code, the value for that code "SHALL NOT be present".
For timing, effectiveDateTime or effectivePeriod "is the time that the observation is most relevant as an observation of the subject", which for a person is "the physiologically relevant time of the observation"; for an observation obtained directly from the subject, such as blood pressure, it is the start and end of the observation process. In our reading, that is when the watch or cuff measured, not when your server received the reading; issued is the element for when a version was made available.
The Vital Signs profile#
The Observation page says that if implementations use the resource to express the profile's concepts as structured data, "they SHALL conform to the following profiles", naming Vital signs. The profile page puts it this way: "When a FHIR implementation supports any of the vital signs listed below, the implementation SHALL conform to this profile for the vital sign observation." Each vital-signs Observation must have:
- a status;
- a category code of 'vital-signs';
- a LOINC code for what is measured, which the profile calls the "magic value";
- a patient;
- a time indicating when the measurement was taken;
- a numeric result value with a standard UCUM unit or, when there is no numeric result, a reason.
The profile's minimum set of codes and units:
| Vital sign | LOINC code | UCUM unit |
|---|---|---|
| Vital signs panel | 85353-1 | none; member observations linked with hasMember |
| Respiratory rate | 9279-1 | /min |
| Heart rate | 8867-4 | /min |
| Oxygen saturation | 2708-6 | % |
| Body temperature | 8310-5 | Cel or [degF] |
| Body height | 8302-2 | cm or [in_i] |
| Head circumference | 9843-4 | cm or [in_i] |
| Body weight | 29463-7 | g, kg or [lb_av] |
| Body mass index | 39156-5 | kg/m2 |
| Blood pressure panel | 85354-9 | none; systolic and diastolic go in components |
| Systolic blood pressure | 8480-6 | mm[Hg] |
| Diastolic blood pressure | 8462-4 | mm[Hg] |
For blood pressure, "you must have both a systolic and a diastolic component, though one or both may have dataAbsentReason instead of a value." The bindings are extensible: if you also record a more specific code, "implementers must support both the values (LOINC) listed below and the translated code". The table is a floor, and the profile says "The table above is the minimum set, additional vital signs are allowed."
How a server is searched#
The profile's quick start lists searches servers must support: a patient's vital signs by category, by category and date, and by one or more of the LOINC codes, for example GET [base]/Observation?patient=[id]&category=vital-signs. Blood pressure has a trap: the code parameter "searches only Observation.code", so searching for the systolic or diastolic component code "will not return the resource", and the profile points to the combo-code search parameter to search both. The profile also has the server ensure "that every API request includes a valid Authorization token" and reject unauthorized requests "by returning an HTTP 401 Unauthorized response code".
Mapping a wearable reading (our worked example)#
| From the wearable | Into the Observation | Spec basis |
|---|---|---|
| The metric type, such as heart rate | code with LOINC 8867-4 | Vital Signs code table |
| The number and its unit | valueQuantity with the UCUM unit /min | Vital Signs mandatory elements |
| When the device measured | effectiveDateTime, or effectivePeriod for an interval | Observation timing note |
| When your system made it available | issued | Observation element table |
| The user | subject, a Patient | Vital Signs mandatory elements |
| The watch or sensor | device, a Device or DeviceMetric | Observation element table |
| A missed reading | dataAbsentReason, with no value | Rule obs-6 and the profile's reason rule |
| A waveform, such as ECG samples | valueSampledData | Observation value types |
| A metric computed from other readings | derivedFrom, pointing at the source observations | Observation grouping notes |
A heart-rate reading's code and value, in the shape HL7's own examples use for LOINC codes and UCUM quantities:
"code": {
"coding": [{ "system": "http://loinc.org", "code": "8867-4" }]
},
"valueQuantity": {
"value": 72,
"unit": "/min",
"system": "http://unitsofmeasure.org",
"code": "/min"
}
The full resource also needs its status, the vital-signs category, the patient and the time.
Metrics outside the vital-signs table, such as step counts, are still Observations, but R4 binds Observation.code to LOINC only as an example, so the receiving system's own profile decides which code it expects. HL7's grouping notes add that "Profiling will normally be necessary for implementation." HL7 also publishes a Personal Health Device FHIR implementation guide, version 2.0.0 (STU 2), based on R4; we have not reviewed its contents.
Before you build (our recommendation)#
- Ask for the contract. Find out which FHIR version the receiving system accepts and which Observation profiles it enforces; the base resource and the Vital Signs profile are the floor, not the whole agreement.
- Keep independent readings separate. HL7 says a body mass index Observation "should not contain components for height and weight because they are clinically relevant observations on their own", and asks for separate Observations linked with derivedFrom instead.
- Keep your raw data apart from the FHIR projection, so a profile change means re-mapping rather than re-collecting; see normalizing wearable data across providers.
- Expect HIPAA when you send data for a provider. The FTC's health-app tool says a developer operating an app on behalf of a HIPAA covered entity likely is a business associate; see does my fitness app need HIPAA. For the opposite direction, reading clinical records on the phone, see HealthKit clinical records and Health Connect medical records.
Frequently asked questions
- Which FHIR resource carries readings from a wearable?
- Observation. HL7 defines it as measurements and simple assertions made about a patient, device or other subject, lists device measurements such as EKG and pulse oximetry data among its uses, and its Vital Signs profile names patients sharing data from wearables as a reason a common vocabulary is needed. In R4, status and code are the only mandatory elements.
- What LOINC code and unit does FHIR use for heart rate?
- The R4 Vital Signs profile uses LOINC 8867-4 with the UCUM unit /min. The profile adds that 8887-2, heart rate device type, may be included as an additional observation. The Observation also needs a status, the vital-signs category, the patient and the time the measurement was taken.
- How does FHIR represent a home blood pressure reading?
- As one blood pressure panel Observation, LOINC 85354-9, with no top-level value and systolic (8480-6) and diastolic (8462-4) components in mm[Hg]. The profile requires both components, though either may carry a dataAbsentReason instead of a value. A search by the component codes alone will not return it; the profile points to the combo-code parameter.
- How should a missing wearable reading be sent in FHIR?
- Leave the value out and fill dataAbsentReason. Rule obs-6 says dataAbsentReason SHALL only be present when Observation.value[x] is not present, and the Vital Signs profile says that if there is no numeric result you have to supply a reason. When the reading failed with an error, HL7's Observation notes say to use dataAbsentReason with the value error or NaN.
- Should a wearable integration send FHIR R4 or R5?
- Whichever the receiving system accepts. HL7's R4 pages say the current version, 5.0.0, supersedes R4, while R4 stays at its permanent address. The Observation resource and the Vital Signs profile described here are R4's, so confirm the version and any further profiles with the system you send to before you build.
Keep reading
Elsewhere on the site
Pages that share this one’s concepts and sources, from other sections.
Next steps
Was this page useful?
Independent comparison, last reviewed October 17, 2026. Pricing, rate limits, and feature availability change often — confirm current details in each provider’s official documentation before you commit. Product and company names are trademarks of their respective owners; AIFitnessAPI is not affiliated with, endorsed by, or sponsored by any product listed here.
← All fitness apis · by AIFitnessAPI