# HL7v2 to FHIR - what you get per message

The FHIR resources produced for ADT, ORU, ORM, SIU, VXU, RDE, MDM, REF and MFN messages, and the segments each resource comes from.

An HL7v2 message is one flat list of segments, while FHIR splits the same information into separate resources - the patient in PID becomes a Patient, the visit in PV1 an Encounter, each OBX result an Observation. The tables below spell that mapping out, segment by segment and message family by message family.

What a [converted bundle](https://zato.io/docs/dev/healthcare/hl7/to-fhir/index.html) contains depends on the segments actually present in the message, so two messages of the same type can produce different resources. Every bundle opens with the MessageHeader built from MSH, followed by the Organization resources for the sending and receiving facilities from MSH-4 and MSH-6, which the header references as sender and receiver.

## Segments and their resources {#segments-and-their-resources}

| Segment | FHIR resource | Notes |
| --- | --- | --- |
| MSH | MessageHeader | Event coding from MSH-9, source and destination endpoints, `focus` points at the Patient and the Encounter, the MSH-4 and MSH-6 facilities become Organization resources referenced as sender and receiver, MSH-17 becomes the `country` of the sender's address, MSH-19 is the header's `language` and each MSH-21 profile keeps a `message-profile` extension |
| SFT | - | Enriches the MessageHeader - the sending software's name and version go into `source` |
| MSA | - | Enriches the MessageHeader - the acknowledged control ID and outcome become `response` |
| ERR | OperationOutcome | One per ERR, pointed at from `MessageHeader.response.details` |
| EVN | - | Backs the Encounter period up when PV1-44 is empty - the event occurred time first, the recorded time second |
| PID | Patient | Identifiers, names, birth date, gender, addresses, telecoms, language, marital status, birth place, race, citizenship, multiple-birth and deceased indicators, the PID-12 county fills the address's `district` |
| PD1 | - | Enriches the Patient - organizations and practitioners become `generalPractitioner` |
| MRG | Patient | An inactive Patient with the prior identifiers, linked from the surviving one with `replaces` |
| PDA | - | Enriches the Patient - the death advice marks the patient deceased |
| NK1 | RelatedPerson | One per NK1 - name, relationship, address, telecom |
| GT1 | RelatedPerson | One per GT1 - the guarantor's name, relationship, address, telecom |
| PV1 | Encounter | Class, status, locations, attending, referring, consulting and admitting practitioners, service type, admit source, visit number, period |
| PV2 | - | Enriches the Encounter - admit reason and visit description become `reasonCode`, the prior pending location a planned `location`, the expected admit and discharge times the R5 planned start and end date extensions, the actual length of stay `length`, the referral source a `participant`, the visit priority `priority` and the mode of arrival its standard extension |
| ROL | - | Enriches the Encounter - each role becomes a `participant` with its Practitioner |
| ZBE | - | Enriches the Encounter - the IHE PAM movement IDs become identifiers |
| OBX | Observation | Value typing follows OBX-2, see below - plus the body site, the instance identifier, the analysis time and the performing organization with its address. In document messages a text OBX is the document body and an ED one an attachment, in result messages an ED OBX is the report's `presentedForm` |
| AL1 | AllergyIntolerance | One per AL1 - code, category, type, criticality, severity, reactions |
| IAM | AllergyIntolerance | One per IAM - the AL1 successor with action codes and onset |
| DG1 | Condition | One per DG1, also appended to `Encounter.diagnosis` with its use and rank - a working diagnosis is `provisional`, a final one `confirmed` |
| PRB | Condition | One per PRB in problem list messages - code, identifier, category `problem-list-item`, onset, resolution, confirmation and life cycle statuses |
| PR1 | Procedure | One per PR1 |
| IN1 | Coverage | One per IN1, plus the payor Organization |
| IN2 | - | Enriches the Coverage - everything in IN2 is preserved on it |
| FT1 | ChargeItem | One per FT1 - code, quantity, occurrence, performer |
| ORC and OBR | ServiceRequest | One per order group in order and result messages, an ORC with no OBR after it still becomes one |
| OBR | DiagnosticReport | In result messages, one per OBR, linked `basedOn` its ServiceRequest |
| TQ1 | - | Enriches the ServiceRequest - start and end times become the occurrence. After a pharmacy segment it is the dosage timing of the medication resource, after an SCH the Appointment's start, end and duration |
| NTE | - | Becomes a `note` on the resource it follows - an Observation, a ServiceRequest or a medication resource - or the `comment` of an Appointment. With nothing to attach to it is preserved on the MessageHeader |
| SPM | Specimen | One per SPM, appended to the current report's `specimen` |
| SAC | - | Enriches the Specimen - the container identifier |
| SCH | Appointment | In scheduling messages - identifiers, status, timing, duration, the appointment reason from SCH-7, the contact people and the SCH-20 enterer as participants |
| ARQ | Appointment | In scheduling requests - a proposed Appointment |
| AIS, AIG, AIL, AIP | - | Enrich the Appointment - service type and participants, with Location and Practitioner resources where applicable |
| RXA | Immunization | In immunization messages - vaccine code, occurrence, dose, performer, lot number, manufacturer Organization, `primarySource` and `reportOrigin` from RXA-9 |
| RXA | MedicationAdministration | In every other message - medication, effective time or period, dose, performer |
| RXO | MedicationRequest | The original pharmacy order - medication, dose, dosage instructions, dispense request |
| RXE | MedicationRequest | The encoded pharmacy order - medication, dose, dispense request |
| RXG | MedicationRequest | The give instruction - medication, dose |
| RXD | MedicationDispense | The dispense - medication, quantity, handed-over time |
| RXR | - | Enriches the medication resource it follows - route and site |
| BPO | ServiceRequest | A blood product order and its ORC - product, processing requirements, quantity, intended use time, indications |
| IPC | - | Enriches the imaging ServiceRequest - accession number, procedure and protocol codes |
| ZDS | - | Enriches the imaging ServiceRequest - the study instance UID |
| RF1 | ServiceRequest | The referral - status, priority, type, identifiers, dates |
| PRD | - | Enriches the referral - the referring provider becomes `requester`, the referred-to one `performer`, both as Practitioners |
| TXA | DocumentReference | In document messages, body gathered from the text OBX segments that follow |
| STF | Practitioner | In master file notifications - identifiers, names, active flag, telecoms, addresses |
| PRA | - | Enriches the Practitioner - specialties become `qualification`, practitioner ID numbers identifiers |
| PRT | - | Enriches the nearest report, order, observation, condition, document or encounter with a participation |
| Z-segments | Basic | Preserved as [extensions](https://zato.io/docs/dev/healthcare/hl7/to-fhir/z-segments/index.html) |

Repeating segments become multiple resources - three NK1 segments produce three RelatedPerson resources, five OBX segments five Observations, and so on.

Practitioner, Location and Organization resources are created on demand when a field references one, e.g. the attending doctor in PV1-7 or the assigned location in PV1-3, and identical entities are [deduplicated](https://zato.io/docs/dev/healthcare/hl7/to-fhir/references/index.html) across the whole bundle. A PL location such as `WARD1^101^A^GENHOSP` becomes a hierarchy of Location resources - the facility, the point of care, the room and the bed, each `partOf` the one before it and with its own `physicalType` - and the Encounter points at the most specific one, which also takes the PL-5 location status as `operationalStatus` and the PL-6 person location type as `type`.

Any standard segment without a mapping of its own, and an enriching segment that arrives without the resource it enriches - a PV2 with no PV1, an RXR with no pharmacy segment - is preserved as a Basic resource, as Z-segments are.

## Message families {#message-families}

The message structure from MSH-9 selects a conversion family, which determines how order-related segments group together:

| Family | Message structures | Adds on top of the common segments |
| --- | --- | --- |
| Admissions and the rest | ADT and everything not listed below | Nothing - the common segments alone |
| Orders | ORM, OML, OMG, OMI, OMB, ORL, ORR | ServiceRequest per ORC/OBR group |
| Results | ORU, OUL | ServiceRequest and DiagnosticReport per group, with Observations in `result` and Specimens in `specimen` |
| Scheduling | SIU, SRM, SRR | Appointment with participants |
| Immunizations | VXU, VXR, RSP answering a CDC immunization query | Immunization per RXA |
| Medications | RAS, RGV, RDE, RDS, OMP | MedicationAdministration, MedicationRequest or MedicationDispense per pharmacy segment |
| Documents | MDM | DocumentReference from TXA and the text OBX body |
| Problems | PPR, PGL, PRR, PPP | Condition per PRB |
| Referrals | REF, RRI | ServiceRequest from RF1 with the PRD providers |
| Master files | MFN | Practitioner per STF |

## An ORU^R01 example {#an-orur01-example}

```python
from zato.hl7v2 import parse_hl7

raw = (
    'MSH|^~\\&|LAB|FACILITY|EHR|FAC|20260315101112||ORU^R01^ORU_R01|CTL002|P|2.9\r'
    'PID|||12345^^^HOSP^MR||SMITH^JOHN^A||19800115|M\r'
    'ORC|RE|PLACER001|FILLER001\r'
    'OBR|1|PLACER001|FILLER001|24331-1^Lipid panel^LN|||202603150930||||||||||||||||||F\r'
    'OBX|1|NM|2093-3^Cholesterol^LN||187|mg/dL^^UCUM|<200|N|||F\r'
    'NTE|1||Fasting sample\r'
    'OBX|2|NM|2085-9^HDL cholesterol^LN||62|mg/dL^^UCUM|>40|N|||F\r'
    'OBX|3|NM|2571-8^Triglycerides^LN||145|mg/dL^^UCUM|<150|N|||F\r'
    'SPM|1|SPM001||SER^Serum\r'
)

msg = parse_hl7(raw, validate=False)
bundle = msg.to_fhir()

for entry in bundle.entry:
    print(entry.resource.resource_type)
```

```text
MessageHeader
Organization
Organization
Patient
ServiceRequest
DiagnosticReport
Observation
Observation
Observation
Specimen
```

Each Observation has a `valueQuantity` - 187, 62 and 145 mg/dL - and the NTE comment is stored in the first one's `note`. The DiagnosticReport is `final` from the `F` in OBR-25, lists all three Observations in `result` and the Specimen in `specimen`, and everything points back at the Patient.

## Observation values {#observation-values}

The OBX-2 value type determines how OBX-5 is represented:

| OBX-2 | FHIR element |
| --- | --- |
| NM | `valueQuantity`, with units from OBX-6 |
| ST, TX, FT | `valueString` |
| CE, CWE, CNE, CF, IS | `valueCodeableConcept` |
| SN | `valueQuantity`, `valueRange` or `valueRatio`, depending on the structured numeric |
| DT, DTM, TS | `valueDateTime` |
| TM | `valueTime` |
| ED | An `attachment` extension with the decoded content type and data - R4 observations have no attachment value of their own |
| RP | `valueString` with the whole reference pointer |

A `valueQuantity` gets `system` and `code` only when OBX-6 names a coding system, e.g. `mg/dL^^UCUM`. Units with no system keep the `unit` text alone, as the FHIR invariant on Quantity requires.

A value type the parser does not recognize is read as `ST`, so the value arrives as `valueString`. A recognized type outside this table, e.g. `MO` or `CP`, has no FHIR element to go to, so the type and the value are preserved as `unmapped/OBX-2` and `unmapped/OBX-5` [extensions](https://zato.io/docs/dev/healthcare/hl7/to-fhir/z-segments/index.html). Either way the value stays in the output.

## See also {#see-also}

- [Examples](https://zato.io/docs/dev/healthcare/hl7/to-fhir/examples/index.html) - Messages next to the FHIR they become, with the code that converts them
- [Automatic conversion](https://zato.io/docs/dev/healthcare/hl7/to-fhir/index.html) - One call, a complete MLLP-to-FHIR service and validation
- [References and deduplication](https://zato.io/docs/dev/healthcare/hl7/to-fhir/references/index.html) - How the resources point at each other and identical entities merge
- [Customizing code mappings](https://zato.io/docs/dev/healthcare/hl7/to-fhir/codes/index.html) - Standard tables, local values and unknown codes
- [Z-segments](https://zato.io/docs/dev/healthcare/hl7/to-fhir/z-segments/index.html) - Custom segments preserved as Basic resources with extensions

## Learn more {#learn-more}

- [Healthcare interface engine](https://zato.io/docs/dev/healthcare/) - Clinical messages, FHIR, conversion and operations
- [HL7 v2 parsing](https://zato.io/docs/dev/healthcare/hl7/v2/parsing/) - Messages as typed objects, with field access and validation
- [MLLP channels](https://zato.io/docs/dev/healthcare/hl7v2/mllp/) - Receiving and sending HL7 v2 over MLLP sockets
- [FHIR integrations](https://zato.io/docs/dev/healthcare/hl7/fhir/) - Read and write FHIR resources, with paths, bundles and extensions
- [HL7 v2 to FHIR](https://zato.io/docs/dev/healthcare/hl7/to-fhir/) - Converting v2 messages into FHIR bundles, codes and references included
- [EDIFACT](https://zato.io/docs/dev/healthcare/edifact/) - Parsing interchanges, dialects and the transports they arrive on
- [Transformation](https://zato.io/docs/dev/healthcare/transformation/) - One message in, another standard out, in ordinary Python
- [Audit log](https://zato.io/docs/dev/healthcare/audit-log.html) - Every message with its acknowledgment, searchable by patient identifier
- [AI in clinical interfaces](https://zato.io/docs/dev/healthcare/ai/) - Services calling LLMs and AI agents calling clinical services as tools
