The pipeline model
The five source stages and three per-destination hooks a message passes through, and what belongs in each one.
Last updated August 12, 2026
On this page
Every channel has the same shape. Understanding which stage does what is most of what it takes to write maintainable integrations here.
Source stages — once per inbound message
1. Preprocessor
Normalize the raw payload before anything structured happens: character-encoding fixes, line-ending normalization, stripping a transport envelope. Keep it mechanical — no business logic.
2. Validator
Assert the message is what you expect. A missing required segment or a malformed resource should be rejected at the edge with a clear reason, not discovered three stages later.
3. Filter
Decide whether this message belongs in this channel at all. Return false and processing stops cleanly — this is not an error path. Filtering on message type (ADT^A01 but not ADT^A31) belongs here.
4. Transformer
The main mapping. Reshape HL7 segments into FHIR resources, enrich from a lookup, restructure for downstream consumption. Most of the code in a channel lives here.
5. Postprocessor
Runs after the source pipeline completes. Bookkeeping, shaping the acknowledgement, side effects that should happen exactly once per inbound message regardless of how many destinations there are.
Per-destination hooks — once per destination
A channel with three destinations runs these three stages three times, independently.
Filter — should this particular destination receive this message? A cardiology system might only want messages for one department.
Transformer — shape the payload for this specific system. One destination gets a FHIR bundle, another gets a flat file, and neither has to compromise.
Response Transformer — interpret what the destination sent back. Some systems return HTTP 200 with an error inside the body; this is where you decide what counts as success.
Why the split matters
Putting per-destination logic in the source transformer is the most common habit carried over from older interface engines. It works until the second destination needs something different, and then the source transformer accumulates conditionals until nobody wants to touch it.
The rule of thumb: if the logic would change when you add a destination, it belongs in a destination hook.
Everything is TypeScript
All of these stages are TypeScript modules with type checking, npm type packages, and in-browser execution against sample messages. See Writing TypeScript transformers.