FHIR R4
Send FHIR R4 to EDFlow: batch Bundles, the $ingest operation for collection, searchset and Subscriptions Backport notifications, $process-message, how each resource maps to events, Epic and Oracle Health notes, and a worked example.
The FHIR adapter maps FHIR R4 resources to the same normalized events as HL7 v2, following the HL7 V2-to-FHIR concept maps where they apply, so HL7 and FHIR sites get the same behaviour. Raw resources are never stored.
Endpoints
| Endpoint | Accepts | Notes |
|---|---|---|
POST /v1/fhir | batch Bundles (up to 200 entries) | Each entry processed independently. transaction is refused (422 transaction_not_supported) |
POST /v1/fhir/$ingest | collection, searchset and history Bundles, including Subscriptions Backport notifications | Handshakes and heartbeats produce no events; heartbeats keep the feed healthy |
POST /v1/fhir/$process-message | message Bundles with a MessageHeader | Duplicates detected by Bundle.id + MessageHeader.id for 30 days |
GET /v1/fhir/metadata | CapabilityStatement | Public |
GET /v1/fhir/.well-known/smart-configuration | SMART Backend Services discovery | private_key_jwt only |
Accept: application/fhir+json returns a batch-response Bundle with an OperationOutcome per entry; Accept: application/json returns the normalized results. A transient failure returns 503 with Retry-After for the whole request; entries already committed return duplicate on retry.
printf '%s' '{"entry":[],"meta":{"tag":[{"code":"HTEST","system":"http://terminology.hl7.org/CodeSystem/v3-ActReason"}]},"resourceType":"Bundle","type":"batch"}' > body.jsoncurl -sS -X POST 'https://api.sandbox.edflow.space/v1/fhir' \ -H 'Content-Type: application/fhir+json' \ -H "Authorization: Bearer $EDFLOW_ACCESS_TOKEN" \ --data-binary @body.jsonSubscription rest-hooks
Backport rest-hook channels send static headers, so a 5-minute token doesn't fit. Use a static API key scoped to fhir:write only in Subscription.channel.header. In customer test and live it needs an IP allowlist and expires within 90 days; update the Subscription before it expires.
Sandbox test marker
In the sandbox, Bundles must carry the HTEST tag (http://terminology.hl7.org/CodeSystem/v3-ActReason#HTEST) in meta.tag; in live it's refused.
Resource mapping
| Resource | Condition | Events |
|---|---|---|
Encounter | class = EMER, or an ED location | First arrived/in-progress → encounter.arrived (period.start); a triaged status history entry → encounter.triaged; active location → encounter.location_changed; participant → careteam.snapshot; onleave → out of department; finished → encounter.departed with the discharge disposition; cancelled → encounter.cancelled |
Observation | Code is one of the profile's CTAS codes | encounter.triaged. Every other Observation is ignored |
ServiceRequest | Linked to an ED encounter; laboratory, imaging or ECG category | order.placed / order.status_changed: active → placed, on-hold → on hold, revoked/entered-in-error → cancelled. Consult categories → consult.status_changed |
Specimen | References a known order | Collected, received |
ImagingStudy | basedOn an order | Started → in progress; available → performed |
DiagnosticReport | basedOn an order | registered → in the lab; partial/preliminary → preliminary; final → resulted; amended/corrected/appended → corrected. result, conclusion, conclusionCode and presentedForm are dropped unread |
Appointment | basedOn an order | Scheduled, with start |
CareTeam | Encounter-level team (Oracle) | careteam.snapshot |
Task | focus is an order | Optional hints for in-progress and performed |
Practitioner, PractitionerRole, Location | Enrichment only |
Event ids are {sourceId}:fhir:{resourceType}/{id}/_history/{versionId or meta.lastUpdated}:{n}; recordedAt comes from meta.lastUpdated.
Vendor notes
Epic. The ED treatment team comes back as Encounter.participant, not CareTeam. Encounter search is scoped per patient in the public catalogue, and a general FHIR Subscription API isn't published, so most Epic sites send HL7 v2 (Bridges) for the real-time feed and use FHIR for enrichment or a SMART launch. Backend services use client_credentials with a private_key_jwt assertion signed with RS384 or ES384.
Oracle Health Millennium. CareTeam supports encounter-level teams with roles and periods, which fits the ED well. R4 Subscriptions aren't in the public documentation, so real-time events usually come from HL7 v2 outbound through Oracle's interface tooling.
Any vendor. A FHIR relay or subscription product in front of the EHR works with $ingest.
Worked example (synthetic, sandbox)
{
"resourceType": "Bundle",
"type": "batch",
"meta": { "tag": [{ "system": "http://terminology.hl7.org/CodeSystem/v3-ActReason", "code": "HTEST" }] },
"entry": [{
"resource": {
"resourceType": "Encounter", "id": "syn-enc-1", "meta": { "versionId": "1", "lastUpdated": "2026-09-14T12:02:14Z" },
"status": "arrived",
"class": { "system": "http://terminology.hl7.org/CodeSystem/v3-ActCode", "code": "EMER" },
"identifier": [{ "system": "urn:example:visit", "value": "SYN3000123" }],
"period": { "start": "2026-09-14T08:02:11-04:00" },
"location": [{ "location": { "reference": "Location/edwr", "display": "Waiting room" }, "status": "active" }]
},
"request": { "method": "PUT", "url": "Encounter/syn-enc-1" }
}]
}This produces encounter.arrived at 08:02:11 in the waiting room.
Related
Last updated on