Subscriptions R5 Backport
1.0.0 - ballot International flag

Subscriptions R5 Backport, published by HL7 FHIR Infrastructure WG. This is not an authorized publication; it is the continuous build for version 1.0.0). This version is based on the current content of https://github.com/HL7/fhir-subscription-backport-ig/ and changes regularly. See the Directory of published versions

Channels

In FHIR R5, there are four channel types which were common enough to be defined in the specification, along with the ability to define additional channel types externally. In this Implementation Guide, we define those same channel types and include the ability to define additional channel by other implementations or Implementation Guides.

Deciding On Channel Type

Both the FHIR specification and this guide rely on the definitions of a reduced set of channel types. While the specification allows for additional channel type definitions, the included set attempts to cover the majority of common use cases. Below is some guidance for implementers to consider when selecting a channel type.

REST-Hook

The FHIR standard makes extensive use of the RESTful model. Given the popularity of REST and widespread adoption, most implementers should assume REST-hook as the ‘default’ channel type. In general, REST-based systems are well-supported (e.g., tooling, infrastructure, documentation, etc.), and will present the lowest bar for implementation.

Websocket

Websockets are unique in the pre-defined channel types in being the only channel that does not require the client to have an endpoint. Due to this property, the websocket channel is very useful for clients where creating an endpoint would be difficult or impossible (e.g., mobile clients, web-based clients, etc.).

Email

The Email channel is the only channel that could contest REST in popularity of non-FHIR implementations. That said, Email communication is often high-latency and is typically used for communication to individuals - not applications. Email channels are particularly useful in the context of these non-application use cases, such as public health notifications. For example, if a public health agency does not have the ability or desire to build a custom RESTful solution (e.g., creating and maintaining an endpoint to receive notifications, as well as software to consume those notifications), it is straightforward to map notifications to email addresses or aliases.

FHIR Messaging

FHIR Messaging is a mechanism defined to allow for non-RESTful communication between FHIR servers and clients. One common use case is when connectivity is an issue (e.g., remote sites that batch all communications when connections are available). This channel defines how to integrate topic-based subscriptions with the FHIR Messaging model.

Custom Channels

For use cases that are not well-met by any of the predefined channels, the Subscriptions Framework allows for custom channel definitions. Some examples of scenarios where custom channels may be applicable include:

  • requirements for reliable (guaranteed) delivery (e.g., message queues)
  • implementations using other communication protocols (e.g., protocols specific to a cloud-based provider)
  • implementations using a non-standard serialization format

REST-Hook

To receive notifications via HTTP/S POST, a client should request a subscription with the channel type of rest-hook and set the endpoint to the appropriate client URL. Note that this URL must be accessible by the hosting server.

To convey an event notification, the server POSTs a Bundle to the client’s nominated endpoint URL per the format requests in the Subscription:

When a Subscription is created for a REST Hook channel type, the server SHALL set initial status to requested, pending verification of the nominated endpoint URL. After a successful handshake notification has been sent and accepted, the server SHALL update the status to active. Any errors in the initial handshake SHALL result in the status being changed to error.

An example workflow for establishing a rest-hook subscription is shown below.

FHIR ServerREST APIFHIR ServerREST APIClientClientClient Endpoint(HTTP/S)Client Endpoint(HTTP/S)Subscription Negotiation1Create Subscriptionchannel.type: rest-hook2SuccessSubscription.status: requested3HTTP POST - HandshakeBundle:Bundle.type: historySubscriptionStatus.type: handshake4HTTP Success, e.g.:200: OKServer updates Subscription:Subscription.status: activeREST Subscription Processing[Server sends heartbeat]5HTTP POST - HeartbeatBundle:Bundle.type: historySubscriptionStatus.type: heartbeat6HTTP Success, e.g.:200: OK[Server sends event-notification]7HTTP POST - NotificationBundle:Bundle.type: historySubscriptionStatus.type: event-notification8HTTP Success, e.g.:200: OK
Diagram showing a possible workflow for rest-hook subscriptions
  1. Client creates a Subscription with the channel.type set to rest-hook.
  2. Server responds with a success code and creates the subscription with a state of requested.
  3. Server performs an HTTP POST to the requested endpoint with a handshake notification.
  4. Client Endpoint accepts the POST and returns a success HTTP code (e.g., 200).
  5. Server may send notifications of type heartbeat at any time.
  6. Endpoints should respond with appropriate HTTP status codes (e.g., 200).
  7. Server may send notifications of type event-notificaiton at any time.
  8. Endpoints should respond with appropriate HTTP status codes (e.g., 200).
Security Notes

HTTP is neither a secure nor an encrypted channel, nor does it provide endpoint verification. It is strongly recommended that implementations refuse requests to send notifications to URLs using the HTTP protocol (use HTTPS instead).

Websockets

