# Automatic HL7v2 to FHIR conversion

Bring every HL7 v2 feed into your FHIR ecosystem with no mapping tables to build or maintain.

Move clinical data from HL7 v2 interfaces into FHIR servers with no mapping tables to build or maintain. One call converts a message that [arrived over MLLP](https://zato.io/docs/dev/healthcare/hl7v2/mllp/index.html) or was [parsed](https://zato.io/docs/dev/healthcare/hl7/v2/parsing/index.html) with `parse_hl7`:

```python
bundle = msg.to_fhir()
```

The result is a typed FHIR R4 transaction bundle - a Patient built from PID, an Encounter from PV1, Observations from OBX, and so on for every segment the message contains. All cross-references between the resources are resolved inside the bundle, and data that has no FHIR equivalent - [custom Z-segments](https://zato.io/docs/dev/healthcare/hl7/to-fhir/z-segments/index.html) and [unknown code values](https://zato.io/docs/dev/healthcare/hl7/to-fhir/codes/index.html) alike - is preserved as extensions, so nothing is dropped.

## A complete service {#a-complete-service}

The service below receives ADT admissions over MLLP, converts each message and posts the resulting bundle to a FHIR server through an [outgoing FHIR connection](https://zato.io/docs/dev/healthcare/hl7/fhir/connections/index.html):

```python
# -*- coding: utf-8 -*-

# Zato
from zato.server.service import Service

# ###########################################################################
# ###########################################################################

if 0:
    from zato.hl7v2.base import HL7Message

# ###########################################################################
# ###########################################################################

class ADTToFHIR(Service):
    """ Receives ADT admissions over MLLP and stores them in a FHIR server.
    """
    name = 'hl7-api.adt-to-fhir'

    def handle(self) -> 'None':

        # The MLLP channel already parsed the raw ER7 bytes for us
        msg:'HL7Message' = self.request.input

        # One call converts the whole message to a FHIR transaction bundle
        bundle = msg.to_fhir()

        # Post the bundle to the FHIR server
        client = self.fhir['FHIR.Sample']
        response = client.execute('', method='post', data=bundle.to_dict())

        self.logger.info('Stored %s in FHIR server -> %s',
            msg.msh.message_control_id, response['type'])
```

The service works without any mapping table or configuration file. When site-specific settings are needed, such as your own identifier system URIs or local code values, they go into an [.ini file](https://zato.io/docs/dev/healthcare/hl7/to-fhir/config/index.html) named in the call, e.g. `msg.to_fhir(config='hl7-fhir-demo')`.

## What comes out of a message {#what-comes-out-of-a-message}

The resources produced depend on the segments present. For a typical ADT^A01 admission:

```python
from zato.hl7v2 import parse_hl7

raw = (
    'MSH|^~\\&|SENDER|FACILITY|RECEIVER|FAC|20260315101112||ADT^A01^ADT_A01|CTL001|P|2.9\r'
    'EVN|A01|20260315\r'
    'PID|||12345^^^HOSP^MR||SMITH^JOHN^A||19800115|M|||123 MAIN ST^^BOSTON^MA^02101\r'
    'NK1|1|SMITH^JANE|SPO\r'
    'PV1|1|I|WARD^101^BED1|||||1234^JONES^MARIA|||||||||||V001\r'
    'AL1|1|DA|70618^Penicillin|SV\r'
    'DG1|1||I10^Essential hypertension^I10|||A\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
RelatedPerson
Location
Location
Location
Practitioner
Encounter
AllergyIntolerance
Condition
```

The two Organizations are the sending and receiving facilities from MSH-4 and MSH-6, referenced from the MessageHeader that opens every bundle. The three Locations are the ward, the room and the bed from PV1-3, each part of the previous one. An ORU^R01 lab result additionally produces a ServiceRequest, a DiagnosticReport, one Observation per OBX and a Specimen per SPM, with the report linked to its order and results. The full segment-by-segment breakdown for every message family is in [what you get per message](https://zato.io/docs/dev/healthcare/hl7/to-fhir/resources/index.html).

## Dict and JSON output {#dict-and-json-output}

When the bundle is needed as plain data rather than a typed resource, two shortcuts convert in one step:

```python
data = msg.to_fhir_dict()
text = msg.to_fhir_json()

# Pretty-printed JSON
text = msg.to_fhir_json(indent=2)
```

Both accept the same `config` argument as `to_fhir`.

## Validation before sending {#validation-before-sending}

Like any typed FHIR resource, the bundle's resources can be validated against the R4 schema before anything leaves your service:

```python
# Zato
from zato.fhir import validate

for entry in bundle.entry:
    result = validate(entry.resource)

    if not result.is_valid:
        self.logger.warning('Validation errors: %s', result.errors)
```

## 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
- [What you get per message](https://zato.io/docs/dev/healthcare/hl7/to-fhir/resources/index.html) - The resources produced for each message family, segment by segment
- [Configuration](https://zato.io/docs/dev/healthcare/hl7/to-fhir/config/index.html) - Identifier systems, timezone, code overrides and the extension base URL
- [Sending bundles to FHIR servers](https://zato.io/docs/dev/healthcare/hl7/to-fhir/sending/index.html) - Posting a bundle, reading the reply and handling rejections
- [FHIR connections](https://zato.io/docs/dev/healthcare/hl7/fhir/connections/index.html) - The outgoing connection that delivers bundles to a server

## 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
- [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
