IntuMessage
Overview
Section titled “Overview”IntuMessage is the standard message envelope that flows through every pipeline stage. Regardless of the source protocol — HTTP, TCP, Kafka, file — the raw payload is wrapped in an IntuMessage before entering the pipeline. Transformers, validators, and filters all receive and return this object.
TypeScript Interface
Section titled “TypeScript Interface”interface IntuMessage { id: string; correlationId: string; channelId: string; body: any; contentType: string; transport: string; sourceCharset: string; timestamp: string; metadata: Record<string, string>; version: number;}Properties
Section titled “Properties”| Property | Type | Description |
|---|---|---|
id | string | Unique message identifier (UUID v7), assigned at ingestion |
correlationId | string | Groups related messages (e.g. a request and its response, or batch siblings) |
channelId | string | The channel that is processing this message |
body | any | The parsed message payload — an HL7v2 object, FHIR resource, JSON, XML DOM, etc. |
contentType | string | Format of the body (hl7v2, fhir+json, json, xml, x12, csv, etc.) |
transport | string | Source protocol: http, tcp, kafka, file, smtp, dicom, database, etc. |
sourceCharset | string | Character encoding of the original payload (e.g. UTF-8, ISO-8859-1) |
timestamp | string | ISO 8601 timestamp of when the message was received |
metadata | Record<string, string> | Transport-specific metadata (see below) |
version | number | Schema version for forward-compatible evolution of the envelope format |
Transport-Specific Metadata
Section titled “Transport-Specific Metadata”The metadata map is populated with keys that depend on the source transport. This provides access to protocol-level details without polluting the core interface.
| Key | Example |
|---|---|
http.method | POST |
http.path | /api/hl7/adt |
http.query | facility=MAIN&format=json |
http.remoteAddr | 10.0.1.42:54321 |
http.header.<name> | http.header.Content-Type → application/json |
| Key | Example |
|---|---|
kafka.topic | hl7.adt.inbound |
kafka.partition | 3 |
kafka.offset | 284710 |
kafka.key | PATIENT-12345 |
kafka.header.<name> | kafka.header.source → epic |
| Key | Example |
|---|---|
tcp.remoteAddr | 10.0.1.42:54321 |
tcp.localAddr | 0.0.0.0:6661 |
| Key | Example |
|---|---|
file.path | /data/incoming/ADT_A01.hl7 |
file.name | ADT_A01.hl7 |
file.size | 2048 |
file.modTime | 2026-03-15T08:30:00Z |
| Key | Example |
|---|---|
smtp.from | lab@hospital.org |
smtp.to | results@clinic.com |
smtp.subject | Lab Results - Patient 12345 |
smtp.date | 2026-03-15T08:30:00Z |
| Key | Example |
|---|---|
dicom.callingAE | PACS_MAIN |
dicom.calledAE | INTU_ROUTER |
dicom.sopClassUID | 1.2.840.10008.5.1.4.1.1.2 |
Database
Section titled “Database”| Key | Example |
|---|---|
database.driver | postgres |
database.table | outbound_queue |
database.columns | id,patient_id,message_body,created_at |
.intuJSON Format
Section titled “.intuJSON Format”When a message is persisted to storage, exported, or queued for replay, it is serialized as .intuJSON — the canonical envelope format:
{ "version": 1, "id": "019505a1-7c44-7f8e-b4a2-3c8d1e9f0a6b", "correlationId": "019505a1-7c44-7f8e-b4a2-3c8d1e9f0a6b", "channelId": "adt-to-fhir", "contentType": "hl7v2", "transport": "tcp", "sourceCharset": "UTF-8", "timestamp": "2026-03-15T14:23:01.847Z", "metadata": { "tcp.remoteAddr": "10.0.1.42:54321", "tcp.localAddr": "0.0.0.0:6661" }, "body": { "MSH": { "1": "|", "2": "^~\\&", "3": "EPIC", "4": "MAIN_CAMPUS", "5": "INTU", "6": "ROUTER", "7": "20260315142301", "9": { "1": "ADT", "2": "A01" }, "10": "MSG-00042", "11": { "1": "P" }, "12": "2.5.1" }, "PID": { "3": [{ "1": "12345", "5": "MR" }], "5": [{ "1": "DOE", "2": "JOHN" }], "7": "19850315", "8": "M" }, "PV1": { "2": "I", "3": { "1": "4N", "2": "412", "3": "A" }, "19": "V00098765" } }}The version field enables schema evolution. Consumers should check the version and handle unknown fields gracefully to maintain forward compatibility.
IntuContext
Section titled “IntuContext”Every TypeScript function receives an IntuContext alongside the message. It provides access to runtime services and scoped state:
interface IntuContext { channelId: string; messageId: string; destinationName: string; globalMap: Map<string, unknown>; channelMap: Map<string, unknown>; connectorMap: Map<string, unknown>; responseMap: Map<string, unknown>; logger: Logger;}| Property | Description |
|---|---|
channelId | ID of the channel processing this message |
messageId | Same as IntuMessage.id — the unique message identifier |
destinationName | Name of the current destination (only set in destination-scoped stages) |
globalMap | Shared across all channels for the lifetime of the engine |
channelMap | Scoped to the current channel |
connectorMap | Scoped to the current connector instance |
responseMap | Scoped to the current message’s request-response lifecycle |
logger | Structured logger with channel and message context pre-attached |
Accessing Message Data in Transformers
Section titled “Accessing Message Data in Transformers”Reading HL7v2 fields
Section titled “Reading HL7v2 fields”export default function transform(msg: IntuMessage, ctx: IntuContext) { const messageType = msg.body.MSH[9][1]; // "ADT" const patientId = msg.body.PID[3][0][1]; // "12345" const lastName = msg.body.PID[5][0][1]; // "DOE"
ctx.logger.info("Processing admission", { messageType, patientId }); return msg;}Building a FHIR resource from HL7v2
Section titled “Building a FHIR resource from HL7v2”import { v4 as uuid } from "uuid";
export default function transform(msg: IntuMessage, ctx: IntuContext) { const pid = msg.body.PID;
msg.body = { resourceType: "Patient", id: uuid(), identifier: [ { system: "http://hospital.org/mrn", value: pid[3][0][1], }, ], name: [ { family: pid[5][0][1], given: [pid[5][0][2]], }, ], gender: pid[8] === "M" ? "male" : pid[8] === "F" ? "female" : "unknown", birthDate: `${pid[7].slice(0, 4)}-${pid[7].slice(4, 6)}-${pid[7].slice(6, 8)}`, };
msg.contentType = "fhir+json"; return msg;}Using metadata and context maps
Section titled “Using metadata and context maps”export default function transform(msg: IntuMessage, ctx: IntuContext) { const sourceIp = msg.metadata["tcp.remoteAddr"]; const facilityMap = ctx.globalMap.get("facilities") as Map<string, string>;
if (facilityMap?.has(msg.body.MSH[4])) { msg.metadata["facility.name"] = facilityMap.get(msg.body.MSH[4])!; }
ctx.responseMap.set("accepted", true); return msg;}