FHIR messaging is the part of HL7 FHIR that sends resources as an event-driven message, much like an HL7 v2 message, instead of reading and writing them one at a time through REST. A sender packages an event and its data into one Bundle, and the receiver processes it as a unit and replies.
This guide explains how a message Bundle is built, what the MessageHeader carries, how responses, retries and duplicates work, and when messaging is a better fit than REST or subscriptions.
What is FHIR messaging?
Designing a FHIR exchange between systems?
Agnotic builds FHIR APIs, messaging endpoints and SMART on FHIR apps for health systems and digital health companies.
FHIR messaging is an exchange pattern in which a sender transmits a Bundle of type message, whose first entry is a MessageHeader naming the event, sender, destination and focus resources. The receiver processes the whole Bundle according to that event and returns a response message reporting success or failure.
The pattern exists because some exchanges are events, not resource updates. A lab order, a notification that a patient was admitted or a request for a claim decision each means "something happened, act on it", and several resources travel together to describe it.
How does a message Bundle differ from a REST call?
A REST call acts on one resource at a known URL and gets an HTTP status back. A message Bundle carries an event and all its resources to one endpoint, and the receiver decides what to create, update or ignore based on the event code.
- Unit of work: REST works on a single resource per call, or a set of independent calls in a transaction Bundle. Messaging works on one event with all its resources.
- Addressing: REST targets resource URLs such as Patient or Observation. Messaging posts everything to a single $process-message endpoint.
- Meaning: In REST, the HTTP verb says what to do. In messaging, the MessageHeader event says what happened, and the receiver's rules decide the action.
- Response: REST returns an HTTP status and possibly an OperationOutcome. Messaging returns a response message whose MessageHeader reports the outcome.
- State: REST assumes the server is the source of truth for the resource. Messaging assumes the receiver keeps its own data and applies events to it.
- Routing: Messages can pass through intermediaries that route them by event, as interface engines do with HL7 v2.
Teams coming from HL7 v2 will recognize the shape. The MessageHeader plays the role of the MSH segment, the event code replaces the trigger event, and the response message replaces the ACK. The difference is that the payload is FHIR resources rather than pipe-delimited segments.
What goes in the MessageHeader?
The MessageHeader carries the message's identity, event, parties and focus. In FHIR R4, version 4.0.1, it must be the first entry in a Bundle of type message, and the receiver reads it before anything else.
- event: A coding or URI naming the event, such as an admit notification or an order request, from a code system both sides agree on.
- source: The sending system's name, software and endpoint, which the receiver uses for routing replies and for audit.
- destination: One or more receivers, each with an endpoint and optionally the intended recipient organization or practitioner.
- sender and author: The organization or person responsible for the message and the one who created its content.
- focus: References to the resources that are the subject of the event, such as the ServiceRequest in an order.
- response: Present only in replies, with the identifier of the original message and a code for the outcome.
- definition: An optional link to a MessageDefinition that formally describes the event and its allowed content.
FHIR itself defines no message events. Each exchange agrees its own event codes, ideally documented in MessageDefinition resources or an implementation guide, so both sides know which resources each event must carry.
How do you build a FHIR messaging exchange step by step?
A FHIR messaging exchange follows the same stages each time, and they contrast with a plain REST call at every step. In REST, the client sends one resource to its URL and reads an HTTP status. In messaging, the stages below run from event to response.
1. Event: Something happens in the sending system, such as an order being signed, and the sender maps it to an agreed event code.
2. Build the Bundle: The sender creates a Bundle of type message with a new Bundle id. The MessageHeader goes first, with a unique id, the event, source, destination and focus references.
3. Add focus resources: The resources the event is about, such as Patient, ServiceRequest and Encounter, follow the MessageHeader as further entries, plus anything they reference that the receiver needs.
4. Send: The sender posts the Bundle to the receiver's $process-message operation endpoint over an authenticated, encrypted connection.
5. Validate and process: The receiver checks the Bundle against the agreed profiles, checks for duplicates, and applies the event to its own data according to its rules.
6. Respond: The receiver returns a response message, a Bundle of type message whose MessageHeader.response quotes the original message id and gives an outcome code, with an OperationOutcome when there are errors.
In synchronous mode, the response comes back on the same HTTP exchange. In asynchronous mode, the receiver acknowledges receipt and sends the response later to the sender's endpoint, which is harder to build but suits long-running processing.
Handling responses, retries and duplicates
Responses tell the sender what to do next. In R4, MessageHeader.response.code is ok, transient-error or fatal-error. A transient error means the sender may retry the same message later. A fatal error means retrying unchanged will fail again, so the message needs a fix or a person.
Retries create duplicates, and the specification sets out how a receiver detects them using the Bundle id and the MessageHeader id. If both have been seen, the receiver resends its original response. If the MessageHeader id is known but the Bundle id is new, it is a deliberate resend that the receiver may reprocess or reject. If the Bundle id is known with a new MessageHeader id, something is wrong.
In practice, this means the receiver keeps a cache of recent message and Bundle ids with the response it sent, for longer than the sender's timeout. Senders reuse the MessageHeader id on retry, so the receiver can tell a resend from a new event.
Order matters for some events. If an update can arrive before the create it depends on, the receiver needs a rule: hold it, request the missing data, or reject it with a transient error so the sender retries after the first message lands.
When should you choose messaging over REST or subscriptions?
Choose messaging when the exchange is an event between systems that each keep their own data, especially through intermediaries. Choose REST when a client reads or updates resources on a server that owns them. Choose subscriptions when a client needs to know that a resource on a server changed.
- Messaging fits: Orders and results between separate systems, notifications routed through an engine, payer requests that must be processed as a unit, and partners replacing HL7 v2 feeds with FHIR content.
- REST fits: Apps reading patient data, SMART on FHIR launches, writes to a server that is the record's home, and anything covered by US Core APIs.
- Subscriptions fit: Telling a client that a resource it cares about has changed, so it can fetch the new version through REST.
- Mixed designs: A notification by subscription or message, followed by REST reads for detail, is common and keeps messages small.
Support also differs. Most certified EHR FHIR APIs expose REST, and messaging endpoints are less common. Check what the partner's server actually supports before committing to a pattern.
How should message endpoints be secured and tested?
Message endpoints should be secured like any system-to-system FHIR API: mutual TLS or OAuth client credentials, an allow-list of known senders, and audit logs that record each message id, event, sender and outcome. Messages usually carry PHI, so the logs should store identifiers, not full payloads, unless retention rules require more.
Test with a fixed set of sample messages for every event, including broken ones. Each sample should have an expected response code, so the suite proves that a missing focus resource returns a fatal error, a temporary database outage returns a transient error, and a resent message returns the original response.
Run the FHIR Validator against every sample Bundle in the build pipeline, using the agreed profiles. Content errors caught there never reach a partner.
Where FHIR messaging projects break
FHIR messaging projects tend to break on agreements between partners rather than on the format. These failure points are worth checking in design review.
- Undefined events: Event codes are agreed in email instead of a MessageDefinition or guide, so each side reads them differently.
- Missing referenced resources: The focus resource references a Practitioner or Location that is not in the Bundle and that the receiver cannot resolve.
- No duplicate cache: The receiver processes every retry as new, creating duplicate orders or encounters.
- Fatal errors retried: The sender treats every failure as transient and loops on a message that can never succeed.
- Silent async replies: Asynchronous responses go to an endpoint nobody monitors, so failures are never seen.
- Unvalidated content: Bundles are not checked against profiles before processing, and bad data enters the receiver's store.
How Agnotic can help
A FHIR messaging exchange works when both sides agree the events, the content and the error rules before they write code. Agnotic Technologies builds those exchanges for clients: MessageDefinition and profile design, sender and receiver implementations, $process-message endpoints, duplicate caches, retry logic and monitoring of response codes.
Where a partner only supports REST or HL7 v2, Agnotic designs the bridge, so the client's system sees one consistent event model.
Build a FHIR exchange that handles every response
See how Agnotic designs FHIR exchanges between systems, or browse the FHIR hub.
Related reading on Agnotic
Continue with FHIR implementation guides, Questionnaire, subscriptions, HL7 v2 versus FHIR and FHIR integration.


