Every HL7 interface requires a test suite involving: a parser, an integration engine, a destination system, or a unit test harness. Each test requires a message. Writing one by hand is fraught with difficulty: forgetting encoding characters in MSH-2, miscounting pipe delimiters, confusing MSH-5 with MSH-3, or omitting a mandatory segment can lead to silent rejections at the receiving end. This guide covers the anatomy of a valid HL7 v2.x message, the most common trigger events you will need to test, and how to generate safe synthetic data without the risk of exposing PHI.
Anatomy of a Valid HL7 Message
An HL7 v2.x message is a pipe-delimited plain-text document. The first segment is always MSH (Message Header), with its first two fields being somewhat peculiar: MSH-1 is the field separator itself (|), and MSH-2 declares the remaining encoding characters—component (^), repetition (~), escape (\), and subcomponent (&). Every standard message begins exactly with MSH|^~\&|. Without this, no downstream parser will be able to understand the rest.
Following the MSH segment, specific segments associated with the trigger event carry the actual content. An ADT^A01 (admission) message requires EVN, PID, and PV1 segments at a minimum, while an ORU^R01 (laboratory result) message requires PID, OBR, and OBX segments. Each segment follows a strict field order defined by the HL7 specification, with each field having a specific data type and cardinality (mandatory, optional, or repeatable). The receiving parser validates these messages, rejecting any that deviate from the established structure.
The Test-Data Problem
Production clinical messages commonly contain Protected Health Information (PHI), including patient names, medical record numbers, dates of birth, physician identifiers, and institutional addresses. HIPAA in the United States, along with equivalent data-protection regimes elsewhere, prohibits sharing PHI outside the authorized environment. Therefore, the obvious testing shortcut—grabbing a production message—is not a safe option. Integration engineers can write messages manually from the specification, use an anonymizer while accepting residual risk, copy templates from vendor documentation, or use a generator that produces structurally valid synthetic messages.
Synthetic Data Strategies
Good synthetic data should meet three principles: (1) it is clearly fictitious, (2) it has a realistic structure, and (3) it is deterministic when reproducibility is required. “Clearly fictitious” means using a name such as TEST^PATIENT^A, a birth date such as 19000101, and a facility named SENDING_APP. Anyone looking at a message with those values can immediately identify it as test data, with no risk of confusing it with production data.
"Realistic structure" means that field lengths, cardinality, and data types match those of a real message. A PID-3 field must follow a valid MRN format—such as TEST000001^^^TESTFAC^MR—rather than something like abc. An OBX-5 value must be a plausible number with plausible units. The receiving system's validators enforce strict typing, even when the values are obviously fake.
Determinism is crucial for regression tests. If the generator produces different values on each run, assertions about specific fields will fail. The generator on this site uses fixed sample values so that you can commit a generated message to a fixture and obtain the same result the following week.
The Most Common Trigger Events to Test
ADT (Admit, Discharge, Transfer)
The ADT family is the most common reason for an interface to exist. Virtually all clinical and administrative systems rely on ADT channels and messages to keep their own patient records synchronized. Test, at a minimum:
- ADT^A01 — Admit. New inpatient visit. Ensures new-patient registration and visit creation flows work end-to-end.
- ADT^A03 — Discharge. Ensures discharge cleanup, billing handoff, and bed-management updates trigger correctly.
- ADT^A04 — Register outpatient. Validates the outpatient registration path, which often has slightly different downstream routing than inpatient.
- ADT^A08 — Update patient info. The workhorse event. Any demographic correction, insurance change, or visit-detail update flows as an A08. It is also the most likely source of downstream bugs because downstream systems must reconcile updates against existing state.
- ADT^A11 — Cancel admit. Ensures prior-admit cancellations propagate; a common source of bed-management and billing discrepancies when mishandled.
Orders and Results (ORM, ORU)
ORM^O01 transmits new requests — laboratory, imaging, pharmacy — to the executing system, and ORU^R01 returns results. The tests must exercise:
Single-OBX observation (single numeric result).
Multi-OBX observation (a laboratory panel with 5–15 individual measurements).
Abnormal flags (OBX-8) and result statuses (OBX-11: P, F, C for preliminary, final, corrected).
Text-format results (OBX-2 = 'TX') for radiology and pathology reports.
Scheduling, Documents, Finance, Immunizations
SIU^S12 tests appointment creation—a common source of calendar synchronization bugs. MDM^T02 tests clinical document transmission; BAR^P01 and DFT^P03 handle the financial pipeline; and VXU^V04 covers immunization reporting (already mandatory in several countries as a public health feed). A generator that supports all of these via a single interface accelerates the quality assurance (QA) process for teams working across multiple domains.
Setting Processing ID Correctly
MSH-11, the Processing ID, tells processing systems whether the message is for production, pre-production/training, or development/testing. Valid values are P (production), T (training), and D (debug). When injecting synthetic messages into a shared testing environment, always use T or D. Almost all processing systems have routing filters that ignore non-P traffic for billing, prescribing, and reporting purposes; this is the mechanism that prevents a test lab result from appearing in a real patient's medical history.
This site's generator uses P by default because the message is intended as a structural reference. Change it to T before sending it to a real integration engine.
Round-Tripping Through Parser and ACK
A generated message that appears correct on screen may still fail within a processing system. The most cost-effective validation method is to perform a "round-trip" process on the message using a local processor—ideally, the same library used by your production system. Here, the HL7 Viewer processes and highlights messages segment by segment, detecting structural issues such as missing mandatory fields or incorrect data types. The ACK Generator simulates the receiving system's response, exercising the acknowledgment workflow that is often overlooked during testing.
Common Pitfalls
Field separators in data. A name containing an apostrophe or an ampersand will break the message structure unless escaped using
\F\,\S\, etc. Most generators handle this automatically; always test using at least one name that includes a special (escapable) character.Time zones in MSH-7. The specification allows for time zones, but support varies across systems. For maximum compatibility, use UTC without a time zone offset (e.g.,
20260420120000).Missing components in PID-5. A name lacking at least the surname and first name components (e.g.,
SMITH^JOHN) may fail on some receiving systems, even though the specification permits single-component names.Z-segments. Proprietary segments (prefixed with "Z") are permitted by the specification but are not standardized. Do not assume a receiving system will understand them; if your integration relies on one, test it explicitly.
Character encoding. HL7 v2 defaults to ASCII; while UTF-8 is becoming increasingly common, it must be declared in MSH-18. Accented characters in PID-5 may appear correct on your screen but arrive corrupted if the encoding has not been properly negotiated.
Using the Generator
Open the HL7 Message Generator, select a message type, click 'Fill with sample data' to populate mandatory fields with obviously fictitious values, and finally, customize it to meet your system's or test's requirements. The generated message updates in real time as you type. Copy or download it for your end-to-end test, or click 'Open in HL7 Viewer' to inspect it segment by segment before sending.
Every generated message parses cleanly through our HL7 parser — we verify this in the test suite — so the message you copy is guaranteed to be structurally valid. Combine it with the HL7 Viewer and ACK Generator and you have a complete loop for testing either side of an interface without touching real patient data.
Scaling Up: Automated Test Data Generation
For most automated interface testing workflows, a manually curated subset of synthetic messages suffices. However, when building automated regression tests for a large integration engine or certifying a new EHR connector, you might need hundreds or thousands of messages featuring diverse patient profiles, boundary-case field values, and a mix of message types. A highly effective pattern for this is to use this generator as a template source, then automate it with a short script (10–20 lines of Python or Node) that swaps in field values from a CSV or JSON dataset. This yields deterministic, version-controlled test data that preserves the generator's structural integrity, all without the need to manually edit hundreds of files. Common parameterizations include patient name pools (e.g., TEST^SMITH, TEST^JONES, TEST^GARCIA), realistic yet synthetic date ranges, and rotating MRN patterns designed to exercise the formats your integration expects from various source systems.
Compliance Context: HIPAA Safe Harbor and Synthetic Data
The HIPAA Safe Harbor method identifies 18 categories of identifiers that must be removed for health information to be considered de-identified under that method. Synthetic test data avoids this issue because it never belonged to a real person: no identifiers need to be redacted, no IRB approval is needed for ordinary internal system testing, and no Business Associate Agreement is required solely to protect nonexistent patient data. Integration teams therefore often prefer synthetic generators to de-identified production extracts: the legal and procedural burden is lower, and test coverage can improve because teams can introduce edge cases that may not exist in production.
Debugging Tips When a Generated Message Fails Downstream
Occasionally, a generated message passes our parser but fails in an integration engine or vendor system. The most common causes are:
- The receiver expects a specific Processing ID (MSH-11): change P to T or D.
- The receiver expects a specific Sending Facility format (MSH-4): replace
SENDING_FACwith an identifier recognized by its configuration. - The receiver expects specific table values; for example, Patient Class in PV1-2 must be I, O, E, P, R, or B according to HL7 Table 0004, not a custom string.
- The receiver requires a specific version in MSH-12.
- A vendor-specific Z-segment is missing. For case 5, consult the vendor’s integration specification and add the Z-segment manually before sending the message.