HL70103 is the small but consequential table that tells a receiver whether a message is real. It sits in MSH-11.1 Processing ID and answers a single binary-ish question: should the receiver write this to the production database, send it to a test sandbox, or treat it as a debugging artifact that should be logged but never persisted. Three codes — D, P, T — cover every v2 version from 2.1 through 2.8.1, unchanged across the entire history of the standard.
Purpose
HL70103 disambiguates traffic on shared interface infrastructure. In modern hospitals, the same MLLP listener often serves production, integration-test, and training environments behind a single endpoint, distinguished only by MSH-11. A receiver that respects HL70103 will route a T message to its training database, return an AA acknowledgment that does not trigger downstream production workflows, and never invoice a patient or order a medication based on a non-P message.
The field is one character in v2.5 and earlier (PT data type, processing ID only) and gains a second component in v2.6+ for the processing mode (archive, initial load, restore, current), but the first component — HL70103 — is the part this page covers.
Where it's used
- MSH-11.1 Processing ID — the canonical home of HL70103, populated on every v2 message ever sent. The field is required (R) on every version of MSH.
- BHS-11 / FHS-11 Batch and File Header processing IDs use the same code set when a batch envelopes multiple messages.
Code list
| Code | Display | Comment/Description |
|---|---|---|
| D | Debugging | Message is for development or troubleshooting; receiver should log and discard, never persist to clinical or financial systems. |
| P | Production | Message reflects a real-world event; receiver should process normally and update its system of record. |
| T | Training | Message belongs to a training environment; receiver should persist to training/sandbox data only, never to production. |
Code system OID
- OID:
2.16.840.1.113883.18.32 - Canonical URI:
http://terminology.hl7.org/CodeSystem/v2-0103
HL7-defined vs user-defined
HL70103 is HL7-defined and the code set is closed at three values. Sites occasionally invent local fourth values like S (Staging) or U (UAT), but these break conformance — every receiver is expected to interpret only D, P, T. Multi-environment routing is normally handled by separate MLLP endpoints or by MSH-3 / MSH-5 sending and receiving application identifiers, not by extending HL70103.
Version differences
- v2.1 – v2.5 — Single-component
PTdata type carrying just the processing ID (D,P,T). No further structure. - v2.6+ — MSH-11 retains HL70103 as MSH-11.1 but gains an optional MSH-11.2 Processing Mode (HL70207) with values like
A(Archive),R(Restore),I(Initial load),C(Current processing). HL70103 itself is unchanged. - v2.8 – v2.8.1 — Set stable; the three-code table has never been extended.
Common mistakes
- Emitting
Pfrom a non-production system. A test harness or staging environment that accidentally sendsPwill cause the receiver to write real records — phantom admissions, ghost orders, fake encounters. This is one of the most common root causes of "test data appeared in production"; the fix is to hard-wire test environments to emitTorDat the engine level and refuse to allow operators to override it. - Sending
Tand then expecting prod-grade audit. Receivers under HIPAA and similar regimes treatTandDmessages as out-of-scope for audit logging and chain-of-custody. If you need a fully audited training environment, you need a separate production-grade endpoint, not MSH-11 =T. - Using
Dfor "production debug" mode.Dmeans the message will not be persisted. Sites who flip MSH-11 toDto "get more logging" on production traffic end up with messages that downstream systems silently drop. Use a debug log channel, not MSH-11. - Leaving MSH-11 empty. MSH-11 is required on every version. An empty MSH-11 is a conformance error; receivers should reject the message with
AErather than guess. - Lowercase codes.
p,t,dare not valid; HL70103 is case-sensitive uppercase.
Examples
A production ADT admission:
MSH|^~&|EPIC|MGH|RHIO|STATE|20260625120000||ADT^A01^ADT_A01|MSG00001|P|2.5
PID|1||10456^^^MRN^MR||DOE^JANE^A||19850412|F
The same message from a training environment — note T in MSH-11:
MSH|^~&|EPIC-TRAIN|MGH|RHIO-TRAIN|STATE|20260625120000||ADT^A01^ADT_A01|MSG99001|T|2.5
PID|1||TRAIN001^^^MRN^MR||TESTPATIENT^TRAINING||19850412|F
A debug message during interface development — D tells the receiver not to persist:
MSH|^~&|DEV|MGH|TEST|MGH|20260625120000||ADT^A01^ADT_A01|MSG_DEBUG_001|D|2.5
PID|1||DEBUG1^^^MRN^MR||DEBUG^PATIENT||19850412|F
A v2.6+ message using both MSH-11 components — P processing ID, A archive mode:
MSH|^~&|EPIC|MGH|ARCH|MGH|20260625120000||ADT^A01^ADT_A01|MSG_ARCH_001|P^A|2.6
PID|1||10456^^^MRN^MR||DOE^JANE||19850412|F
FHIR projection of P as a Meta.tag on a transported resource:
{
"resourceType": "Patient",
"id": "10456",
"meta": {
"tag": [{
"system": "http://terminology.hl7.org/CodeSystem/v2-0103",
"code": "P",
"display": "Production"
}]
}
}
FHIR mapping
FHIR does not publish a ValueSet for HL70103 because the processing mode is metadata about the message envelope, not the resource. The v2-to-FHIR IG suggests one of two patterns:
- Bundle-level tag — apply a
Bundle.meta.tagto the entire converted Bundle withsystem = http://terminology.hl7.org/CodeSystem/v2-0103andcode = P|T|D. This preserves the original v2 semantics at the envelope level. - Resource-level tag — apply the same tag to each resource inside the Bundle so a non-production resource can be filtered out by every downstream consumer, not just the one that opens the Bundle.
Vorro emits both, since downstream FHIR consumers vary in how strictly they unwrap Bundles.
Engine considerations
- Default behavior on missing MSH-11. Engines should treat missing MSH-11 as a conformance error and reject with
AErather than default toP— defaulting to production is the worst possible failure mode. - Environment-locked emission. Test environments should be configured at the engine level to emit
T(orD) and to refuse to forward any outbound message with MSH-11 =P. This is a one-line config that has prevented countless "test data appeared in prod" incidents. - Routing on MSH-11. Some sites use a single MLLP port for production and test, dispatching internally on MSH-11. This works, but the recommended pattern is separate ports / separate endpoints — fewer ways to misroute.
- Case sensitivity. Codes are case-sensitive uppercase. Normalize on ingest if upstream senders are sloppy, and log every normalization so a sender emitting
pinstead ofPis identified and corrected.
How Vorro handles HL70103
Vorro validates MSH-11.1 against the three published HL70103 codes on every inbound message. Messages with an empty MSH-11 or an unknown code are rejected at the port boundary with AE rather than passed to downstream handlers, because the cost of guessing wrong (production data corruption, missed training-data quarantine) is asymmetrically high.
Vorro's outbound channels are environment-locked: a non-production deployment will refuse to emit P even if the upstream sender supplied it, and will rewrite MSH-11 to T with an audit log entry. This prevents the most common cross-environment leak.