While the primary interface for FHIR servers is the FHIR REST API, notifications need not occur via REST. Indeed, some subscribers may be unable to expose an outward-facing HTTP server to receive triggered notifications. For example, a pure client-side Web app or mobile app may want to subscribe to a data feed. This can be accomplished using a websocket notification channel.

A client can declare its intention to receive notifications via Web Sockets by requesting a subscription with the channel type of websocket.

An example workflow for receiving notifications via websockets is shown below:

FHIR ServerREST APIFHIR ServerREST APIClientClientFHIR ServerWebsocket EndpointFHIR ServerWebsocket EndpointSubscription Negotiation1Create Subscriptionchannel.type: websocket2SuccessSubscription.status: activeWebsocket Subscription Processing[Client binds via initial token]3FHIR Operation:.../Subscription/example/$get-ws-binding-token4FHIR Parameters:token: token-abcexpiration: some-future-date-timewebsocket-url: wss://example.org/fhirR4ws5Connectwss://example.org/fhirR4ws6bind-with-token token-abc7Bundle:Bundle.type: historySubscriptionStatus.type: handshake[Sever sends heartbeat]8Bundle:Bundle.type: historySubscriptionStatus.type: heartbeat[Server sends event-notification]9Bundle:Bundle.type: historySubscriptionStatus.type: event-notification[Token is expiring soon]10FHIR Operation:.../Subscription/example/$get-ws-binding-token11FHIR Parameters:token: token-defexpiration: another-future-date-timewebsocket-url: wss://example.org/fhirR4ws12Client checks the url and establishesa new connection if necessary13bind-with-token: token-def14Bundle:Bundle.type: historySubscriptionStatus.type: handshake[Either system may end a session]15Disconnect
Diagram showing a possible workflow for websocket subscriptions
  1. Client creates a Subscription with the channel.type set to websocket.
  2. Server responds with a success code and creates the subscription.
  3. Client requests a websocket binding token, by invoking the $get-ws-binding-token operation via REST. Note: this call is intended to be repeated as necessary (e.g., prior to a token expiring, a client should request a new one).
  4. Server returns Parameters with a token,expiration, and websocket-url.
  5. Client connects to the server via websockets, via the returned websocket-url (wss:// preferred).
  6. Client sends a bind-with-token message via websockets, with the token provided by the server. Note: this operation can be repeated concurrently for multiple subscriptions, and serially for continued operation over a single websocket connection.
  7. Server sends one or more handshake messages via websockets (one per Subscription included in the token). Note: some servers may additionally send one or more event-notification messages at this time (e.g., all messages since last connected, last ‘n’ messages, etc.). Clients are expected to handle either flow.
  8. Server may send notifications of type heartbeat at any time.
  9. Server may send notifications of type event-notification at any time.
  10. If the token is expiring soon and the Client wishes to continue receiving notifications, it should invoke the $get-ws-binding-token operation via REST.
  11. Server returns Parameters with a new token, expiration, and websocket-url.
  12. If the websocket-url is different from the existing connection, the Client establishes a new connection to the Client Endpoint.
  13. Client sends a bind-with-token message via websockets, with the token provided by the server. Note: this operation can be repeated concurrently for multiple subscriptions, and serially for continued operation over a single websocket connection.
  14. Server sends one or more handshake messages via websockets (one per Subscription included in the token). Note: some servers may additionally send one or more event-notification messages at this time (e.g., all messages since last connected, last ‘n’ messages, etc.). Clients are expected to handle either flow.
  15. Either the server or the client may close the websocket connection.

Notes:

  • Notifications sent from the server SHALL be in the MIME Type specified by the Subscription.channel.payload, however, if notifications are requested for multiple subscriptions with different MIME types, the server MAY choose to send all notifications in a single MIME type.
  • Notifications SHALL conform to the content level specified by the http://hl7.org/fhir/uv/subscriptions-backport/StructureDefinition/backport-payload-content extension.
  • When receiving notifications, a connected websocket client has no responsibilities beyond reading the message (e.g., there is no acknowledgement message).
Security Notes

WebSocket security poses several challenges specific to the channel. When implementing websockets for notifications, please keep in mind the following list of some areas of concern:

  • Authentication of WebSockets is not generically interoperable with JWT or other ‘Authentication header’ protocols - the JavaScript WebSocket API does not include the ability to include them.
  • Given client limitations on concurrent WebSocket connections (commonly 6), it is recommended that a single connection be able to authenticate to multiple Subscription resources.
  • Unlike HTTP/S requests, WebSockets can be long-lived. Because of this, the case of revoking access of an active connection must be considered.

Email

While the primary interface for FHIR servers is the FHIR REST API, notifications need not occur via REST. Indeed, some subscribers may be unable to maintain an outward-facing HTTP server to receive triggered notifications. For example, a public health organization may want to be notified of outbreaks of various illnesses. This can be accomplished using an email notification channel.

A client can declare its intention to receive notifications via Email by requesting a subscription with the channel type of email and setting the endpoint to the appropriate email URI (e.g., mailto:public_health_notifications@example.org).

The server will send a new message each time a notification should be sent (e.g., per event or per batch). The server will create a message based on the values present in the Subscription.channel.payload and payload content fields. If a server cannot honor the requested combination, the server SHOULD reject the Subscription request rather than send unexpected email messages.

The email channel sets two guidelines about content:

  • Message Body content SHALL be human readable
  • Message Attachments SHOULD be machine readable

Due to these guidelines, the Subscription.channel.payload refers to the content of the body of the message. Attachment type information can be appended as a MIME parameter, for example:

  • text/plain: a plain-text body with no attachment
  • text/html: an HTML body with no attachment
  • text/plain;attach=application/fhir+json: a plain-text body with a FHIR JSON bundle attached
  • text/html;attach=application/fhir+xml: an HTML body with a FHIR XML bundle attached

The payload content field SHALL be applied to any attachments and MAY be applied to body contents (depending on server implementation). However, a server must not include a body which exceeds the specified content level. For example, a server may choose to always include a standard message in the body of the message containing no PHI and vary the attachment, but cannot include PHI in the body of an email when the content is set to empty.

An example workflow for receiving notifications via email is shown below:

FHIR ServerREST APIFHIR ServerREST APIClientClientEmail Server(SMTP/S, Direct)Email Server(SMTP/S, Direct)Negotiating a Subscription1Create Subscriptionchannel.type: email[Without handshake]2SuccessSubscription.status: active[With handshake]3SuccessSubscription.status: requested4Message with verification, opt-in, opt-out, etc.5SMTP Success, e.g.:250: OKServer updates Subscription:Subscription.status: activeEmail Subscription Processing[Server sends heartbeat]6Message with readable content and attached Bundle:Bundle.type: historySubscriptionStatus.type: heartbeat[Server sends event-notification]7Message with readable content and attached Bundle:Bundle.type: historySubscriptionStatus.type: event-notification
Diagram showing a possible workflow for email subscriptions
  1. Client creates a Subscription with the channel.type set to email.
  2. Server may respond with a success code and create the subscription with a state of active.
  3. Server may respond with a success code and create the subscription with a state of requested.
  4. Server sends an initial message via the specified email server (e.g., verify the request, opt-out instructions, etc.).
  5. Email Server responds with a channel appropriate response code (e.g., 250: OK).
  6. Server may send notifications of type heartbeat at any time.
  7. Server may send notifications of type event-notification at any time.
Security Notes

Email (SMTP) is not a secure channel. Implementers must ensure that any messages containing PHI have been secured according to their policy requirements (e.g., use of a system such as Direct).

FHIR Messaging

Warning: This section is considered draft; feedback from topic authors is requested to refine the message-based channel.

There are times when it is desireable to use Subscriptions as a communication channel between FHIR servers that are connected via Messaging instead of REST. This can be accomplished using a Subscription with the channel type of message.

To receive notifications via messaging, a client should request a subscription with the channel type of message and set the endpoint to the destination FHIR server base URL. Note that this URL must be accessible by the hosting server.

The FHIR server hosting the subscription (server) will send FHIR messages to the destination FHIR server (endpoint) as needed. These messages will, as the contents of the message, have a fully-formed notification Bundle.

An example workflow for receiving notification via FHIR messaging is shown below:

FHIR ServerREST APIFHIR ServerREST APIClientClientClient FHIR ServerREST EndpointClient FHIR ServerREST EndpointSubscription Negotiation1Create Subscriptionchannel.type: message2SuccessSubscription.status: requested3POST$process-message(handshake)Bundle:Bundle.type: messageentry[1]: Bundle: subscription-notificationSubscriptionStatus.type: handshake4Success, e.g.:200: OKServer updates Subscription:Subscription.status: activeFHIR Messaging Subscription Processing[Server sends heartbeat]5POST$process-message(heartbeat)Bundle:Bundle.type: messageentry[1]: Bundle: subscription-notificationSubscriptionStatus.type: heartbeat6Success, e.g.:200: OK[Server sends event-notification]7POST$process-message(event-notification)Bundle:Bundle.type: messageentry[1]: Bundle: subscription-notificationSubscriptionStatus.type: event-notification8Success, e.g.:200: OK
Diagram showing a possible workflow for FHIR Messaging subscriptions
  1. Client creates a Subscription with the channel.type set to message.
  2. Server responds with a success code and creates the subscription with a state of requested.
  3. Server sends a FHIR Message to the requested endpoint with a handshake notification.
  4. Client Endpoint accepts the Message and returns success.
  5. Server may send notifications of type heartbeat at any time.
  6. Server may send notifications of type event-notification at any time.
Security Notes

Servers MAY require that the end-point is allow-listed prior to allowing these kinds of subscriptions. Additionally, servers MAY impose authorization/authentication requirements for server to server communication (e.g., certificate pinning, etc.).