Skip to content
HL7 v2 is not dead, it is just pipes and carets
article3 min read244 viewsAug 10, 2026

HL7 v2 is not dead, it is just pipes and carets

The message format that still carries most of a hospital, and how not to lose lab results with it.

Hannah Weiss@hannahweiss

August 10, 2026 · 3 min read

#1 in Vitals and Variables

244 views4 comments

Last time I promised something about HL7 v2. Here it is. If FHIR is the modern REST API, v2 is the message format that has been running inside hospitals since the late eighties, and it still carries most of the admissions, lab orders and results today. You will meet it, even in a "FHIR project", because something behind the FHIR facade still speaks v2.

What a message looks like

MSH|^~\&|LAB|HOSP_A|EHR|HOSP_A|202607091130||ORU^R01|MSG00042|P|2.5.1
PID|1||556677^^^HOSP_A^MR||TEST^PATIENT||19800101|F
OBR|1||LAB-991|24331-1^Lipid panel^LN
OBX|1|NM|2093-3^Cholesterol^LN||182|mg/dL|<200|N|||F

Every line is a segment, named by its first three letters. Segments are separated by a carriage return (\r, not \n, which is the first bug everybody writes). Fields are separated by |, components by ^, repetitions by ~, subcomponents by &, and \ is the escape character. MSH defines these characters itself in MSH-1 and MSH-2, so in theory a sender could choose different ones. In practice nobody does, but a good parser reads them from the message anyway.

The message types you will see first

  • ADT^A01 admit, ADT^A03 discharge, ADT^A08 update patient information. The hospital telling everybody who is where.
  • ORM^O01 or OML^O21, orders going to the lab or radiology.
  • ORU^R01, results coming back, like the example above.

Acknowledgements are not optional

The receiver answers every message with an ACK, and MSA-1 says AA (accepted), AE (error) or AR (rejected). Most interface engines resend, or stop the whole queue, until they get an AA. If your system returns AA before the data is safely stored, you have built a very efficient way to lose lab results. Store first, then acknowledge.

Transport: MLLP

Over TCP the messages are framed with MLLP: a start byte 0x0B, the message, then 0x1C 0x0D. Simple framing, that is the good news. The bad news is that connections stay open for days, firewalls kill idle connections silently, and the sender only notices when no ACK comes back. Monitor the age of the last received message per interface, not only whether the port is open.

Things that will hurt

  1. Splitting on | with a string function works until a field contains an escaped separator like \F\. Use a real parser.
  2. Z-segments (ZPD, ZIN and friends) are local extensions. Every hospital has some. Ask for the interface specification and expect it to be outdated.
  3. The same "version 2.5" from two vendors is not the same. Fields that are optional in the standard are required by one side and always empty from the other.
  4. Timestamps very often come without a time zone offset. This deserves its own post.

It is old, it is ugly, and it works surprisingly well. Treat it with respect, and with a proper parser.

Written by

Hannah Weiss

Clinical informatics engineer in Berlin. I build integrations between hospital systems: HL7v2, FHIR, and a lot of patience. Formerly a nurse, which explains my opinions about alert fatigue.

Get the best of #vitals_and_variables weekly

One email each Monday with the most useful posts. No spam; unsubscribe in one click.

Comments

4

Sign in to join the conversation.

Sign in to comment
Arjun Mehta@arjunmehta#3 in Vitals and Variables
Store first then ACK, same rule as payment webhooks. We return 200 to the gateway only after the DB commit, otherwise they stop retrying and the money is in limbo. Nice to see healthcare learned the same lesson the hard way also
0 net votes
Marcus Johnson@marcus_oncall#2 in Vitals and Variables
"monitor the age of the last received message per interface" this right here. we do the same for EDI feeds at work. an open port tells you nothing, a silent queue at 2am tells you everything
0 net votes
Tomasz Kowalski@tomasz_k#3 in Vitals and Variables
Z segments. In 2009 I worked on hospital lab project and there was ZXX segment which meant three different things depending on sending department. Nobody knew which. Good post, brings back pain.
0 net votes
Hannah Weiss@hannahweiss#1 in Vitals and Variables
Three meanings is still a good result. I have seen one where the meaning depended on the day of the week, because of a night shift workaround from years ago.
0 net votes