This page is part of the FHIR Specification (v4.2.0: R5 Preview #1). The current version which supercedes this version is 5.0.0. For a full list of available versions, see the Directory of published versions . Page versions: R5 R4B R4 R3 R2
FHIR Infrastructure Work Group | Maturity Level: 0 | Trial Use | Security Category: Business | Compartments: Not linked to any defined compartments |
The subscription resource describes a particular client's request to be notified about a Topic.
The subscription resource is used to establish proactive event notifications from a FHIR server to another system. Subscribers request event notifications within a predefined Topic that the server supports, and can further refine their notifications by supplying filters. Each Topic
resource defines a set of allowed filters (Topic.canFilterBy
), which a subscriber refer to within a Subscription
resource (Subscription.filterBy
). Once a subscription is created, any event matching a specified Topic that meets the filtering criteria will cause a notification to be sent using the provided channel.
Subscriptions are active resources; a server can only accept a subscription if it will execute the specified channel for any resources subsequently received. The subscription is no longer active once it is deleted from the server.
By adjusting Subscription.channel.payload.content
, subscribers can request event notifications that include full resource content; or just the ID of the triggering resource; or an empty notification body.
Several channels are defined in the core specification:
Subscription.channel.endpoint
URL (e.g., https://...
)Subscription.channel.endpoint
email URI (e.g., mailto:...
)Subscription.channel.endpoint
URIAdditional channel types can be defined by external implementation guides. See below for further discussion of the various channels.
As an alternative to subscriptions, the RESTful API describes a polling-based method for observing events using bundles and the history operation. This method of polling allows for a much tighter relationship between the client and the server that doesn't involve missing updates and/or deletes.
When using the Subscription
resource, the FHIR server combines
the roles of publisher and information distributer. Other arrangements
of the publish and subscribe pattern describe separate agents for the two roles.
Implementers may implement the Subscription
resource using an
architecture with separate agents, or using any other pub/sub architectire
(e.g. see FHIRCast , or, more generally,
W3C Pub/Sub ).
TODO: Does this imply that Subscriptions can be built on top of FHIRCast?
No resources refer to this resource directly.
This resource does not implement any patterns.
Structure
Name | Flags | Card. | Type | Description & Constraints |
---|---|---|---|---|
Subscription | TU | DomainResource | Notification about a Topic Elements defined in Ancestors: id, meta, implicitRules, language, text, contained, extension, modifierExtension | |
identifier | Σ | 0..* | Identifier | Additional identifiers (business identifier) |
name | Σ | 0..1 | string | Human readable name for this subscription |
status | ?!Σ | 1..1 | code | requested | active | error | off SubscriptionStatus (Required) |
topic | Σ | 1..1 | Reference(Topic) | Reference to the topic being subscribed to |
contact | Σ | 0..* | ContactPoint | Contact details for source (e.g. troubleshooting) |
end | Σ | 0..1 | instant | When to automatically delete the subscription |
reason | Σ | 0..1 | string | Description of why this subscription was created |
filterBy | Σ | 0..* | BackboneElement | Criteria for narrowing the topic stream |
name | Σ | 1..1 | string | Filter label defined in Topic |
matchType | Σ | 0..1 | code | = | in | not-in | above | below SubscriptionFilterByMatchType (Required) |
value | Σ | 1..1 | string | Literal value or resource path |
error | Σ | 0..* | CodeableConcept | Latest error code or note Subscription Error Codes (Example) |
eventCount | Σ | 0..1 | unsignedInt | Notification event counter |
channel | Σ | 1..1 | BackboneElement | The channel on which to report matches to the criteria |
type | Σ | 1..1 | CodeableConcept | rest-hook | websocket | email | message SubscriptionChannel Type Codes (Extensible) |
endpoint | Σ | 0..1 | url | Where the channel points to |
header | Σ | 0..* | string | Usage depends on the channel type |
heartbeatPeriod | Σ | 0..1 | unsignedInt | Interval in seconds to send 'heartbeat' notification |
payload | Σ | 0..1 | BackboneElement | Payload definition |
contentType | Σ | 0..1 | code | MIME type to send, or omit for no payload MimeType (Required) |
content | Σ | 0..1 | code | empty | id-only | full-resource SubscriptionPayloadContent (Required) |
Documentation for this format |
UML Diagram (Legend)
XML Template
<Subscription xmlns="http://hl7.org/fhir"> <!-- from Resource: id, meta, implicitRules, and language --> <!-- from DomainResource: text, contained, extension, and modifierExtension --> <identifier><!-- 0..* Identifier Additional identifiers (business identifier) --></identifier> <name value="[string]"/><!-- 0..1 Human readable name for this subscription --> <status value="[code]"/><!-- 1..1 requested | active | error | off --> <topic><!-- 1..1 Reference(Topic) Reference to the topic being subscribed to --></topic> <contact><!-- 0..* ContactPoint Contact details for source (e.g. troubleshooting) --></contact> <end value="[instant]"/><!-- 0..1 When to automatically delete the subscription --> <reason value="[string]"/><!-- 0..1 Description of why this subscription was created --> <filterBy> <!-- 0..* Criteria for narrowing the topic stream --> <name value="[string]"/><!-- 1..1 Filter label defined in Topic --> <matchType value="[code]"/><!-- 0..1 = | in | not-in | above | below --> <value value="[string]"/><!-- 1..1 Literal value or resource path --> </filterBy> <error><!-- 0..* CodeableConcept Latest error code or note --></error> <eventCount value="[unsignedInt]"/><!-- 0..1 Notification event counter --> <channel> <!-- 1..1 The channel on which to report matches to the criteria --> <type><!-- 1..1 CodeableConcept rest-hook | websocket | email | message --></type> <endpoint value="[url]"/><!-- 0..1 Where the channel points to --> <header value="[string]"/><!-- 0..* Usage depends on the channel type --> <heartbeatPeriod value="[unsignedInt]"/><!-- 0..1 Interval in seconds to send 'heartbeat' notification --> <payload> <!-- 0..1 Payload definition --> <contentType value="[code]"/><!-- 0..1 MIME type to send, or omit for no payload --> <content value="[code]"/><!-- 0..1 empty | id-only | full-resource --> </payload> </channel> </Subscription>
JSON Template
{ "resourceType" : "Subscription", // from Resource: id, meta, implicitRules, and language // from DomainResource: text, contained, extension, and modifierExtension "identifier" : [{ Identifier }], // Additional identifiers (business identifier) "name" : "<string>", // Human readable name for this subscription "status" : "<code>", // R! requested | active | error | off "topic" : { Reference(Topic) }, // R! Reference to the topic being subscribed to "contact" : [{ ContactPoint }], // Contact details for source (e.g. troubleshooting) "end" : "<instant>", // When to automatically delete the subscription "reason" : "<string>", // Description of why this subscription was created "filterBy" : [{ // Criteria for narrowing the topic stream "name" : "<string>", // R! Filter label defined in Topic "matchType" : "<code>", // = | in | not-in | above | below "value" : "<string>" // R! Literal value or resource path }], "error" : [{ CodeableConcept }], // Latest error code or note "eventCount" : "<unsignedInt>", // Notification event counter "channel" : { // R! The channel on which to report matches to the criteria "type" : { CodeableConcept }, // R! rest-hook | websocket | email | message "endpoint" : "<url>", // Where the channel points to "header" : ["<string>"], // Usage depends on the channel type "heartbeatPeriod" : "<unsignedInt>", // Interval in seconds to send 'heartbeat' notification "payload" : { // Payload definition "contentType" : "<code>", // MIME type to send, or omit for no payload "content" : "<code>" // empty | id-only | full-resource } } }
Turtle Template
@prefix fhir: <http://hl7.org/fhir/> . [ a fhir:Subscription; fhir:nodeRole fhir:treeRoot; # if this is the parser root # from Resource: .id, .meta, .implicitRules, and .language # from DomainResource: .text, .contained, .extension, and .modifierExtension fhir:Subscription.identifier [ Identifier ], ... ; # 0..* Additional identifiers (business identifier) fhir:Subscription.name [ string ]; # 0..1 Human readable name for this subscription fhir:Subscription.status [ code ]; # 1..1 requested | active | error | off fhir:Subscription.topic [ Reference(Topic) ]; # 1..1 Reference to the topic being subscribed to fhir:Subscription.contact [ ContactPoint ], ... ; # 0..* Contact details for source (e.g. troubleshooting) fhir:Subscription.end [ instant ]; # 0..1 When to automatically delete the subscription fhir:Subscription.reason [ string ]; # 0..1 Description of why this subscription was created fhir:Subscription.filterBy [ # 0..* Criteria for narrowing the topic stream fhir:Subscription.filterBy.name [ string ]; # 1..1 Filter label defined in Topic fhir:Subscription.filterBy.matchType [ code ]; # 0..1 = | in | not-in | above | below fhir:Subscription.filterBy.value [ string ]; # 1..1 Literal value or resource path ], ...; fhir:Subscription.error [ CodeableConcept ], ... ; # 0..* Latest error code or note fhir:Subscription.eventCount [ unsignedInt ]; # 0..1 Notification event counter fhir:Subscription.channel [ # 1..1 The channel on which to report matches to the criteria fhir:Subscription.channel.type [ CodeableConcept ]; # 1..1 rest-hook | websocket | email | message fhir:Subscription.channel.endpoint [ url ]; # 0..1 Where the channel points to fhir:Subscription.channel.header [ string ], ... ; # 0..* Usage depends on the channel type fhir:Subscription.channel.heartbeatPeriod [ unsignedInt ]; # 0..1 Interval in seconds to send 'heartbeat' notification fhir:Subscription.channel.payload [ # 0..1 Payload definition fhir:Subscription.channel.payload.contentType [ code ]; # 0..1 MIME type to send, or omit for no payload fhir:Subscription.channel.payload.content [ code ]; # 0..1 empty | id-only | full-resource ]; ]; ]
Changes since R3
Subscription | |
Subscription.identifier |
|
Subscription.name |
|
Subscription.status |
|
Subscription.topic |
|
Subscription.reason |
|
Subscription.filterBy |
|
Subscription.filterBy.name |
|
Subscription.filterBy.matchType |
|
Subscription.filterBy.value |
|
Subscription.error |
|
Subscription.eventCount |
|
Subscription.channel.type |
|
Subscription.channel.heartbeatPeriod |
|
Subscription.channel.payload |
|
Subscription.channel.payload.contentType |
|
Subscription.channel.payload.content |
|
Subscription.criteria |
|
See the Full Difference for further information
This analysis is available as XML or JSON.
See R3 <--> R4 Conversion Maps (status = 2 tests that all execute ok. 2 fail round-trip testing and all r3 resources are valid.)
Structure
Name | Flags | Card. | Type | Description & Constraints |
---|---|---|---|---|
Subscription | TU | DomainResource | Notification about a Topic Elements defined in Ancestors: id, meta, implicitRules, language, text, contained, extension, modifierExtension | |
identifier | Σ | 0..* | Identifier | Additional identifiers (business identifier) |
name | Σ | 0..1 | string | Human readable name for this subscription |
status | ?!Σ | 1..1 | code | requested | active | error | off SubscriptionStatus (Required) |
topic | Σ | 1..1 | Reference(Topic) | Reference to the topic being subscribed to |
contact | Σ | 0..* | ContactPoint | Contact details for source (e.g. troubleshooting) |
end | Σ | 0..1 | instant | When to automatically delete the subscription |
reason | Σ | 0..1 | string | Description of why this subscription was created |
filterBy | Σ | 0..* | BackboneElement | Criteria for narrowing the topic stream |
name | Σ | 1..1 | string | Filter label defined in Topic |
matchType | Σ | 0..1 | code | = | in | not-in | above | below SubscriptionFilterByMatchType (Required) |
value | Σ | 1..1 | string | Literal value or resource path |
error | Σ | 0..* | CodeableConcept | Latest error code or note Subscription Error Codes (Example) |
eventCount | Σ | 0..1 | unsignedInt | Notification event counter |
channel | Σ | 1..1 | BackboneElement | The channel on which to report matches to the criteria |
type | Σ | 1..1 | CodeableConcept | rest-hook | websocket | email | message SubscriptionChannel Type Codes (Extensible) |
endpoint | Σ | 0..1 | url | Where the channel points to |
header | Σ | 0..* | string | Usage depends on the channel type |
heartbeatPeriod | Σ | 0..1 | unsignedInt | Interval in seconds to send 'heartbeat' notification |
payload | Σ | 0..1 | BackboneElement | Payload definition |
contentType | Σ | 0..1 | code | MIME type to send, or omit for no payload MimeType (Required) |
content | Σ | 0..1 | code | empty | id-only | full-resource SubscriptionPayloadContent (Required) |
Documentation for this format |
XML Template
<Subscription xmlns="http://hl7.org/fhir"> <!-- from Resource: id, meta, implicitRules, and language --> <!-- from DomainResource: text, contained, extension, and modifierExtension --> <identifier><!-- 0..* Identifier Additional identifiers (business identifier) --></identifier> <name value="[string]"/><!-- 0..1 Human readable name for this subscription --> <status value="[code]"/><!-- 1..1 requested | active | error | off --> <topic><!-- 1..1 Reference(Topic) Reference to the topic being subscribed to --></topic> <contact><!-- 0..* ContactPoint Contact details for source (e.g. troubleshooting) --></contact> <end value="[instant]"/><!-- 0..1 When to automatically delete the subscription --> <reason value="[string]"/><!-- 0..1 Description of why this subscription was created --> <filterBy> <!-- 0..* Criteria for narrowing the topic stream --> <name value="[string]"/><!-- 1..1 Filter label defined in Topic --> <matchType value="[code]"/><!-- 0..1 = | in | not-in | above | below --> <value value="[string]"/><!-- 1..1 Literal value or resource path --> </filterBy> <error><!-- 0..* CodeableConcept Latest error code or note --></error> <eventCount value="[unsignedInt]"/><!-- 0..1 Notification event counter --> <channel> <!-- 1..1 The channel on which to report matches to the criteria --> <type><!-- 1..1 CodeableConcept rest-hook | websocket | email | message --></type> <endpoint value="[url]"/><!-- 0..1 Where the channel points to --> <header value="[string]"/><!-- 0..* Usage depends on the channel type --> <heartbeatPeriod value="[unsignedInt]"/><!-- 0..1 Interval in seconds to send 'heartbeat' notification --> <payload> <!-- 0..1 Payload definition --> <contentType value="[code]"/><!-- 0..1 MIME type to send, or omit for no payload --> <content value="[code]"/><!-- 0..1 empty | id-only | full-resource --> </payload> </channel> </Subscription>
JSON Template
{ "resourceType" : "Subscription", // from Resource: id, meta, implicitRules, and language // from DomainResource: text, contained, extension, and modifierExtension "identifier" : [{ Identifier }], // Additional identifiers (business identifier) "name" : "<string>", // Human readable name for this subscription "status" : "<code>", // R! requested | active | error | off "topic" : { Reference(Topic) }, // R! Reference to the topic being subscribed to "contact" : [{ ContactPoint }], // Contact details for source (e.g. troubleshooting) "end" : "<instant>", // When to automatically delete the subscription "reason" : "<string>", // Description of why this subscription was created "filterBy" : [{ // Criteria for narrowing the topic stream "name" : "<string>", // R! Filter label defined in Topic "matchType" : "<code>", // = | in | not-in | above | below "value" : "<string>" // R! Literal value or resource path }], "error" : [{ CodeableConcept }], // Latest error code or note "eventCount" : "<unsignedInt>", // Notification event counter "channel" : { // R! The channel on which to report matches to the criteria "type" : { CodeableConcept }, // R! rest-hook | websocket | email | message "endpoint" : "<url>", // Where the channel points to "header" : ["<string>"], // Usage depends on the channel type "heartbeatPeriod" : "<unsignedInt>", // Interval in seconds to send 'heartbeat' notification "payload" : { // Payload definition "contentType" : "<code>", // MIME type to send, or omit for no payload "content" : "<code>" // empty | id-only | full-resource } } }
Turtle Template
@prefix fhir: <http://hl7.org/fhir/> . [ a fhir:Subscription; fhir:nodeRole fhir:treeRoot; # if this is the parser root # from Resource: .id, .meta, .implicitRules, and .language # from DomainResource: .text, .contained, .extension, and .modifierExtension fhir:Subscription.identifier [ Identifier ], ... ; # 0..* Additional identifiers (business identifier) fhir:Subscription.name [ string ]; # 0..1 Human readable name for this subscription fhir:Subscription.status [ code ]; # 1..1 requested | active | error | off fhir:Subscription.topic [ Reference(Topic) ]; # 1..1 Reference to the topic being subscribed to fhir:Subscription.contact [ ContactPoint ], ... ; # 0..* Contact details for source (e.g. troubleshooting) fhir:Subscription.end [ instant ]; # 0..1 When to automatically delete the subscription fhir:Subscription.reason [ string ]; # 0..1 Description of why this subscription was created fhir:Subscription.filterBy [ # 0..* Criteria for narrowing the topic stream fhir:Subscription.filterBy.name [ string ]; # 1..1 Filter label defined in Topic fhir:Subscription.filterBy.matchType [ code ]; # 0..1 = | in | not-in | above | below fhir:Subscription.filterBy.value [ string ]; # 1..1 Literal value or resource path ], ...; fhir:Subscription.error [ CodeableConcept ], ... ; # 0..* Latest error code or note fhir:Subscription.eventCount [ unsignedInt ]; # 0..1 Notification event counter fhir:Subscription.channel [ # 1..1 The channel on which to report matches to the criteria fhir:Subscription.channel.type [ CodeableConcept ]; # 1..1 rest-hook | websocket | email | message fhir:Subscription.channel.endpoint [ url ]; # 0..1 Where the channel points to fhir:Subscription.channel.header [ string ], ... ; # 0..* Usage depends on the channel type fhir:Subscription.channel.heartbeatPeriod [ unsignedInt ]; # 0..1 Interval in seconds to send 'heartbeat' notification fhir:Subscription.channel.payload [ # 0..1 Payload definition fhir:Subscription.channel.payload.contentType [ code ]; # 0..1 MIME type to send, or omit for no payload fhir:Subscription.channel.payload.content [ code ]; # 0..1 empty | id-only | full-resource ]; ]; ]
Changes since Release 3
Subscription | |
Subscription.identifier |
|
Subscription.name |
|
Subscription.status |
|
Subscription.topic |
|
Subscription.reason |
|
Subscription.filterBy |
|
Subscription.filterBy.name |
|
Subscription.filterBy.matchType |
|
Subscription.filterBy.value |
|
Subscription.error |
|
Subscription.eventCount |
|
Subscription.channel.type |
|
Subscription.channel.heartbeatPeriod |
|
Subscription.channel.payload |
|
Subscription.channel.payload.contentType |
|
Subscription.channel.payload.content |
|
Subscription.criteria |
|
See the Full Difference for further information
This analysis is available as XML or JSON.
See R3 <--> R4 Conversion Maps (status = 2 tests that all execute ok. 2 fail round-trip testing and all r3 resources are valid.)
See the Profiles & Extensions and the alternate definitions: Master Definition XML + JSON, XML Schema/Schematron + JSON Schema, ShEx (for Turtle) + see the extensions, the spreadsheet version & the dependency analysis a
Path | Definition | Type | Reference |
---|---|---|---|
Subscription.status | The status of a subscription. | Required | SubscriptionStatus |
Subscription.filterBy.matchType | Operator to apply to filter label. | Required | SubscriptionFilterByMatchType |
Subscription.error | Codes to represent subscription error details. | Example | SubscriptionErrorCodes |
Subscription.channel.type | The type of method used to execute a subscription. | Extensible | SubscriptionChannelTypeCodes |
Subscription.channel.payload.contentType | The mime type of an attachment. Any valid mime type is allowed. | Required | Mime Types |
Subscription.channel.payload.content | Codes to represent how much resource content to send in the notification payload. | Required | SubscriptionPayloadContent |
Trial-Use Note:
TODO
- Clarify relationship to FHIRcast or websub
Updates to "Managing Subscriptions and Errors"
- Discuss error codes (Extensible Codeable Concept)
- Define basic error codes here
- Need to discuss eventCount and error detection (insert appropriate examples/workflows)
Updates to "Tracking Subscription Notifications" SHOULD define what the AuditEvent looks like
Servers are responsible for following FHIR sercurity guidance. Recommendations specific to subscriptions are provided below.
A subscription is a request for future event notifications. As with any client-initiated interaction, Subscriptions could request information that a client is not allowed to see, and servers SHALL enforce authorization in accordance with their policy requirements. Servers SHOULD take a Subscription's Topic and filters into account when authorizing the creation of a Subscription, and SHOULD ensure that authorization is (still) in place at the time of each event notification.
When sending an event notification, servers can adopt various strategies to ensure that authorization is still in place. Some strategies may provide imperfect assurance (e.g., a server might rely on signed tokens with some pre-specified lifetime as evidence of authorization). In addition to these strategies, servers can mitigate the risk of disclosing sensitive information by limiting the payload types it supports (e.g., by prohibiting certain clients from requesting full-resource
notification payloads and relying instead on id-only
payloads).
Subscription
resources are not intended to be secure storage for secrets (e.g., OAuth Client ID or Tokens, etc.). Implementers MAY use their judgement on including limited-use secrets (e.g., a token supplied in Subscription.channel.header
to verify that a message is from the desired source).
Each channel type involves the server sending a communication that could reveal information about the client and server relationship, as well as sensitive administrative or clinical information. Servers are responsible for ensuring appropriate security is employed for each channel. The Subscription resource does not address these concerns directly; it is assumed that these are administered by other configuration processes. For instance, a server might maintain a whitelist of acceptable endpoints or trusted certificate authorities for rest-hook channels.
This specification describes three distinct outbound notification types: Event, Handshake and Heartbeat. For each, the notification body is a history Bundle with specific extensions on the Bundle.meta
element to convey Subscription notification details.
TODO: In the following sections provide specific guidance for each channel types this including for REST Hooks + Web Sockets
The primary notification is a notification about an event.
The client expectations upon receipt of a Handshake notification are defined separately for each channel type (e.g., for the rest-hook
channel type, a client endpoint responds to event notifications with standard HTTP response codes).
When a connection to an Endpoint is established, the Server will send an empty History Bundle as a Handshake notification to the client.
The client is not expected to take any special action in receipt of a Handshake notification beyond the channel-specific requirement for receiving an event notification.
Servers MAY periodically send notifications across a channel to ensure that the connection is still alive and valid (e.g., in accordance with a client's requested heartbat interval). The Heartbeat notification is an empty History Bundle sent without incrementing the subscription event count.
The client is not expected to take any special action in receipt of a Heartbeat notification beyond the channel-specific requirement for receiving an event notification.
The subscription-event-count extension indicates the number of unique events that have triggered notification attempts on this Subscription PRIOR to the current notification being sent.
In the case of a handshake, this count will always be zero (0).
In the case of a heartbeat notification, this count will be the same as the last notification and will not be incremented due to the heartbeat notification.
In the case of event notifications, the event count will be incremented by the number of notifications contained within this bundle (often a single notification, though servers may choose to batch notifications within a short time interval).
In the case of an event notification that cannot be delivered (e.g., because a client endpoint is offline), the server MAY retry delivery but does not increment the event count; the count represents unique events, not unique delivery attempts.
The bundle-event-count extension represents the number of event notifications conveyed by the Bundle. This helps clients:
Determine if a notification requires further processing (e.g., a client might discard handshake and heartbeat notifications)
Determine the number of events it should expeect to find in follow-on queries when the in empty
payload type is used
Handle batched results (e.g., a server sending at max one notification per second)
For handshake and heartbeat notifications, the bundle-event-count
will alwayhs be zero.
The subscription-status extension represents the Subscription status values at the time the notification is sent. Note that the status might change between the time the notification is sent and the time it is received/processed, and therefore this status recorded in the extension is not guaranteed to represent status at the time of receipt. The field is useful as a hint to inform the client if the server has encountered errors in notifications immediately preceding this notification.
The subscription-topic-url extension references the Topic resource relevant to this notification, as an absolute URL for the Topic resource on the server that generated the notification (NOT a reference to a canonical Topic URL.)
The subscription-url extension references the Subscription resource that triggered this notification, as an absolute URL for the Subscription resource on the server that generated the notification.
There are three options available when specifying the contents of a Notification: empty
, id-only
, and full-resource
. These options change the level of detail conveyed in the notification Bundle entries.
When deciding which payload type to request, systems SHOULD consider both ease of processing and security of PHI. To mitigate the risk of information leakage, systems SHOULD use the minimum level of detail consistent with the use case. In practice, id-only
provides a good balance for many real-world scenarios.
Examples:
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
.
To convey an event notification, the server POSTs to the client's nominated endpoint URL (i.e. to the channel[type=rest-hook].endpoint
) as shown in the following examples. The content-type of the POST SHALL match the contentType (channel.payload.contentType
) requested during creation of the Subscription. Each channel.header
value SHALL be conveyed as an HTTP request header.
For consistency, the rest of this document assumes application/fhir+json
has been selected, and will be referred to as JSON.
This example uses an event notification with an empty payload to alert the subscriber that new results are available:
Request:
POST to [client endpoint]
Request Payload
{ "resourceType": "Bundle", "id": "notification-empty", "meta": { "extension": [ { "url": "http://hl7.org/fhir/StructureDefinition/subscription-event-count", "valueDecimal": 1 }, { "url": "http://hl7.org/fhir/StructureDefinition/bundle-event-count", "valueUnsignedInt": 1 }, { "url": "http://hl7.org/fhir/StructureDefinition/subscription-status", "valueCode": "active" }, { "url": "http://hl7.org/fhir/StructureDefinition/subscription-topic-url", "valueUrl": "https://example.org/baseR4/Topic/admission" }, { "url": "http://hl7.org/fhir/StructureDefinition/subscription-url", "valueUrl": "https://example.org/Subscription/cb2dce51-a1f5-40b4-a98b-c934eae368e8" } ] }, "type": "history", "timestamp": "2019-08-07T10:24:13.1882432-05:00" }
Since the content element is set to empty
, the data in the resources is only available through the REST API, which helps consolidate authorization and authentication logic. When the subscriber receives a POST to its nominated endpoint it might query the server to fetch all the relevant resources based on the Topic and applicable filters. The client might include a &_since=
query parameter, supplying its last query timestamp to retrieve only the most recent resources. For example, the Topic above is patient admission, and the subscriber might fetch the most recent Encounters for a patient or group of patients.
In this example, the event notification contains the only the ids for resource. This provides the subscriber with a resource ID for focused follow-on queries.
Request:
POST to [client endpoint]
Request Payload
{ "resourceType": "Bundle", "id": "notification-id-only", "meta": { "extension": [ { "url": "http://hl7.org/fhir/StructureDefinition/subscription-event-count", "valueDecimal": 2 }, { "url": "http://hl7.org/fhir/StructureDefinition/bundle-event-count", "valueUnsignedInt": 1 }, { "url": "http://hl7.org/fhir/StructureDefinition/subscription-status", "valueCode": "active" }, { "url": "http://hl7.org/fhir/StructureDefinition/subscription-topic-url", "valueUrl": "https://example.org/baseR4/Topic/admission" }, { "url": "http://hl7.org/fhir/StructureDefinition/subscription-url", "valueUrl": "https://example.org/Subscription/cb2dce51-a1f5-40b4-a98b-c934eae368e8" } ] }, "type": "history", "timestamp": "2019-08-07T10:24:13.1882432-05:00", "entry": [ { "fullUrl": "https://example.org/baseR4/Encounter/2", "request": { "method": "PUT", "url": "Encounter/2" }, "response": { "status": "201" } } ] }
Since the content element is set to id-only
the data in the resources is only available through the REST API which (as in the empty
payload example) helps consolidate authorization and authentication logic. When the subscriber receives a POST to its nominated endpoint it might query the server to fetch all the relevant resources using the supplied resource ids. For example, the Topic above is patient admission, and the subscriber may fetch the Encounter(s) for a patient or group of patients.
In this example, the event notification contains the the entire resource. This is usually appropriate for defining routing rules within a managed ecosystem such as a healthcare institution.
Request:
POST to [client endpoint]
Request Payload
{ "resourceType": "Bundle", "id": "notification-full-resource", "meta": { "extension": [ { "url": "http://hl7.org/fhir/StructureDefinition/subscription-event-count", "valueDecimal": 3 }, { "url": "http://hl7.org/fhir/StructureDefinition/bundle-event-count", "valueUnsignedInt": 1 }, { "url": "http://hl7.org/fhir/StructureDefinition/subscription-status", "valueCode": "active" }, { "url": "http://hl7.org/fhir/StructureDefinition/subscription-topic-url", "valueUrl": "https://example.org/baseR4/Topic/admission" }, { "url": "http://hl7.org/fhir/StructureDefinition/subscription-url", "valueUrl": "https://example.org/Subscription/cb2dce51-a1f5-40b4-a98b-c934eae368e8" } ] }, "type": "history", "timestamp": "2019-08-07T10:24:13.1882432-05:00", "entry": [ { "fullUrl": "https://example.org/baseR4/Encounter/3", "resource": { "resourceType": "Encounter", "id": "3", "meta": { "versionId": "1", "lastUpdated": "2019-08-07T10:49:22Z" }, "status": "in-progress", "class": { "system": "http://terminology.hl7.org/CodeSystem/v3-ActCode", "code": "VR", "display": "virtual" }, "subject": { "reference": "Patient/123" } }, "request": { "method": "PUT", "url": "Encounter/3" }, "response": { "status": "201" } } ] }
This requests that a server forward a copy of any matching resource in JSON format to the nominated server as a create, update, or delete operation within the history bundle.
Note that HTTP is not a secure or encrypted channel. It is strongly recommended that implementations refuse requests to send notifications to URLs using the HTTP protocol (use HTTPS instead). Note that HTTP does not provide endpoint verification. It is strongly recommended that implementations refuse requests to send notifications to URLs using the HTTP protocol (use HTTPS instead).
Subscriptions are created exclusively via the FHIR REST API. But 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 without polling using the /history operation. This can be accomplished using a websocket notification channel.
A client can declare its intention to listen via Web Sockets:
{ "channel": { "type": { "coding": [ { "system": "http://terminology.hl7.org/CodeSystem/subscription-channel-type", "code": "websocket" } ] } } }
The subscriber would then initiate a Web Socket connection to the server, at a URL advertised in the FHIR server's Capability statement (subscriptions/webSocketUrl (todo)). A simple protocol is used to listen for notifications:
Trial-Use Note: Warning: The WebSocket channel type is being examined to provide functional parity with other channel types. In particular, the current system fails to address authentication and a desire for multiple subscriptions to be available to a single WebSocket connection. More work is required.
WebSocket security for FHIR Subscriptions is not yet well understood. Implementers should be aware (at minimum) of the following areas:
A client can register for its user to receive notifications by email:
{ "channel": { "type": { "coding": [ { "system":"http://terminology.hl7.org/CodeSystem/subscription-channel-type", "code":"email" } ] }, "endpoint":"mailto:mt-auburn-results@direct.biliwatch.com", "payload": { "contentType": "text/html", "content": "id-only" } } }
The server will send a new message for each matching resource. The server will create a message based on the values present in the channel.payload.contentType
and channel.payload.content
fields. If a server cannot honor the requested combination, the server can either reject the request or modify it on creation to be suitable.
In general, the email channel makes two assumptions about content:
Because of this context, the channel.payload.contentType
primarily refers to the content of the body of the message. Attachment type information can be appended as a MIME parameter, examples include:
text/plain
: a plain-text body with no attachmenttext/html
: an HTML body with no attachmenttext/plain;attach=application/fhir+json
: a plain-text body with a FHIR JSON bundle attachedtext/html;attach=application/fhir+xml
: an HTML body with a FHIR XML bundle attached
The channel.payload.content
field must 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
.
Trial-Use Note: Warning: The Email/SMS channel types are not yet defined in a highly standardized way, and may not be consistently support by servers. More work is required.
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 ).
A client can register for its user to receive notifications by messaging:
{ "channel": { "type": { "coding":[ { "system":"http://terminology.hl7.org/CodeSystem/subscription-channel-type", "code":"message" } ] }, "endpoint":"http://example.org/fhir/base/endpoint/url", "payload": { "contentType": "application/fhir+json", "content": "full-resource" } } }
For each matching resource, a server will send a message to the nominated end-point. Most servers will require that the end-point is white-listed prior to allowing these kinds of subscriptions.
Trial-Use Note: Warning: The Messaging channel type is not yet defined in a highly standardized way, and may not be consistently support by servers. More work is required.
Defining a new channel type requires clear communication to implementers of both clients and servers around requirements and expectations. Below are some areas which should be considered when creating a channel. Anyone defining a channel type is encouraged to publish the (IG?) at (?).
Trial-Use Note: Warning: This section is still in early drafting.
At a minimum, the following items should be defined:
channel.type
(e.g., 'secure-mq' isntead of 'channel0012')channel.endpoint
(e.g., URI, etc.)channel.header
field values (e.g., REST-hook defines as Auth headers included in a POST)channel.payload.contentType
(e.g., email defines this as the email body, with allowable attachments.)The subscription resource is authored by the client with an initial status of "requested". A new subscription is created on the server using the RESTful create or update interaction. After a successful transaction, the client parses the Location header and saves the new Subscription's logical id for use in subsequent operations.
When the server receives a subscription, it SHOULD check that it is prepared to accept/process the subscription. If it is, it sets the subscription to requested
and process it like a normal create. If it isn't, it SHOULD return an error with an OperationOutcome instead of processing the create
.
The criteria are subject to the same limitations as the client that created it, such as access to patient compartments etc. Note that the subscription MAY remain active after the client access tokens expire.
Once the server has activated the subscription, it sets the status to active
(note: the server may do this as it accepts the resource if it wants).
An appropriately authorized client can use search and/or history operations to see what subscriptions are currently active on the server. Once the subscription is no longer desired, the client deletes the subscription from the server.
The server may retry the notification a fixed number of times and/or refer errors to its own alert logs. If the notification fails, the server SHOULD set the status to error
and mark the error in the resource. If the notification succeeds, the server SHOULD update the status to active
and may remove any error codes. If a subscription fails consistently a server may choose set the subscription status to off
and stop trying to send notifications.
Servers implementing Subscriptions are responsible for complying with their policies on information logging. Servers are encouraged to track all sent notifications, for example with the use of AuditEvent or Communication resources.
Search parameters for this resource. The common parameters also apply. See Searching for more information about searching in REST, messaging, and services.
Name | Type | Description | Expression | In Common |
contact | token | Contact details for the subscription | Subscription.contact | |
payload N | token | The mime-type of the notification payload | Subscription.channel.payload.contentType | |
status N | token | The current state of the subscription | Subscription.status | |
type | token | The type of channel for the sent notifications | Subscription.channel.type | |
url N | uri | The uri that will receive the notifications | Subscription.channel.endpoint |