FHIR CI-Build

This is the Continuous Integration Build of FHIR (will be incorrect/inconsistent at times).
See the Directory of published versions icon

3.5 Messaging using FHIR Resources

FHIR Infrastructure icon Work GroupMaturity Level: 3Standards Status: Trial Use

FHIR Resources can be used in a traditional messaging context, much like HL7 V2 icon (see detailed comparison). Applications asserting conformance to this framework claim to be conformant to "FHIR messaging" (see Conformance).

In FHIR messaging, a "message" is sent from a source application to a destination application when an event happens. Events mostly correspond to things that happen in the real-world.

Messages are Bundle resources, with the following rules:

  • The Bundle type SHALL be message
  • The first resource in the bundle SHALL be a MessageHeader resource
  • If the MessageHeader.focus or MessageHeader.response.details refer to any resources, they SHALL be included in the Bundle entries
  • The bundle MAY also contain additional resources that these other resources refer to. It SHOULD include all resources needed for the receiver to be able to process the message as expected by the MessageHeader.event (this can be defined by a MessageDefinition resource)
  • The bundle SHALL NOT include resources that do have references (by Reference or canonical links) to the MessageHeader

The MessageHeader resource has a code - the message event - that identifies the nature of the request message, and it also carries additional request metadata. The other resources in the bundle depend on the type of the request.

The destination application processes the request and returns zero or more response messages which are also a bundle of resources identified by the type "message", with the first resource in each bundle being a MessageHeader resource with a response section that reports the outcome of processing the message and any additional response resources required.

The message bundle SHALL include only the MessageHeader resource, and any resources directly or indirectly (e.g. recursively) referenced from it.

This specification assumes that content will be delivered from one application to another by some delivery mechanism, and then one or more responses will be returned to the source application. This specification provides a direct delivery mechanism below, although the exact mechanism of transfer can be abstracted, and may include file transfer, HTTP based transfer, MLLP (HL7 minimal lower layer protocol), Apache Kafka, MQ series messaging, or anything else. It is expected that these additional types of transfer mechanisms will define their own implementation guides to define the exact message transfer protocol. The only requirement for the transfer layer is that requests are sent to a known location and responses are returned to the source of the request. This specification considers the source and destination applications as logical entities, and the mapping from logical source and destination to implementation specific addresses is outside the scope of this specification.

The agreements around the content of the messages and the behavior of the two applications form the "contract" that describes the exchange. The contract will add regional and local agreements to the rules defined in this specification.

This specification ignores the existence of interface engines and message transfer agents that exist between the source and destination. Either they are transparent to the message/transaction content and irrelevant to this specification, or they are actively involved in manipulating the message content (in particular, the source and destination headers are often changed). If these middleware agents are modifying the message content, then they become responsible for honoring the contract that applies (including applicable profiles) in both directions.

A key aspect of a message is the impact of its content:

consequence The message represents/requests a change that should not be processed more than once; e.g., making a booking for an appointment.
currency The message represents a response to query for current information. Retrospective processing is wrong and/or wasteful.
notification The content is not necessarily intended to be current, and it can be reprocessed, though there may be version issues created by processing old notifications.

Some Events defined by a FHIR IG are assigned to one of these categories, but others are not able to be categorized in advance, and the category must be determined by the content, context, or use case.

When it is necessary to receive an acknowledgement from multiple parties for a message of notification it becomes a message of consequence: The sender will have to send multiple messages, even if they have the same endpoint.

  • Local protocol requires notification of all lab values
  • Local protocol requires that critical values must be acknowledged by both the ordering and primary provider.
  • Therefore a message of notification becomes a message of consequence.
  • Two messages SHALL be sent, each with a unique identifier.
    • One to the ordering provider
    • One to the primary provider
  • Each message SHALL have a unique acknowledgement

Another key aspect of a message is the impact of its destination.receiver. In some circumstances it may be sufficient to direct a message to an endpoint, while in others it may be necessary to direct the message to a specific organization or person.

Consequence A message of consequence SHOULD have one and only one receiver specified.
Currency A message of currency MAY have one or more receivers specified.
Notification A message of notification MAY have one or more receivers specified.

Each FHIR request message typically has one or more response messages. The response message(s) inform the sender that the message was properly received. Multiple response messages SHALL NOT be returned for messages of consequence, and SHOULD not be returned for notifications. For some notification scenarios it reasonable to only expect either only an http response, possibly accompanied by an OperationOutcome.

