Why HL7 v2 to FHIR Migration Matters
HL7 v2.x has been the backbone of healthcare interoperability since the late 1980s. Hundreds of thousands of hospital interfaces worldwide still exchange ADT, ORU, ORM, and MDM messages over MLLP connections. Yet the industry is steadily shifting to FHIR R4 — driven by US federal regulations (CMS and ONC interoperability rules), SMART on FHIR app ecosystems, and cloud EHR platforms that natively speak REST + JSON. Migration projects must bridge these two worlds: preserving the clinical semantics of existing v2 messages while expressing them in FHIR resource graphs.
The Core Mapping Challenge
HL7 v2 and FHIR represent clinical information very differently. A v2 ADT^A01 message encodes a patient admission in a flat, pipe-delimited structure with segment-level relationships (PID → patient data, PV1 → visit data, EVN → event metadata). FHIR R4 represents the same event as a graph of linked resources: a Patient resource referenced by an Encounter resource, with contained or standalone Location and Practitioner resources. The mapping is conceptually straightforward but operationally complex — every component field in a v2 XPN, XCN, CX, or PL data type must be decomposed and reassembled into the right FHIR datatype.
ADT to Patient and Encounter
The most common migration task is converting ADT^A01 (admit) and ADT^A03 (discharge) messages. The PID segment maps to a FHIR Patient: PID-3 (CX list) becomes Patient.identifier, PID-5 (XPN) becomes Patient.name with family and given components, PID-8 becomes Patient.gender using the administrative-gender vocabulary, and PID-11 (XAD) becomes Patient.address. The PV1 segment maps to a FHIR Encounter: PV1-2 (Patient Class) maps to Encounter.class using the v3 ActCode system, PV1-7 (XCN) maps to Encounter.participant (attender), PV1-19 becomes Encounter.identifier, and PV1-44/PV1-45 become Encounter.period.start and .end.
ORU to DiagnosticReport and Observations
ORU^R01 messages carry laboratory, radiology, and clinical observation results. The OBR segment maps to a FHIR DiagnosticReport: OBR-4 (Universal Service ID, CWE) becomes DiagnosticReport.code, OBR-7 becomes effectiveDateTime, and OBR-14 becomes issued. Each OBX segment maps to a separate FHIR Observation resource, with the result set grouped under DiagnosticReport.result references. The OBX value type field (OBX-2) determines the FHIR observation value type: NM (Numeric) maps to valueQuantity with OBX-6 as the unit, CE/CWE maps to valueCodeableConcept, and ST/TX/FT map to valueString.
ORM to ServiceRequest
ORM^O01 messages represent orders. The ORC segment provides order control (ORC-1), placer order number (ORC-2 → ServiceRequest.identifier), filler order number (ORC-3), order status (ORC-5 → ServiceRequest.status), and ordering provider (ORC-12 → ServiceRequest.requester). The OBR segment contributes the procedure code (OBR-4 → ServiceRequest.code), requested date/time (OBR-6 → ServiceRequest.occurrenceDateTime), and priority (OBR-5 → ServiceRequest.priority).
Key Migration Considerations
- Identifier systems: HL7 v2 CX identifiers (PID-3, PV1-19) carry an assigning authority. In FHIR, these should become Identifier.system URIs — ideally OIDs (urn:oid:...) or NamingSystem canonical URLs registered in your FHIR server.
- Terminology: V2 table values (0001 for sex, 0004 for patient class) must be mapped to FHIR-defined code systems. Many organizations need a terminology service or lookup table to handle local codes in OBR-4 or OBX-3.
- Z-segments: Custom Z-segments (ZDG, ZPI, ZRX) have no FHIR equivalent and require extensions or contained resources, adding project complexity.
- Versioning: HL7 v2.3.1, v2.4, v2.5, and v2.5.1 differ in field positions and available data types. A robust mapper must handle multiple versions gracefully.
- Bi-directional flow: Many integration architectures require both v2-to-FHIR and FHIR-to-v2 translation. Design the mapping schema to be reversible where possible.
Three Migration Workflows
The most common workflow starts with a synthetic ADT^A08 demographic update: paste it in, review the generated Patient resource in the mapping table to confirm PID-5's family and given components landed correctly in Patient.name, then copy the JSON straight into the FHIR Resource Validator to catch anything structurally off before it reaches a sandbox server.
A second workflow converts a synthetic ORU^R01 lab result. The mapping table is the fastest way to confirm OBX-2's value type drove the right FHIR shape — an NM (numeric) OBX becomes Observation.valueQuantity with the OBX-6 unit attached, while a CWE result becomes valueCodeableConcept — a distinction worth checking on every new interface rather than assuming it held.
A third converts a synthetic ORM^O01 order and deliberately includes an NK1 (next of kin) segment to see the warnings panel flag it as unmapped — a quick way to build the list of segments your migration project needs a custom extension or contained resource for, before a real interface goes live.
When Not to Use This Tool
The mapper covers ADT admit/discharge/update events, ORU lab results, and ORM orders only — message types like SIU (scheduling), MDM (documents), BAR (billing), and DFT (financial transactions) are not mapped and fall back to a partial Patient-only bundle from the PID segment. NK1, IN1, GT1, AL1, DG1, and any Z-segment are flagged as unmapped rather than translated. One more limit worth stating plainly: selecting FHIR R5 in the version control changes the label but not the mapping logic — the resource shapes produced are R4-structured regardless of which version you pick, so treat the output as R4 and validate it as R4 until R5-specific mapping is added. For anything beyond these message types, or for full profile conformance, plan on a dedicated interface engine or implementation-guide-aware mapping tool.
HL7 v2 Segment → FHIR R4 Path, in Brief
A short excerpt of the field-level mappings this tool applies, useful as a quick-reference alongside the full provenance table the tool generates for every conversion:
| HL7 v2 field | FHIR R4 path | Message type |
| PID-3 (CX) | Patient.identifier | ADT |
| PID-5 (XPN) | Patient.name | ADT |
| PV1-2 | Encounter.class | ADT |
| OBR-4 (CWE) | DiagnosticReport.code | ORU |
| OBX-5 | Observation.value[x] | ORU |
| ORC-5 | ServiceRequest.status | ORM |
The complete, cumulative field-level mapping guidance for the industry is maintained by HL7 as the official v2-to-FHIR Implementation Guide, which this tool's mappings are informed by for the message types it supports.
Local Processing and HIPAA-Regulated Workflows
An ADT or ORU message being migrated is exactly the kind of record HIPAA classifies as protected health information — patient name, MRN, diagnoses, lab values. Running that message through a cloud-based conversion API would mean transmitting live PHI to a third party mid-migration. Because this mapper parses and converts entirely in the browser's JavaScript engine, an integration engineer can convert a real production message on a hospital workstation and know the content never left that machine — a meaningful difference during an active interface cutover, when real messages, not just synthetic samples, need checking.
Working Alongside Other FHIR and HL7 Tools
A migration check typically chains three tools: inspect the source message in the HL7 Viewer to confirm which segments and fields are actually populated, convert it here to FHIR R4, then paste the resulting Bundle into the FHIR Resource Validator to confirm the mapped output is structurally sound before it reaches a server. Running all three in sequence turns "will this message survive the migration?" into a concrete, private answer.