6 min read

Mapping HL7 v2 to FHIR R4 without losing the message

ADT, ORU and ORM carry more meaning than a field-by-field table admits. What to keep, what to derive, and how to prove nothing fell out.

Most HL7-to-FHIR projects start with a spreadsheet: PID-5 goes to Patient.name, PV1-3 goes to Encounter.location, and so on down the segments. The spreadsheet is necessary. It is also where most of the meaning gets lost, because an HL7 v2 message is not just fields. It is an event, sent in an order, by a system with its own habits.

I have built bidirectional mapping tables for ADT, ORU, ORM, MDM and RDE feeds into normalized FHIR APIs, and written the XSLT and DTL that turn C-CDA, FHIR and HL7 v2 into an engine's internal format. The same few things decide whether the result can be trusted.

Map the event, not just the segments

An A01, an A08 and an A03 can carry nearly identical PID and PV1 segments. What differs is what just happened: an admission, a correction, a discharge. If the mapping only looks at fields, every message becomes an update to the same Patient and Encounter, and the history is gone.

  • Decide per trigger event what it creates, what it updates and what it closes. An A03 should end an Encounter, not just refresh it.
  • Treat merges (A40 and its relatives) as their own workflow. A merge touches identity, and identity mistakes spread into every downstream system.
  • Keep the MSH-10 control ID and the event time with the resources you write — a Provenance resource is the FHIR-native place for it — so a record can always be traced back to the message that made it.

Identifiers carry the whole design

A patient in HL7 v2 is usually a list of identifiers in PID-3, each with its own assigning authority. FHIR expects the same thing as Patient.identifier, with a system for each. The mapping has to know which authority is the enterprise ID, which is a local MRN, and which should never be trusted to match on.

  • Give every assigning authority in PID-3.4 a stable system URI, and write that lookup table down once: Patient.identifier.system is what every later match depends on.
  • Start from HL7's own v2-to-FHIR implementation guide for the segment and field maps, then write down every place your feed differs from it.
  • Never collapse identifiers into one field to make a demo work. It will not survive the second facility.

Results and orders are where the clinical meaning lives

ORU results map naturally to DiagnosticReport and Observation, and ORM orders to ServiceRequest. The hard parts are the codes and the units. An OBX with a local code and a free-text unit is not interoperable just because it is now JSON.

  • Bind OBX-3 result codes to LOINC, and findings to SNOMED CT where a mapping exists, and keep the original local code as a second coding rather than replacing it.
  • Carry OBX-8 into Observation.interpretation, OBX-7 into referenceRange and OBX-11 into Observation.status, not just the value in OBX-5. A preliminary result shown as final is a clinical risk, not a formatting problem.

Prove it with the messages you actually receive

Specification examples are clean. Production feeds are not. Before go-live I validate each flow in staging with the analysts who know the source systems, message type by message type.

  • Replay a representative sample of every trigger event and compare what the FHIR side holds with what the message said.
  • Count, per message type, how many messages produced resources, how many were filtered on purpose, and how many failed. The third number should be zero before cutover, and explained if it is not.
  • Keep the raw message for audit. When a clinician asks why a record looks wrong, the answer is in the original HL7.

Where SMART on FHIR fits

Once the data is sound, exposing it is the easier half. I have built FHIR R4 APIs for Patient, Encounter, Observation and DocumentReference with SMART on FHIR scopes and OAuth2. The scopes should follow the mapping: if a resource was never populated reliably, don't publish it just because the standard has a slot for it.

A mapping is finished when every message type has a rule for what it creates, updates and closes, and a count that proves it.

Investors and consultants can book an hour through Office Hours. Teams with an interface to get right can pick a time directly.