In principle, source applications are not required to wait for a response to a transaction before issuing a new transaction. However, in many cases, the messages in a given stream are dependent on each other, and must be sent and processed in order. In addition, some transfer methods may require sequential delivery of messages.

For this reason, a synchronous exchange pattern - where the sender sends a message, and waits on the same channel for a single response, and then sends the next message - is the easiest to understand and manage:

  • the sender sends a message to the receiver (the server)
  • the server processes it, and then returns a response
  • Usually (though not always) the sender waits for the response to the current message before sending the next message

This kind of messaging exchange is the most common because it's the simplest to understand.

The synchronous message exchange described above, however, does not cater for multiple response messages, which may arise when processing queries, and also imposes through-put limitations which may become relevant at high volumes. Additionally, it might not be practical or appropriate to wait for response messages. In these cases, the asynchronous message pattern should be used.

In Asynchronous messaging, the server acknowledges receipt of the message immediately, and responds to the sender separately. The server may respond more than once to any given message.

When a message is received, a receiver can determine from the content of the message header whether it's a new message to process, or a response to a message that has already been sent. Note that asynchronous messaging can be harder to implement due to the amount of complexity compared to synchronous messaging; more can go wrong. This specification does not dictate any particular error handling protocols or responsibilities; these are left to trading partner agreements between implementers.

Implementation Note: Previous releases used a combination of Bundle.id and MessageHeader.id in an attempt to establish the message identity. This posed problems when crossing the boundaries between messaging and RESTful exchange. The current release uses Bundle.identifier exclusively to establish and maintain the message identity. The subsequent sections reflect this change.

A message has 2 important timestamps:

In addition, the message may have additional timestamps in additional resources in the message, either .meta.lastUpdated or others throughout the resources. The meaning of these will depend on the message event.

An incoming message contains exactly one Bundle.identifier which identifies the message. Each time a new message is created, it SHALL be assigned an identifier (Bundle.identifier) that is unique within that message stream. Note that since message streams are often merged with other streams, it is recommended that the identifier should be globally unique. This can be achieved by using a UUID or an OID. Each time a message is sent, the Bundle.identifier should be changed to a new value.

When a receiver receives and processes the message, it responds with a new message with a new identifier, contained in the Bundle.identifier element of the wrapping bundle. The response message also quotes the request Bundle.identifier in MessageHeader.response.identifier so that the source system can relate the response to its request.

Note that Bundle.id and MessageHeader.id also must be present, according to the rules of the underlying Resource.id definition, which allows the potential additional or complementary use of these resources using the FHIR RESTful API.

Some of the message delivery mechanisms mentioned above are reliable delivery systems - the message is always delivered, or an appropriate error is returned to the source. However most implementations use methods which do not provide reliable messaging, and either the request or the response can get lost in transit. For systems exchanging FHIR messages over infrastructure that does not guarantee reliable messaging, the remainder of this section describes a simple approach that senders and receivers SHOULD conform to that ensures reliable delivery and maintains predictable functionality.

If a sender implements this protocol, it SHALL do the following when it receives no response to a message within a configured timeout period based on the value specified in the CapabilityStatement messaging.event.category for the event associated with the message:

Consequence SHALL resend the same message content with the same Bundle.identifier
Currency SHALL resend the same message content with a different Bundle.identifier
Notification SHOULD resend the same message content with the same Bundle.identifier but MAY resend with a different Bundle.identifier

When a receiver implements reliable messaging, it SHALL check the incoming Bundle.identifier against a cache of previously received messages. The correct action to take depends on what is received:

The Bundle.identifier has not been received Process the message
The Bundle.identifier has already been received The original response has been lost (failed to return to the request issuer), and the original response SHALL be resent

The duration period for caching does generally not need to be very long. At a minimum, it could be 1 minute longer than the timeout of the sending system, though it may need to be longer depending on the re-sending policies of the sending system.

Applications that implement reliable messaging declare their reliable cache period in their Capability Statement.

In the first example, a Clinical EHR issues an order for a particular imaging examination to be performed on a patient. This is considered to be a message of Consequence: multiple orders should not be created (in practice there are usually human review processes that catch multiple orders, but repeat orders create entropy in the system that is harmful). The EHR sends a message where the Bundle.identifier is UUID 1 (72edc4e0-6708-42ab-9734-f56721882c10).

The EHR system never receives a response to the message; it does not know whether the request message got lost, or the imaging management systems was unable to process the request, or whether it successfully processed the message and the response was lost. In this case, the EHR system resends the message with same Bundle.identifier.

