Getting Started

Installation

Install hl7types using pip:

pip install hl7types

Or with uv:

uv add hl7types

To install directly from the repository:

pip install git+https://github.com/KeironO/hl7types.git

Or with uv:

uv add git+https://github.com/KeironO/hl7types.git

Building a message

Every HL7 v2 message is a structured container of segments. Each segment has a defined role in the message and carries a specific set of fields. The message type determines which segments are required and which are optional.

We will use ACK (general acknowledgement) as our example, it is one of the most common messages in any HL7 deployment and has a minimal structure that makes it easy to reason about. It requires exactly two segments: MSH (message header), which is mandatory in every HL7 v2 message and carries routing and encoding metadata, and MSA (message acknowledgement), which carries the acknowledgement code and the control ID of the message being acknowledged.

Segments themselves are composed of datatypes. MSH uses HD (hierarchic designator) to identify sending and receiving applications, TS (time stamp) for the message date and time, MSG to encode the message type and trigger event, PT for the processing ID, and VID for the HL7 version. Each of these is itself a typed model, so invalid values are caught at construction time rather than at the wire boundary.

from hl7types.hl7.v2_5_1.messages import ACK
from hl7types.hl7.v2_5_1.segments import MSA, MSH
from hl7types.hl7.v2_5_1.datatypes import HD, MSG, PT, TS, VID

msh = MSH(
    msh_3=HD(hd_1="SENDING_APP"),
    msh_5=HD(hd_1="RECEIVING_APP"),
    msh_7=TS(ts_1="20260101120000"),
    msh_9=MSG(msg_1="ACK"),
    msh_10="MSG000001",
    msh_11=PT(pt_1="P"),
    msh_12=VID(vid_1="2.5.1"),
)

msa = MSA(
    msa_1="AA",        # AA = Application Accept
    msa_2="MSG000001", # control ID of the message being acknowledged
)

ack = ACK(MSH=msh, MSA=msa)

Encoding to ER7

ER7 is the traditional pipe-delimited wire format defined by the HL7 v2 specification. Each segment occupies its own line, identified by its three-letter name (MSH, MSA, and so on). Within a segment, fields are separated by |, components within a field by ^, repeated values by ~, and subcomponents by &. These four characters, along with the escape character \, are declared in MSH.2 and are present in every ER7 message.

An important detail of the ER7 wire format is that segments are terminated by a carriage return (\r), not a newline (\n). This is mandated by the HL7 specification and is the norm across all HL7 v2 implementations, so splitting on \r is the correct way to recover individual segments from a raw wire string.

Call model_dump_er7() to encode the message:

er7 = ack.model_dump_er7()

for segment in er7.split("\r"):
    print(segment)

Output:

MSH|^~\&|SENDING_APP||RECEIVING_APP||20260101120000||ACK|MSG000001|P|2.5.1
MSA|AA|MSG000001

Decoding back from ER7

Rather than specifying the message class explicitly, use the top-level decode_er7 helper. It reads the message type and version directly from MSH in the wire string and resolves the correct model class automatically, which is how real pipelines work, the receiving system does not always know the message type in advance.

from hl7types import decode_er7

decoded = decode_er7(er7)

print(decoded.MSH.msh_3.hd_1)   # "SENDING_APP"
print(decoded.MSA.msa_1)        # "AA"
print(decoded.MSA.msa_2)        # "MSG000001"

The result is a fully typed ACK instance, field access, validation, and serialisation all work exactly as they did on the original object.