FHIR Source
Expose a FHIR-compliant REST endpoint or subscribe to FHIR Subscriptions for real-time clinical data exchange.
Overview
The FHIR source creates a FHIR-compliant HTTP server that receives FHIR resources via standard REST interactions (create, update) or through FHIR Subscription notifications. It supports both FHIR R4 and STU3 versions.
FHIR (Fast Healthcare Interoperability Resources) is the modern standard for healthcare data exchange. This source enables intu to receive data from EHR systems, health information exchanges, patient portals, and other FHIR-enabled applications using the native FHIR protocol.
subscription_type, intu registers as a FHIR Subscription endpoint and receives push notifications whenever matching resources are created or updated on the remote FHIR server.
Configuration
listener:
type: fhir
fhir:
port: 8090
base_path: /fhir
version: R4
subscription_type: rest-hook
tls:
cert_file: /etc/intu/certs/fhir.crt
key_file: /etc/intu/certs/fhir.key
auth:
type: oauth2
issuer: https://auth.hospital.example.com
audience: intu-fhir-endpoint
jwks_url: https://auth.hospital.example.com/.well-known/jwks.json
Properties
/fhir. Resource-specific routes are appended automatically (e.g. /fhir/Patient).R4 (default), STU3.rest-hook (HTTP callbacks), websocket. When omitted, the source operates as a standard FHIR REST endpoint.cert_file and key_file.bearer, oauth2, and smart (SMART on FHIR) types.Subscription Types
| Type | Description | Use Case |
|---|---|---|
rest-hook |
Receives HTTP POST notifications when subscribed resources change. | Real-time event-driven workflows where the FHIR server supports Subscriptions. |
websocket |
Maintains a persistent WebSocket connection for streaming notifications. | Low-latency scenarios requiring immediate notification delivery. |
Complete Example
A FHIR R4 endpoint that receives Patient resources via REST interactions and forwards them to an internal system after validation and transformation.
id: fhir-patient-ingest
enabled: true
listener:
type: fhir
fhir:
port: 8090
base_path: /fhir
version: R4
tls:
cert_file: /etc/intu/certs/fhir.crt
key_file: /etc/intu/certs/fhir.key
auth:
type: oauth2
issuer: https://auth.hospital.example.com
audience: intu-fhir-endpoint
jwks_url: https://auth.hospital.example.com/.well-known/jwks.json
validator:
runtime: node
entrypoint: validator.ts
transformer:
runtime: node
entrypoint: transformer.ts
destinations:
- master-patient-index
- audit-trail
TypeScript Transformer Example
This transformer validates an incoming FHIR Bundle, extracts Patient and related resources, and produces a normalized output for the master patient index.
import { Message, TransformResult } from "@intu/sdk";
interface FHIRResource {
resourceType: string;
id?: string;
[key: string]: unknown;
}
interface FHIRBundle {
resourceType: "Bundle";
type: string;
entry?: { resource: FHIRResource }[];
}
interface NormalizedPatient {
mrn: string;
source: string;
demographics: {
familyName: string;
givenNames: string[];
birthDate: string;
gender: string;
deceasedBoolean: boolean;
};
identifiers: { system: string; value: string }[];
contacts: { system: string; value: string; use: string }[];
addresses: {
use: string;
lines: string[];
city: string;
state: string;
postalCode: string;
country: string;
}[];
}
export default function transform(msg: Message): TransformResult {
let resource: FHIRResource | FHIRBundle;
try {
resource = JSON.parse(msg.body as string);
} catch {
return { success: false, error: "Invalid JSON in FHIR payload" };
}
const patients: FHIRResource[] = [];
if (resource.resourceType === "Bundle") {
const bundle = resource as FHIRBundle;
for (const entry of bundle.entry || []) {
if (entry.resource?.resourceType === "Patient") {
patients.push(entry.resource);
}
}
} else if (resource.resourceType === "Patient") {
patients.push(resource);
} else {
return { success: false, error: `Unsupported resource type: ${resource.resourceType}` };
}
if (patients.length === 0) {
return { success: false, error: "No Patient resources found" };
}
const normalized: NormalizedPatient[] = patients.map((pt) => {
const names = (pt.name as any[]) || [];
const official = names.find((n: any) => n.use === "official") || names[0] || {};
const identifiers = (pt.identifier as any[]) || [];
const telecoms = (pt.telecom as any[]) || [];
const addrs = (pt.address as any[]) || [];
const mrn = identifiers.find(
(id: any) =>
id.type?.coding?.some((c: any) => c.code === "MR")
);
return {
mrn: mrn?.value || pt.id || "",
source: msg.metadata?.source || "fhir-ingest",
demographics: {
familyName: official.family || "",
givenNames: official.given || [],
birthDate: (pt.birthDate as string) || "",
gender: (pt.gender as string) || "unknown",
deceasedBoolean: (pt.deceasedBoolean as boolean) || false,
},
identifiers: identifiers.map((id: any) => ({
system: id.system || "",
value: id.value || "",
})),
contacts: telecoms.map((t: any) => ({
system: t.system || "",
value: t.value || "",
use: t.use || "",
})),
addresses: addrs.map((a: any) => ({
use: a.use || "",
lines: a.line || [],
city: a.city || "",
state: a.state || "",
postalCode: a.postalCode || "",
country: a.country || "",
})),
};
});
return {
success: true,
body: JSON.stringify(normalized.length === 1 ? normalized[0] : normalized),
contentType: "application/json",
};
}