In this case, the imaging system successfully received the message, and processed it. Because it receives the resent order after 1 minute (which is within its 15 minute cache time), and UUID 1 matches the Bundle.identifier of a message it has already processed, it knows that it already processed the order, and simply returns the previous response. In the case of additional resent requests, the application keeps sending the original response, though it may also alert system administrators that the same original message keeps being resent, since lost messages should be a rare occurrence.

When the EHR system finally receives the message, it knows how the imaging management system responded; it can be sure because the Bundle.identifier from the original request is echoed in the response portion of the returned message.

In this second example, a Clinical EHR needs to know what appointment slots are available for a particular imaging procedure. This is a message of Currency: available slots are ever disappearing, and ordering a slot that has become unavailable is a waste of time for the humans and systems involved. The EHR sends a message where the Bundle.identifier is UUID 2 (4c7f5cb2-5964-4d42-b719-e0227461818c)

The EHR system never receives a response to the message; it does not know whether the request message got lost, or the imaging management systems was unable to process the request, or whether it successfully processed the message and the response was lost. In this case, the EHR system resends the message with a different Bundle.identifier UUID 3 (c7c17fe4-9560-49c7-b2ae-42636476fb86).

In this case, the imaging system successfully received the first message, and processed it. When it receives the resent order after 1 minute (which is within its 15 minute cache time), it sees that a new message of currency with Bundle.identifier UUID3, and it processes the new message, and sends a new response.

When the EHR system finally receives the message, it knows the current slot availability on the imaging management system responded. If for some reason both responses arrive, the EHR system knows that the response linked to the message with UUID 3 is the more current.

Note that the existence of active intermediaries (or "middleware") creates the need for this protocol - the original sender matches the response to the request based on the Bundle.identifier, and so an active intermediary that chooses the re-initiate a query that it previously relayed cannot change the Bundle.identifier.

Applications may only assert conformance to "FHIR messaging" if they publish a Capability statement so the claim may be verified. A Capability statement lists all the message events supported (either as sender or receiver) and for each event, a profile that states which resources are bundled (sender), or are required to be bundled (receiver), and any rules about the information content of the individual resources.

The simplest way to handle messages where there are also RESTful interactions occurring is to use the $process-message. This operation accepts a message, processes it according to the definition of the event in the message header, and returns a one or more response messages. See the operation definition for further details.

The messaging framework documented here is in addition to the FHIR RESTful API defined in this specification. The messaging and RESTful frameworks are related in that both share the same set of resources on which they operate. In fact, the basic MessageHeader resource that the messaging framework is implemented is itself a resource that can treated in a RESTful approach.

The kinds of functionality that the RESTful API and the messaging framework offer are very similar; their primary difference is architectural in nature.

For instance, the messaging framework defines an event for notifying that an administration resource has been created or updated; the REST API offers similar services (history and Subscription). On the other hand, there are differences in the capabilities offered - while a patient merge can be implemented as a series of RESTful operations performed by the client that update all resources linked to the patient, when a message command to merge patient records is processed, the server will do all the work, and is also able to merge in areas not exposed on the RESTful API. The REST API, however, provides a set of basic operations on all resources that would need special definitions in the messaging framework - definitions that are not provided.

There is no expectation that RESTful systems will need to offer messaging support, or vice versa, though systems may find it useful to support both sets of functionality in order to satisfy a wider range of implementers.

In such systems, the RESTful representation of the message is the complete Bundle resource, as the message identity is always driven by the Bundle (and Bundle.identifier). The MessageHeader within the Bundle does not have reliable identity.

As a resource that can be used with the RESTful framework, the MessageHeader resource has the normal resource end-point (/MessageHeader). A FHIR server, which is also a message sender, can use a MessageHeader instance to stage the creation of a message, for example:

  • Create a new MessageHeader resource populating the appropriate elements
  • Point MessageHeader.focus to the relevant resource(s) for the event that is triggering the message
  • Using the information from a MessageDefinition resource (or any other pre-defined template for the message structure), walk the graph of relevant resources and build the Bundle for the message
  • Only when the Bundle is constructed and assigned a Bundle.identifier we have a new message instance ready to be sent
Creating or updating MessageHeader resources in this fashion does not represent the actual occurrence of any event, nor can it trigger any logic associated with the actual event. It is just for managing a set of message header resources.

It is possible to exchange messages using the RESTful end-point as a central point of exchange. This is not particularly efficient compared to other methods, but is useful for low-volume asynchronous exchange.

