Mirth Connect channel architecture: source, filter, transformer, destination
How to structure NextGen Connect (formerly Mirth Connect) channels — source, filter, transformer, destination, configuration maps, error queues versus retry, and Git export of channel XML.
Ala Ben Aicha

Direct answer
A NextGen Connect (Mirth Connect) channel is source, filter, transformer, destination. Put environment URLs in the configuration map, not the channel XML. Error queues are not retries. Export channels to Git.
Channel mapping
Mirth Connect is the product name most interface engineers still search. The vendor name is NextGen Connect (Mirth Connect by NextGen Healthcare). On 19 March 2025, with version 4.6, NextGen moved new releases to a commercial licence; source for 4.6+ is not on GitHub. Older source, upgrade notes and the FAQ remain. This page is channel architecture, not a licence opinion.
A channel is one pipeline:
Source connector
→ source filter (accept / drop)
→ source transformer (normalize)
→ destination 1..n
→ destination filter
→ destination transformer
→ send + response
→ postprocessor
| Stage | What it does | What it must not do |
|---|---|---|
| Source | Receive or poll: TCP/MLLP listener, HTTP, file, database, JMS, channel reader | Encode the whole mapping; that belongs in transformers |
| Source filter | Boolean: process this message at all (MSH-9, sending facility, basic structure) | Mutate msg; filters that change data hide failures |
| Source transformer | Normalize: namespaces, dates, identifier systems, FHIR JSON | Call production HTTP without a destination queue in front |
| Destination filter | Route subsets (A01 to registration, A08 to demographics, ORU to the lab channel) | Duplicate the source filter without a reason |
| Destination transformer | Build the outbound payload for that system | Hard-code hostnames, ports, or secrets |
| Destination | Deliver (MLLP, HTTP FHIR, file, channel writer) | Be the only copy of environment configuration |
Maps (from the NextGen variable-map list):
| Map | JS / shortcut | Lifetime |
|---|---|---|
| Connector | connectorMap / $co |
This connector, this message |
| Channel | channelMap / $c |
This channel, this message |
| Source | sourceMap / $s |
From the source connector |
| Global channel | globalChannelMap / $gc |
This channel, process lifetime |
| Global | globalMap / $g |
Server process |
| Configuration | configurationMap / $cfg |
File on disk (configuration.properties under appdata), not the in-memory global map |
The configuration map is the environment file: FHIR bases, MLLP hosts, path prefixes. NextGen stores it as a flat file and does not include it in server configuration exports, so the same channel XML can move across test and production. That is the point.
Hybrid v2 plus FHIR still sits on this pipeline — the v2-to-FHIR migration playbook. Order/result identifiers belong in radiology and lab workflows, not in a second copy of those maps inside every transformer.
Identifier traps
| Trap | Symptom | Fix |
|---|---|---|
| Hostnames in transformer JavaScript | Channel XML differs per environment; Git diffs are noise | $cfg('fhir.base') / $cfg('his.mllp.host') in connector properties |
Config map keys colliding with globalChannelMap |
Wrong URL because lookup precedence hits $gc first |
Do not reuse config-map names in other maps |
| Source filter too tight | A08 never updates FHIR Patient | Filter on message family (ADT) at source; split A01/A08 at destination |
| One channel for ADT + ORM + ORU | Shared transformer with a forest of if (msg['MSH']…) |
One channel per inbound contract, or a thin source that channel-writes to specialists |
| Treating ERROR status as “will retry” | Silent loss after max retries | See queues below |
| Exporting channels without code templates | Import misses shared functions | Export libraries with the channel, or version templates in the same Git repo |
HL7 field indexing in Rhino is 1-based (msg['PID']['PID.3']['PID.3.1']). Empty fields throw if you call .toString() without a guard — that is a transformer bug, not a “bad ADT”.
Test fixtures
De-identified. Channel XML is huge; keep fixtures as messages + expected outbound, not as screenshots of the administrator.
Inbound ADT (same Jane Example as the ADT article):
MSH|^~\&|HIS|HOSP-A|MIRTH|HOSP-A|20260917103000||ADT^A01^ADT_A01|MSG0001|P|2.5
PID|1||HOSP-MRN-0001^^^HOSP-A^MR||EXAMPLE^JANE^Q||19770412|F
PV1|1|I|||||||||||||||||VN-2026-00042^^^HOSP-A^VN|||||||||||||||||||||||||20260917103000
Configuration map (never commit real secrets; this is a shape):
his.mllp.host=his-test.example.org
his.mllp.port=2575
fhir.base=https://fhir-test.example.org/fhir
fhir.patient.mrn.system=https://hosp-a.example.org/mrn
Destination HTTP body (truncated Patient):
{
"resourceType": "Patient",
"identifier": [
{
"system": "https://hosp-a.example.org/mrn",
"value": "HOSP-MRN-0001"
}
],
"name": [{ "family": "EXAMPLE", "given": ["JANE", "Q"] }],
"birthDate": "1977-04-12"
}
Minimum tests:
- Source filter: ADT^A01 accepted; ORM dropped (or routed elsewhere).
- Transformer guard: PID-3 empty → filtered or ERROR with a reason, not a
TypeError. - Config map: same channel XML against test and production bases.
- Destination queue: stop the FHIR server, send A01, message QUEUED, server returns, message SENT once.
- Git: export channel XML, change one transformer line, diff shows that line — not a 4,000-line property reorder.
Never paste live PHI into channel descriptions, test messages stored in Git, or the administrator’s “Generate” pane on a production box.
Failure modes and rollback
Destination queues are not the error table.
| Mode | Behaviour | Use |
|---|---|---|
| Never | Failed send fails the destination immediately | Only if a later destination must see a synchronous response |
| On failure | Try once, then queue | Default for FHIR HTTP and MLLP toward brittle receivers |
| Always | Queue first, send async; downstream sees QUEUED | High volume; accept out-of-order if you also enable rotate |
ERROR means the message is not going to retry by itself. Operators reprocess, or you route failures to an error channel (channel writer) that pages a human. Retry is the destination queue. Mixing the two is how ADT A03 sits behind a poison A08 until census is fiction.
Git export: channels live as XML in the engine database. There is no native “the repo is the runtime”. Practical pattern: REST export each channel to one file ({id}-{name}.xml), pretty-print for diffs, pull request, import + deploy on the target. Do not store configuration.properties in the same repo as production secrets; store keys and inject values per environment.
| Failure | Detection | Rollback |
|---|---|---|
| Transformer exception | Connector ERROR, MLLP still ACKed if you ACK’d at source too early | ACK after destination success, or ACK at source and accept that you now own replay |
| Queue backup | QUEUED count climbing | Pause source; fix receiver; do not halt-and-wipe the queue |
| Wrong environment | Test Patient on production FHIR | Config map + separate client IDs; disable the production destination in lower environments |
| Loop via channel writer | CPU cliff | Source map of MSH-3; drop messages this engine just emitted |
Disable a bad writer by undeploying that destination (or the channel) without touching the HIS feed. Keep the inbound MLLP up if clinical senders cannot buffer.
Related service
If you need NextGen Connect / Mirth channels designed, exported, and tested (ADT, ORM/ORU, FHIR HTTP), that is Mirth Connect. For a scoped channel-architecture spike, use contact with project intent.
This page is not a NextGen licence, not Channel History as a substitute for Git, and not a throughput benchmark.