ADT (Admit, Discharge, Transfer) messages are the patient-movement backbone of hospital integration. Every registration, bed move, demographic update, and discharge generates an ADT event that pharmacy, lab, billing, and clinical decision support all depend on in real time. A production Mirth Connect ADT channel must process four core event types correctly: A01 (admit), A04 (register outpatient), A08 (update demographics), and A40 (merge patient records). This guide covers the channel architecture, Z-segment handling, fan-out routing, and A08 duplicate suppression. See our production HL7 interface engine guide for the broader deployment context.
ADT Event Types: What Each One Signals Downstream
A01 — Admit Inpatient: PV1.2 = 'I'. Most consequential ADT event — triggers pharmacy medication reconciliation, lab order activation, bed assignment, and inpatient billing simultaneously. A missed or misrouted A01 leaves downstream systems unaware of the admission, causing orders to fail. A04 — Register Outpatient: PV1.2 = 'O'. Same destination set as A01 but triggers outpatient billing codes and skips inpatient workflows. Emergency department visits also commonly use A04. A08 — Update Patient Info: Any change to demographics, insurance, or visit data. Typically 60–70% of total ADT volume — see HL7 backlog elimination for handling high-volume A08 streams. A40 — Merge Patient: MRG segment carries the retiring duplicate MRN; PID-3 carries the surviving MRN. Highest-risk event — mishandled merges create split records that mix two patients' clinical histories.
Channel Architecture: Single Source, Multiple Queued Destinations
The standard Mirth pattern is one MLLP source channel receiving all ADT events, with multiple destinations each filtered by event type. The source transformer extracts routing variables into channelMap; each destination filter checks the relevant values:
// Source transformer — extract all routing variables
var eventType = msg['MSH']['MSH.9']['MSG.2'].toString();
var mrn = msg['PID']['PID.3']['CX.1'].toString();
var lastName = msg['PID']['PID.5']['XPN.1']['FN.1'].toString();
var dob = msg['PID']['PID.7'].toString();
var patClass = msg['PV1']['PV1.2'].toString();
var location = msg['PV1']['PV1.3']['PL.1'].toString();
var facility = msg['MSH']['MSH.4'].toString();
channelMap.put('event_type', eventType);
channelMap.put('mrn', mrn);
channelMap.put('facility', facility);
// Destination filter — pharmacy sees only A01, A04, A11, A13
// var event = channelMap.get('event_type');
// return ['A01','A04','A11','A13'].indexOf(event) !== -1;Reading Z-Segments: Epic, Cerner, and Vendor Extensions
Z-segments carry proprietary fields not in the HL7 standard. Epic sends ZPI (patient info), ZIN (insurance), ZEP (Epic-specific). Mirth's E4X parser handles them transparently, but every Z-segment access needs a guard — not all ADT messages from a mixed environment send the same Z-segments:
// Guard — absent segment returns length 0
if (msg['ZPI'].length() > 0) {
var vipFlag = msg['ZPI']['ZPI.6'].toString();
var patientType = msg['ZPI']['ZPI.1'].toString();
channelMap.put('vip_flag', vipFlag);
}
if (msg['ZIN'].length() > 0) {
var primaryPayer = msg['ZIN']['ZIN.1']['CWE.2'].toString();
var memberID = msg['ZIN']['ZIN.4'].toString();
channelMap.put('member_id', memberID);
}Z-segment guards are what make ADT channels portable across vendors. Without them, a channel built for Epic will fail when it receives standard HL7 from a scheduling system. This is one of the most common issues in our Mirth integration failures guide.
A40 MRN Merge: High-Stakes Processing
if (channelMap.get('event_type') === 'A40') {
var survivingMRN = msg['PID']['PID.3']['CX.1'].toString();
var duplicateMRN = msg['MRG']['MRG.1']['CX.1'].toString();
if (!survivingMRN || !duplicateMRN)
throw new Error('A40 missing MRN: surviving=' + survivingMRN + ' retiring=' + duplicateMRN);
// Immutable audit log — must happen BEFORE routing
var audit = {
event: 'MRN_MERGE',
surviving: survivingMRN,
retired: duplicateMRN,
facility: channelMap.get('facility'),
timestamp: new Date().toISOString()
};
channelMap.put('merge_audit', JSON.stringify(audit));
logger.warn('MRN MERGE: retiring ' + duplicateMRN + ' into ' + survivingMRN);
}A08 Duplicate Suppression: Cut Volume by 60%
High-volume A08 streams often contain redundant updates — same fields, different timestamps. A fingerprint comparison suppresses up to 60% of these before they reach downstream systems:
if (channelMap.get('event_type') === 'A08') {
var fp = [
channelMap.get('mrn'),
msg['PID']['PID.5']['XPN.1']['FN.1'].toString(),
msg['PID']['PID.7'].toString(),
msg['PV1']['PV1.3']['PL.1'].toString(),
msg['ZIN'].length() > 0 ? msg['ZIN']['ZIN.4'].toString() : ''
].join('|');
var cached = globalMap.get('adtfp_' + channelMap.get('mrn'));
if (fp === cached) {
channelMap.put('suppress', 'true');
} else {
globalMap.put('adtfp_' + channelMap.get('mrn'), fp);
channelMap.put('suppress', 'false');
}
}For HA deployments, see our Mirth Connect HA guide. Monitor ADT channel performance with OpenTelemetry instrumentation.