To send a message, a sender posts the message bundle to the /Bundle end-point, with a uri that identifies the receiver at MessageHeader.destination.endpoint. The RESTful server accepts the bundle, stores it as a single resource, and indexes it on the contents of the included MessageHeader.

To receive messages, a receiver searches for all messages destined for itself, since its last check:

 GET [base]/Bundle?message.destination-uri=[rcv]&_lastUpdated=gt2015-03-01T02:00:02+01:00

The receiver works through the response, processing each message. As each message is processed, the receiver creates a response message, reversing the source and destination, and posts it back to the server.

To check for responses, the original sender searches for response messages destined for itself, since its last check:

 GET [base]/Bundle?message.destination-uri=[snd]&message.response-id:missing=false
      &_lastUpdated=gt2015-03-03T06:03:522+01:00

This lightweight protocol needs ongoing administration to ensure that multiple parties do not interfere with each other by re-using the same system identifier (and against malicious attack).

The MessageHeader.event[x] element identifies the event that the message conveys.

Events may be defined using the MessageDefinition resource. This specification does not define any events, but may do so in the future if implementers find this useful.

A message can be used to invoke an operation as defined for a RESTful interface using an operation definition. To invoke an operation using a message:

  • The requester sends a message (a bundle with type = message, and a message header resource)
  • The message header has an event.system of urn:ietf:rfc:3986
  • The event.code is the URL from the operation definition OperationDefinition.url
  • The MessageHeader.focus refers to a Parameters resource
  • The parameters resource is populated appropriately as specified by the nominated operation definition

The recipient executes the operation as specified, and then:

  • The receiver sends a message (a bundle with type = message, and a message header resource)
  • The message header has the same event as the original message
  • The MessageHeader contains a response that refers to the original request message, and a code for the outcome, with details if the operation failed
  • The MessageHeader.focus refers to a Parameters resource
  • The parameters resource is populated appropriately as specified for the response by the nominated operation definition
  • If the operation definition specifies a single return, then this is returned as the target of the MesssageHeader.focus directly

Here's an example:

<Bundle xmlns="http://hl7.org/fhir">
  <id value="77831928-2a35-4c08-9496-8232323bf48c"/>
  <identifier> 
    <system value="urn:example-org:sender.identifiers"/> 
    <value value="6daec3d0-e3ee-42d4-a51d-50cfacad94a9"/> 
  </identifier> 
  <type value="message"/>
  <!-- normal bundle stuff -->
  <entry>
    <fullUrl value="urn:uuid:6080d4a7-5e05-45dc-96d5-f75329564d1f"/>
    <resource>
      <MessageHeader>
        <id value="6080d4a7-5e05-45dc-96d5-f75329564d1f"/>
        <!-- normal message header stuff -->
        <eventCoding>
          <system value="urn:ietf:rfc:3986"/>
          <!-- value set expansion -->
          <code value="http://hl7.org/fhir/OperationDefinition/ValueSet-expand"/>
        </eventCoding>
        <!-- more normal message header stuff -->
      </MessageHeader>
    </resource>
  </entry>
  <entry>
    <fullUrl value="urn:uuid:00213637-dc7c-40d2-a7de-f4ef1eea5685"/>
    <resource>
      <Parameters>
        <parameter>
          <name value="identifier"/>
          <valueUri value="http://hl7.org/fhir/ValueSet/identifier-type"/>
        </parameter>
      </Parameters>
    </resource>
  </entry>
</Bundle>

Note that there's no way to anchor the execution of the operation against a URL. The only operations that can be executed in this way are defined to be executed at the System or Resource level for a particular resource.

To perform a search with messaging that mirrors REST functionality, the message should leverage a Parameters instance with a standard parameter name of "url" and a valueString that corresponds to the search string that would typically be to the right of the base URL. E.g. if in REST you would say http://someserver.org/fhir/Patient?name=joecode>, then the parameter would have a value of Patient?name=joe.

The event in this case would be either search-type or search-system in the system http://hl7.org/fhir/restful-interaction. (If search-system is used, the string would start with a ? rather than [resource]?.) Systems would indicate support for this search mechanism using their CapabilityStatement as they would any other messaging event. If a system supports messaging search but not RESTful search, the means of describing any limitations on what resources, search parameters, qualifiers, etc. they support is not currently defined.

Messaging interfaces may also choose to define alternative search mechanisms including query-by-example searches that pass in a partially completed resource, or that use a Parameters instance populated in other ways.