A route can take messages from several sources and deliver them to several destinations, in any mix of types: MLLP, HTTP(S) REST and SOAP, Service Bus and SFTP. Use it when one feed goes to several systems, or several senders feed the same systems.
[[route]]
name = "adt-feed"
mode = "store_and_forward"
on_unmatched = "accept" # no filter matches: "accept" (AA, counted) or "reject" (AR)
sources = [
{ type = "mllp", listen = "10.10.10.10:7890" },
{ type = "http", listen = "0.0.0.0:8443", path = "/hl7/adt", tls = { … }, auth = { type = "api_key", keys = { … } } },
]
[[route.destinations]]
name = "lab" # required with several destinations
type = "http"
url = "https://lab.example.org/hl7/admissions"
filter = { message_type = ["ADT^A01", "ADT^A04", "ADT^A08"], fields = { "PV1-2" = ["I", "E"] } }
[[route.destinations]]
name = "his"
type = "mllp"
address = "his.hospital.local:6661"
store_and_forward = { on_reject = "dead_letter" } # this destination's own settings
[[route.destinations]]
name = "archive"
type = "sftp"
host = "sftp.archive.local"
username = "hl7"
private_key = "keys/archive"
host_key = "SHA256:…"
directory = "inbox"
batch_format = "hl7_batch"Sources
Every source feeds the route's queues, in the order messages arrive, whichever source they came by. Each source answers its own sender with the ACK, as on a route with one source. Listening sources each take their own address, and HTTP sources can still share a port by path.
Destinations
- A queue each. Every destination has a queue of its own, so one that is down or blocked holds up no other. The sender gets AA once the message is stored for every destination it goes to.
- Their own settings. Retries,
on_rejectandmax_attemptscome from the route'sstore_and_forward, or from the destination's own, which replaces them for that destination. Dead letters, pause and Retry now are per destination too. - Names. With several destinations each needs a
name(letters, digits,-and_): the console, the logs, the metrics and the admin API use it. - Upgrading. A route with one source and one destination works as before, and keeps its queue.
Filters
A destination with a filter gets only the messages the filter matches; without one it gets every message. A filter names MSH fields and segment field paths, each with the values it accepts:
filter = { message_type = ["ADT"] } # any ADT
filter = { message_type = ["ADT^A01", "ADT^A08"] } # these events
filter = { message_type = ["ORU^R01"], sending_facility = ["NORTH*"] }
filter = { fields = { "PV1-2" = ["I", "E"] } } # inpatient or emergency
filter = { fields = { "OBR-4.1" = ["GLU", "K", "NA"] } } # any OBR with one of these tests
filter = { fields = { "PID-3.4.1" = ["HOSP"], "ZPI-1" = ["*"] } } # assigning authority, and a ZPI segment| Field | Matches |
|---|---|
message_type | MSH-9, by component: ADT is any ADT, ADT^A08 that trigger event. The message structure in MSH-9.3 isn't compared. |
sending_application, sending_facility, receiving_application, receiving_facility, processing_id | MSH-3, 4, 5, 6 and 11, by their first component: MAIN^1.2.3^ISO matches MAIN. |
fields = { "SEG-n.c.s" = [ … ] } | Any segment field, component or subcomponent: PV1-2, OBR-4.1, PID-3.4.1, Z-segments too. It matches if any occurrence of the segment, and any repetition of the field, has a matching value; OBR[2]-4.1 and PID-3[2].1 pick one. |
- And, or: the values of one field are alternatives (OR); every field the filter names must match (AND).
- Values compare without regard to case, with
*and?wildcards. An empty or missing field has the value""; a segment that isn't in the message never matches. - No match: when no destination takes a message,
on_unmatcheddecides:"accept"(the default) acknowledges it and counts it as unmatched;"reject"answers AR.
Any and not
For an OR across different fields, list whole conditions under any: at least one of them must match. To exclude messages, put a condition under not: a message that matches it is left out. Each condition is a filter of its own, so it can have its own any and not, and both join the filter's other fields with AND. A filter can be just a not: every message except those.
# NOT: every ADT except A03
filter = { message_type = ["ADT"], not = { message_type = ["ADT^A03"] } }
# OR across fields: an A08, or anything from NORTH
filter = { any = [ { message_type = ["ADT^A08"] }, { sending_facility = ["NORTH"] } ] }
# All together, as a table of its own:
# (an ADT) AND (an emergency patient OR an inpatient on ward 4W) AND NOT (a test message)
[route.destinations.filter]
message_type = ["ADT"]
any = [
{ fields = { "PV1-2" = ["E"] } },
{ fields = { "PV1-2" = ["I"], "PV1-3.1" = ["4W"] } },
]
not = { processing_id = ["T"] }The full syntax, recipes, and how to check a filter against sample messages: Filters.
Pass-through with several destinations
Mark one destination primary = true. It is delivered while the sender waits, and its answer is the ACK, as on a route with one destination. The other destinations get a copy of what the primary accepted (AA), through queues of their own; a message the primary rejects isn't copied. The primary gets every message, so it can't have a filter. See the Results to the EHR, alerts by order example.
[[route]]
name = "orders"
mode = "passthrough"
source = { type = "mllp", listen = "10.10.10.10:7891" }
[[route.destinations]]
name = "meditech"
type = "mllp"
address = "meditech.hospital.local:6661"
primary = true # its ACK goes back to the sender
[[route.destinations]]
name = "audit"
type = "http"
url = "https://audit.example.org/hl7" # gets a copy of what MEDITECH acceptedIn the console, the API and the metrics
The console lists a route's sources, and a row for each destination with its filter, state, queue, dead letters and last error; select a destination to see its queue. The admin API takes ?destination=<name> on the message, pause, resume and retry-now calls; without it, pause, resume and retry-now apply to every destination of the route. Queue metrics carry a destination label, and hl7edge_unmatched_total counts messages no filter took. In the Helm chart, give each source its own port. See the One ADT feed to three systems example.