HL7 v2 messages are built from a hierarchical set of building blocks: segments contain fields, fields contain components, and components contain sub-components. Each level is separated by a specific delimiter character, creating a compact, human-readable format that is efficient to parse and resilient to local variation.
Segments → Fields → Components → Sub-components
The HL7 v2 message framework is hierarchical, consisting of named building blocks called elements. Understanding the nesting structure is essential to reading, parsing, and generating valid messages.
Segments are the top-level logical units. Every segment begins with a three-character identifier (the segment ID) followed by the field separator, then one or more fields. Common segments include:
- MSH: Message header (required; always first)
- PID: Patient identification
- OBR: Observation request (order)
- OBX: Observation result (single result value)
- ERR: Error information
Each segment is encoded on a single line, terminated by a line break (carriage return, line feed, or carriage return + line feed).
Fields are named data elements within a segment, identified by sequence number. For example, PID-3 is the patient identifier field within the PID segment. Fields are separated by the field-separator character (default: |). A field may be:
- Simple: A string value like "John" or "12345"
- Complex (composite): A structured data type like XPN (extended person name), which is itself composed of named components
Components are named sub-elements within a complex field. The PID-5 field (patient name) is an XPN data type with components:
- XPN-1: Family name
- XPN-2: Given name
- XPN-3: Middle name
- XPN-4: Name prefix
- XPN-5: Name suffix
Components are separated by the component-separator character (default: ^).
Sub-components are named sub-elements within a component, separated by the sub-component-separator character (default: &). For example, the CX data type (extended composite ID with check digit) has component CX.4 (assigning authority), which has sub-components:
- CX.4.1: Namespace ID
- CX.4.2: Universal ID
- CX.4.3: Universal ID type
This four-level hierarchy (segment, field, component, sub-component) allows HL7 v2 to represent rich clinical data efficiently while remaining lightweight and easy to transmit over legacy systems.
Delimiters Explained
HL7 v2 uses five special characters as delimiters. The standard set is |^~&, defined in the MSH segment's encoding-characters field. Each delimiter serves a specific purpose:
| Delimiter | Name | ASCII | Default | Purpose |
|---|---|---|---|---|
| ` | ` | Field separator | 124 | ` |
^ | Component separator | 94 | ^ | Separates components within a field |
~ | Repetition separator | 126 | ~ | Separates repetitions of a field |
| `` | Escape character | 92 | `` | Escapes special characters within field values |
& | Sub-component separator | 38 | & | Separates sub-components within a component |
Field separator (|): Divides the fields of a segment. Every segment uses this character after the segment ID and between each field.
Component separator (^): Divides components within a complex (composite) field. Not used in simple fields. For example, in the XPN data type:
John^Mary^Jr^Dr^
Here, John is the family name, Mary is the given name, Jr is the suffix, Dr is the prefix (note the trailing ^ indicates an empty component for the middle name).
Repetition separator (~): Allows a field to repeat, carrying multiple values. For example, a patient may have multiple identifiers:
12345^MRN~67890^LAB~EXTERNAL456^EXTERNAL
This field repeats three times, each with an identifier and an identifier type.
Escape character (``): Allows you to include a delimiter character inside a field value without breaking the message structure. For example, to include the text Hello|World inside a field:
HelloFWorld
The F represents a literal |. The escape codes are:
F→ field separator (|)S→ component separator (^)T→ sub-component separator (&)E→ escape character (``)R→ repetition separator (~)
Sub-component separator (&): Divides sub-components within a component. Used only in data types that have sub-components, like CX (composite ID with check digit):
12345&MRN&facility&2
Here, the first sub-component is the ID value, the second is the check digit, the third is the assigning authority namespace, and the fourth is the assigning authority universal ID type.
MSH Header — Field-by-Field
The MSH (Message Header) segment is always the first segment in an HL7 message. It defines the message metadata, delimiters, and encoding characters for the entire message. Every other segment follows the format: SEGMENT_ID|field1|field2|..., but MSH is special because its first field is the field separator itself.
MSH segment format:
MSH|^~&|sending_app|sending_facility|receiving_app|receiving_facility|timestamp|security|message_type|message_control_id|processing_id|version_id|sequence_number|continuation_pointer|accept_ack_type|app_ack_type|country_code|character_set|principal_language_of_message
Key MSH fields:
| Field | Name | Type | Description |
|---|---|---|---|
| MSH-0 | Segment ID | — | Always "MSH" (not counted in the field sequence) |
| MSH-1 | Field Separator | ST | The character separating fields (usually ` |
| MSH-2 | Encoding Characters | ST | The component, repetition, escape, and sub-component separators (^~&) |
| MSH-3 | Sending Application | HD | Application or system sending the message |
| MSH-4 | Sending Facility | HD | Facility or organization sending the message |
| MSH-5 | Receiving Application | HD | Application or system receiving the message |
| MSH-6 | Receiving Facility | HD | Facility or organization receiving the message |
| MSH-7 | Timestamp | TS | Date and time the message was created |
| MSH-8 | Security | ST | Security information (often empty) |
| MSH-9 | Message Type | MSG | Type and trigger event (e.g., ADT^A01) |
| MSH-10 | Message Control ID | ST | Unique identifier for this message |
| MSH-11 | Processing ID | PT | Test, Training, or Production (T, T, or P) |
| MSH-12 | Version ID | VID | HL7 version (e.g., 2.5.1) |
| MSH-13 | Sequence Number | NM | Message sequence number (for batches) |
| MSH-14 | Continuation Pointer | ST | Continuation token for multi-part messages |
| MSH-15 | Accept Acknowledgment Type | ID | Whether to request an ACK (AL, NE, SU, ER) |
| MSH-16 | Application Acknowledgment Type | ID | Whether to request an application ACK (AL, NE, SU, ER) |
| MSH-17 | Country Code | ID | Country of origin (ISO 3166-1 numeric) |
| MSH-18 | Character Set | ID | Character set in use (usually ASCII or UTF-8) |
| MSH-19 | Principal Language of Message | CWE | Language of the clinical content |
Encoding Characters
The MSH-2 field (encoding characters) defines the delimiter set used in the entire message. The standard value is ^~&, representing:
^— component separator~— repetition separator- `` — escape character
&— sub-component separator
This field appears immediately after MSH-1 (the field separator) with no field separator between them. An HL7 message always begins:
MSH|^~&|...
The escape character is crucial because it allows you to include the delimiter characters themselves in field values. When parsing an HL7 message:
- Read the first character after "MSH" to identify the field separator (usually
|) - Read the next four characters to identify the encoding characters
- Use these delimiters to parse the rest of the message
This self-identifying approach makes HL7 v2 robust: a parser can be configured dynamically based on what it reads, rather than requiring pre-configuration.
Annotated Sample Message
Here is a minimal but complete HL7 message with annotations showing the segment, field, component, and delimiter structure:
MSH|^~&|EHR|HOSPITAL|LAB|LABFACILITY|20260701120000||ADT^A01^ADT_A01|MSG001|P|2.5.1
PID||12345^MRN~67890^LAB|12345^^^NPI||DOE^JOHN^M^DR^|||19800515|M|||123 Main St^^Springfield^IL^62701^USA||555-1234|(555)555-5678||ENG|S|||PATIENT001|||||USA|
PV1||I|CARDIO^BED401||H|||123456^Smith^Mary^Dr^MD^|||||||||C||||||||20260701100000||||||||||||||||||||||||||||||||
Segment-by-segment breakdown:
MSH (Message Header):
MSH|^~&|EHR|HOSPITAL|LAB|LABFACILITY|20260701120000||ADT^A01^ADT_A01|MSG001|P|2.5.1
|
+-- MSH-1 (Field Separator): |
|
+-- MSH-2 (Encoding Characters): ^~&
|
+-- Component separator: ^
+-- Repetition separator: ~
+-- Escape character:
+-- Sub-component separator: &
|
+-- MSH-3 (Sending App): EHR
+-- MSH-4 (Sending Facility): HOSPITAL
+-- MSH-5 (Receiving App): LAB
+-- MSH-6 (Receiving Facility): LABFACILITY
+-- MSH-7 (Timestamp): 20260701120000
+-- MSH-8 (Security): [empty]
+-- MSH-9 (Message Type): ADT^A01^ADT_A01
|
+-- Component 1 (Message Code): ADT
+-- Component 2 (Trigger Event): A01
+-- Component 3 (Message Structure): ADT_A01
+-- MSH-10 (Message Control ID): MSG001
+-- MSH-11 (Processing ID): P
+-- MSH-12 (Version): 2.5.1
PID (Patient Identification):
PID||12345^MRN~67890^LAB|12345^^^NPI||DOE^JOHN^M^DR^|||19800515|M|||123 Main St^^Springfield^IL^62701^USA||555-1234|(555)555-5678||ENG|S|||PATIENT001|||||USA|
| | | | | | |
| | | | | | +-- Multiple empty fields (components/subcomponents)
| | | | | +-- PID-13 (Home Phone): 555-1234
| | | | +-- PID-11 (Address): 123 Main St^^Springfield^IL^62701^USA
| | | | (Component breakdown: street^apt^city^state^zip^country)
| | | +-- PID-8 (Administrative Sex): M
| | +-- PID-7 (Date of Birth): 19800515
| | |
| | +-- PID-5 (Patient Name): DOE^JOHN^M^DR^
| | (Component breakdown: family^given^middle^prefix^suffix)
| |
| +-- PID-3 (Patient Identifier List): 12345^MRN~67890^LAB~12345^^^NPI
| (Repetition breakdown:
| - 12345^MRN (ID 12345, type MRN)
| - ~ (repetition separator)
| - 67890^LAB (ID 67890, type LAB)
| - ~ (repetition separator)
| - 12345^^^NPI (ID 12345, empty check digit, empty assigning authority, type NPI))
|
+-- PID-1 (Set ID): [empty]
+-- PID-2: [empty]
...
PV1 (Patient Visit):
PV1||I|CARDIO^BED401||H|||123456^Smith^Mary^Dr^MD^|||||||||C||||||||20260701100000|...
| | | | | | | |
| | | | | | | +-- PV1-8 (Attending Doctor): 123456^Smith^Mary^Dr^MD^
| | | | | | +-- PV1-7: [empty]
| | | | | +-- PV1-6 (Prior Temporary Location): H
| | | | +-- PV1-5: [empty]
| | | +-- PV1-4 (Admission Type): [empty]
| | | (Note: address string with component separators)
| | +-- PV1-3 (Assigned Patient Location): CARDIO^BED401
| | (Component breakdown: unit^bed)
| +-- PV1-2 (Patient Class): I (Inpatient)
|
+-- PV1-1 (Set ID): [empty]
...
+-- PV1-19 (Visit Number): 20260701100000
Common Parsing Pitfalls
1. Forgetting the MSH Special Case
MSH is the only segment where the field separator immediately follows the segment ID without a field separator character between them. A parser that treats MSH like any other segment (expecting MSH| then reading the first field as empty) will misalign all subsequent fields. Always handle MSH specially: read the first field separator, then read the encoding characters, then use those to parse the rest.
2. Mishandling the Escape Character
The escape character (``) appears in field values, not just between fields. For example, the text Hello|World is encoded as HelloFWorld. A naive parser that splits on | without respect to escape sequences will break on the escaped pipe. Always process escape sequences during field parsing, not before tokenizing the message.
3. Ignoring Trailing Empty Components
In XPN (person name), trailing empty components are often included. For example:
DOE^JOHN^^DR^
Here, the empty third component represents an empty middle name, and the fourth component is the prefix. A parser that ignores trailing delimiters will lose the position of the suffix component. Preserve the component count.
4. Confusing Repetitions with Components
A field can repeat (separated by ~) and also have components (separated by ^). For example:
12345^MRN~67890^LAB
This is a repeating field with two occurrences, each with two components. A parser must first split by ~ to get repetitions, then split each repetition by ^ to get components. Reversing the order will produce incorrect results.
5. Not Respecting Optional Fields
Many fields are optional. An HL7 message may have consecutive field separators (e.g., ||) representing empty fields. A parser must distinguish between "field is present but empty" and "field is absent" by respecting the delimiter count, not by collapsing consecutive delimiters.
6. Assuming a Fixed Number of Fields
The HL7 standard defines a maximum field count per segment, but senders may include additional (often local) fields beyond the standard. A parser should gracefully ignore extra fields rather than failing. Conversely, a receiver should not assume that a segment is complete if a particular field is expected; always validate by checking field position, not by assuming a segment has a fixed number of fields.
7. Mishandling Sub-components
Sub-components are rare but important. The CX data type (composite ID) includes the assigning authority, which has sub-components for namespace ID, universal ID, and universal ID type. A field like:
12345&MRN&facility&2
requires splitting by & to extract the sub-components. A parser that only splits by ^ and ~ will treat the entire field as a single value.
Related pages
- HL7 Introduction: History, the v2 Family, and Why It Still Dominates
- HL7 v2 Versions: Timeline 2.1 to 2.8.2 and Which to Use
- MSH Segment: Message Header
- PID Segment: Patient Identification
- Data Types: XPN, CX, and Other Complex Types
