HL7v2 to FHIR - references and deduplication

Deliver each HL7v2 message as one consistent resource graph - fullUrl and urn:uuid references, deduplication and bundle_type effects.

A single HL7v2 message usually describes several connected entities - a patient, their visit, the attending doctor, lab results. The conversion turns each of them into a separate resource and wires the connections between them, so the bundle arrives at the FHIR server as one consistent graph.

Every entry has a fullUrl

Each bundle entry includes a fullUrl in the urn:uuid: form and other entries reference it through that URL:

{
  "fullUrl": "urn:uuid:237f706c-3f37-5ee3-98bf-fe8c006c2aa5",
  "resource": {
    "resourceType": "Encounter",
    "subject": {
      "reference": "urn:uuid:98e4f8aa-a5ad-5791-add1-66fd3afb4f83"
    }
  }
}

Here the Encounter's subject points at the Patient entry from the same bundle. When a FHIR server processes the transaction, it assigns real IDs to the created resources and rewrites all the urn:uuid: references to point at them - the graph stays intact on the server side.

The UUIDs are deterministic - they derive from the resource's content, so converting the same message twice yields the same URLs, which makes bundles reproducible and easy to compare in tests.

The references set by the conversion include:

  • Encounter.subject, Observation.subject, Condition.subject and every other clinical resource pointing at the Patient
  • MessageHeader.focus pointing at the Patient and the Encounter, with sender and the destination's receiver pointing at the facility Organizations from MSH-4 and MSH-6
  • Encounter.participant and Encounter.location pointing at Practitioners and Locations, and each Location's partOf pointing at the one that contains it
  • DiagnosticReport.basedOn pointing at the ServiceRequest, DiagnosticReport.result at Observations, DiagnosticReport.specimen at Specimens
  • Coverage.payor pointing at the insurer Organization

The bundle itself records the message's identity too - identifier is the MSH-10 control ID, timestamp the MSH-7 message time and meta.tag the MSH-11 processing ID.

Deduplication

The same entity often appears multiple times in one message - the same doctor in PV1-7 and ORC-12, the same organization in several IN1 segments. Practitioner, Location, Organization and Device resources with identical content are created once, and every later occurrence becomes a reference to the first one:

from zato.hl7v2 import parse_hl7

raw = (
    'MSH|^~\\&|SENDER|FACILITY|RECEIVER|FAC|20260315101112||ADT^A01^ADT_A01|CTL001|P|2.9\r'
    'PID|||12345^^^HOSP^MR||SMITH^JOHN^A||19800115|M\r'

    # The same doctor is both the attending and the referring physician ..
    'PV1|1|I|WARD^101^BED1||||1234^JONES^MARIA|1234^JONES^MARIA\r'
)

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

# .. yet the bundle contains a single Practitioner.
for entry in bundle.entry:
    print(entry.resource.resource_type)
MessageHeader
Organization
Organization
Patient
Location
Location
Location
Practitioner
Encounter

Both Encounter.participant entries point at the one Practitioner. The three Locations are the ward, the room and the bed from PV1-3, each partOf the one before it. Deduplication is content-based and limited to the four resource types that are never changed after they are created - Practitioner, Location, Organization and Device. A Patient or an Encounter is built up by several segments, so two of them are never merged.

What bundle_type changes

By default the bundle is a transaction - the server applies all entries atomically and each entry has a request with POST and the resource type as the URL. The configuration file can change this:

[bundle]
type=batch
  • transaction - all or nothing, the server rolls everything back if any entry fails
  • batch - the same entries, but each is processed independently and failures do not affect the other entries
  • collection - no request elements at all, the bundle is a plain container of resources, useful for storing or forwarding the conversion result without a FHIR server interaction
  • message - no request elements either, the bundle is a FHIR message whose first entry is the MessageHeader, for servers that accept messages at $process-message

In all four cases the fullUrl values and the urn:uuid: references stay the same.

See also

PageWhat it covers
What you get per messageThe resources the references connect, per message family
Sending bundles to FHIR serversHow servers rewrite the urn:uuid references into real IDs
ConfigurationThe bundle type setting and everything else the .ini file holds

Learn more


Schedule a meaningful demo

Book a demo with an expert who will help you build meaningful systems that match your ambitions

"We evaluated 12 integration platforms and Zato was the only one to score 100%."

Philip Zuñiga, Assistant Professor, University of the Philippines