
In the Health Application Lightweight Protocol (HALO), the SMART on FHIR Accelerator (SoFA) enables Point of Care (PoC) systems without a native FHIR API or SMART-compliant authorization server to participate in the SMART App Launch flow. This is achieved in part through the $set-context
(See Operation: $set-context) operation, which allows the PoC to populate relevant FHIR resources and clinical context within the SoFA before launching a SMART application. Once set, this context enables the SMART application to access necessary resources within the SoFA-hosted FHIR server as if it were the PoC.
While $set-context
establishes an initial snapshot of clinical data within the SoFA, it does not handle dataflow back to the PoC. To address this, HALO defines FHIR Subscriptions as the primary mechanism for synchronizing updates and ensuring new or modified data can be incorporated into the patient chart.
FHIR Subscriptions allow the SoFA to notify the PoC when:
$set-context
are modified, ensuring the PoC remains aware of updates relevant to patient care.By implementing FHIR Subscriptions, HALO ensures PoC systems are not passive context providers but can continuously synchronize with updates to relevant clinical data in SoFA post-launch.
The FHIR R5 Backport IG is not a new FHIR version but a set of guidelines that enable FHIR R5’s Subscription model to be used in FHIR R4 (and R4B). It defines how R4 (and R4B) servers and clients can implement topic-based Subscriptions using only FHIR R4 (or R4B) resources and structures. This ensures that systems can adopt the R5 Subscription workflow without requiring a transition to FHIR R5.
HALO is built on FHIR R4 to ensure compatibility with CA Core+ and related specifications. While FHIR R4 does include a FHIR Subscriptions specification, it is based on the earlier query-based subscription mechanism. To support a more scalable and modern event-driven workflow, HALO adopts the FHIR R5 Backport Implementation Guide (IG) for Subscriptions. This allows HALO to implement FHIR R5’s topic-based Subscription model while remaining within an R4-compliant ecosystem.
For more information, please refer to the FHIR Subscriptions R5 Backport specification.
In the FHIR Subscription model, a SubscriptionTopic defines specific events that can occur on a FHIR server—such as a resource state change—that clients can subscribe to. When an event matching a SubscriptionTopic occurs, the server generates a notification, which encapsulates details about the event and any relevant FHIR resources. This notification is then delivered to subscribers according to the delivery method specified in their subscription.
The R5 Backport approach represents topics using a simple canonical URL. This URL serves as a unique identifier and reference to the topic, with the underlying details for the topic defined and provided out of band (e.g., within an Implementation Guide).
Canonical URL: http://fhir.infoway-inforoute.ca/io/HALO/SubscriptionTopic/sofa-content-update
PoC vendors creating a Subscription for this topic SHALL include this URL in the Subscription.criteria element to ensure proper recognition and handling by the SoFA.
For more information on how subscription topics are handled, please see the Subscription Topics in R4 section of the FHIR Subscriptions R5 Backport specification.
PoC vendors leveraging the SoFA launch flow SHALL establish and maintain an active Subscription to this topic prior to initiating the launch sequence.
Jurisdictional SoFA implementations SHALL validate that the launching PoC system has an active Subscription to this topic as part of the processing of the $set-context
operation. If no active Subscription is identified during the $set-context
operation, the SoFA SHALL reject the operation and return an error indicating that an active Subscription is required.
In later versions of FHIR, subscription topics can be made discoverable to potential subscribers via the standard GET /SubscriptionTopic
endpoint. In FHIR R4, the lack of a resource definition makes this option not possible. Instead, the R5 Backport specification defines the CapabilityStatement SubscriptionTopic Canonical Extension. SoFA implementers MAY leverage this extension within their SoFA FHIR server's CapabilityStatement to enable the discovery of this topic.
A FHIR Subscription lifecycle begins when a client submits a new Subscription resource via an HTTP POST
request to the /Subscription
endpoint of the target FHIR server. This request defines the parameters for the subscription, including the desired notification delivery mechanism (i.e., rest-hook
or websocket
), the destination for notifications, and any additional configuration details such as HTTP headers.
In FHIR R5, clients subscribe by creating a standard Subscription resource. Using R5 Backport, the equivalent operation is performed by creating a Susbcription resource that complies with the R4/B Topic-Based Subscription
profile.
The SoFA Content Update topic operates at the PoC system level. Clients creating subscriptions to the SoFA topic SHALL create a single system/instance-wide subscription that is shared for all users and all launch contexts. Further, due to the system-wide nature of this subscription, clients must coordinate out-of-band with their respective jurisdictional SoFA providers to establish system-level authentication to the jurisdictional SoFA FHIR server (e.g., using the client credentials flow).
PoC vendors SHOULD implement measures to avoid creating multiple subscriptions for the SoFA Content Update topic for the same system unless specifically necessary to accommodate their unique implementation. This is to mitigate the risk of duplicate events being created for the same resource without a means for detecting conflicts due to the creation of distinct notification eventNumber
sequences for each subscription (See the Event ID Tracking section below).
To support subscription creation, a SoFA SHALL implement the type-level create
interaction for the Subscription
resource. Additionally, all PoC vendors leveraging the SoFA launch flow MUST have an active Subscription prior to initiating the launch of an app.
The handshake step verifies that the subscriber's requested delivery method is valid before activating the Subscription. When a new Subscription is received, the FHIR server stores it with a status of requested
, keeping it in a pending state while it sends a test notification via the specified delivery channel (e.g., rest-hook
, websocket
). The subscriber MUST acknowledge receipt for the handshake to succeed. If successful, the server transitions the Subscription to active
and begins sending notifications.
In FHIR R5, the handshake notification Bundle is distinguished from other notification Bundles by the SubscriptionStatus.type
element, which is set to handshake
. In FHIR R5 Backport, handshake notifications are represented similarly but use the Parameters.parameter:type
element of the R4 Backported R5 SubscriptionStatus profile instead.
Each supported subscription channel implements the handshake mechanism differently, depending on its capabilities and communication model. The specific handshake procedures for rest-hook
and websocket
subscriptions are described in their respective sections below.
To prevent notifications from being sent to unverified or misconfigured destinations, SoFA implementers MUST NOT activate a Subscription without first successfully performing the handshake interaction. Likewise, PoC vendors MUST implement support for responding to handshake notifications. If the PoC fails to complete the handshake, the SoFA SHALL update the Subscription.status
to error
and refrain from sending further notifications.
For more information on the handshake mechanism, refer to the Handshake Notification section of the FHIR R5 specification.
As part of Subscription creation, the subscriber specifies a heartbeat period, defining the maximum allowable time without receiving a notification before assuming a potential issue with the delivery system.
In FHIR R5, this timeout is defined using the Subscription.heartbeatPeriod
element. In FHIR R4, the equivalent functionality is achieved via the Backport R5 Subscription Heartbeat Period extension, which is applied to the Subscription.channel
element within the R4/B Topic-Based Subscription profile.
Per the base FHIR specification, this element is optional, and PoC systems may omit it, signaling to the FHIR server (i.e., the SoFA) that heartbeat notifications are not required. However, in some jurisdictions, the SoFA MAY enforce the heartbeat mechanism by applying a default period on behalf of the PoC.
To ensure at least one notification is sent within the specified window, FHIR R5 introduces the heartbeat—a special type of notification that serves only to satisfy the timeout period. Unlike event-based notifications, heartbeats do not increment the notification eventNumber
sequence for the Subscription.
In FHIR R5, heartbeat notification Bundles are identified by a heartbeat
value in the SubscriptionStatus.type
element. In FHIR R5 Backport, this element is available through the Parameters.parameter:type
element of the R4 Backported R5 SubscriptionStatus profile.
If a subscriber does not receive any notifications—heartbeat or otherwise—within the expected timeframe, it SHOULD assume an issue has occurred. Refer to the Recovery section for guidance on handling such scenarios.
For more information on how the heartbeat mechanism works, refer to the Heartbeat Notification section of the FHIR R5 specification.
There are multiple ways a SoFA implementation can handle the end of a Subscription’s lifecycle, at which point no further notifications will be delivered.
The most direct method is deleting the Subscription resource. The SoFA SHALL support instance-level delete
interactions for Subscriptions, allowing PoCs to remove the resource and permanently disable notifications. Additionally, if a PoC provides an instant value in the Subscription.end
element, the SoFA SHALL honor this timeout and automatically delete (or soft delete) the Subscription at the specified time.
A less permanent and reversible approach is to set Subscription.status
to off
. The SoFA SHALL support instance-level updates to the Subscription resource, enabling PoCs to modify an existing Subscription. PoCs MAY use this capability to update the Subscription.status
element to off
, deactivating the Subscription without deleting it. This prevents notifications from being sent while preserving the resource. If the PoC later wishes to reactivate the Subscription, it can update the status back to requested
, restarting the lifecycle.
To prevent unexpected notifications, the SoFA SHALL NOT automatically transition a Subscription from off
to requested
or active
on behalf of the PoC.
To enable clients to retrieve and review a list of events triggered for a given Subscription, FHIR R5 introduced the Operation $events on Subscription. This functionality was backported to FHIR R4 via the Subscription Events Operation in the R5 Backport Implementation Guide.
According to the FHIR Subscriptions R5 Backport specification, the $events
operation accepts the following optional input parameters:
eventsSinceNumber
– The starting event number (inclusive), serving as the lower bound.eventsUntilNumber
– The ending event number (inclusive), serving as the upper bound.content
– Specifies the preferred format of returned data, using values from the backport-content-value-set
(e.g., empty
, id-only
, full-resource
). This is a client preference that servers MAY ignore.Further, the operation definition states that servers handling this operation are required to return a notification Bundle containing all matching events and their corresponding resources in an output parameter called return
.
SoFA implementations MUST support the Subscription Events Operation as defined in the FHIR Subscriptions R5 Backport specification. To ensure continuity in event-driven workflows, a Point of Care (PoC) system MUST account for missed notifications. If a PoC detects or suspects notification gaps, it SHALL use the Subscription $events
operation to retrieve past events.
PoC systems can request historical notifications by specifying eventsSinceNumber
with the eventNumber
of the last successfully processed event. This allows them to receive all subsequent notifications.
Some common scenarios where event recovery may be needed include,
Startup Recovery: If a PoC had an existing Subscription before a restart or downtime, it MUST call $events
to retrieve any missed notifications before re-establishing the Subscription.
Error State Recovery: If a Subscription enters an error state (e.g., due to exceeding the heartbeat period), the PoC MUST call $events
to retrieve missed notifications prior to reactivating it or creating a new Subscription.
Notification Gap Recovery: If a PoC detects a gap in received notifications by monitoring eventNumber
values in incoming notifications (as described in the Event Continuity section), it SHALL call $events
with the eventsSinceNumber
parameter set to an eventNumber
just before the detected gap.
When a notification delivery failure occurs for a given Subscription, the FHIR specification requires the Subscription to be moved into an error state by setting the Subscription.status
element to error
. Optionally, the server (i.e., the SoFA) may attempt to recover by retrying the notification. If the notification is successfully delivered, the server may automatically transition the Subscription back to active
. However, if delivery continues to fail after a predefined number of retries (determined by the server), the server may set the Subscription status to off
, ceasing further notifications.
In FHIR R5, when a server transitions a Subscription into the error
status, it populates the Subscription.error element with a list of CodeableConcepts, each containing a coded error that describes the issue. This element is made available in R4 via the Parameters.parameter:error
slice defined within the R4 Backported R5 SubscriptionStatus profile of Parameters. Currently, HALO does not specify a required ValueSet for this element. Instead, individual jurisdictional SoFA implementations determine the appropriate codes and are responsible for communicating expectations to PoC vendors regarding their handling. HALO may introduce a standardized set of error codes for this element in a future iteration based on community feedback.
Since a notification failure indicates that the SoFA is likely unable to communicate with the subscribing PoC system, it becomes the responsibility of the PoC to detect such errors and proactively check in with the server. To facilitate this, the FHIR R5 specification defines the $status operation on Subscription, which allows the PoC to query the FHIR server for the current status of its Subscription.
FHIR supports the $status
operation at both the type level (GET [base]/Subscription/$status
) and the instance level (GET [base]/Subscription/[id]/$status
). In either case, the response includes a single required output parameter, return
, which contains a Bundle
. This Bundle
lists SubscriptionStatus
resources, one per queried Subscription. In the FHIR Subscriptions R5 Backport version of this operation, the return
Bundle is of type searchset
and contains a list of Parameters resources that complies with the R5 Backport SubscriptionStatus profile.
The SoFA SHALL support the instance-level Subscription $status
operation, but the type-level implementation is considered out of scope.
If the PoC detects that its Subscription is in an error state using the $status
operation, it SHALL use the $events
operation to retrieve any missed notifications (as described in the Missed Notification section above) before addressing the issue and reactivating the Subscription.
For more guidance on handling errors within the FHIR Subscriptions paradigm, see the Handling Errors section of the FHIR Subscriptions R5 Backport specification.
For the HALO Content Update subscription topic, events are triggered by interactions from SMART Applications that affect resources on the SoFA FHIR server.
SoFA implementations will generally support three core FHIR interactions: create
, update
, and delete
. However, jurisdictions may opt to support only a subset of these based on their individual requirements and policies (e.g., restricting the use of delete
). Other, more complex interactions—such as conditional creates, conditional updates, conditional deletes, batches, and transactions—are not officially supported in this release.
To maintain synchronization between the PoC and the SoFA, jurisdictions SHALL NOT grant SMART Applications scopes for state-changing interactions unless the SoFA can generate the corresponding notifications and PoC vendors are prepared to handle them. PoC vendors SHOULD ensure that their App Catalog processing checks the state-changing scopes requested by an application and prevents users from launching it if the PoC does not support write operations for the affected CA Core+ profiled resources.
Jurisdictions are responsible for clearly communicating these SoFA-specific limitations to PoC and SMART App vendors during onboarding.
For additional details on interactions defined by FHIR, please refer to the HTTP page of the base HL7 FHIR specification.
In FHIR R5 Backport, notifications are represented as FHIR Bundle
resources with a Bundle.type
of subscription-notification
. The first entry in every such Bundle
SHALL be a SubscriptionStatus
resource, which provides critical metadata about the Subscription, including its current status, a reference to the SubscriptionTopic
, the type of notification (e.g., event-notification
, handshake
, heartbeat
, etc.), event details, references to affected resources within the Bundle
, and any relevant error information if the Subscription is in an error state.
To replicate this structure in FHIR R4, HALO leverages profiles from the R5 Backport specification. Notification Bundles SHALL conform to the R4 Backported R5 SubscriptionStatus profile, with the SubscriptionStatus
resource implemented using the Parameters
resource, following the R4/B Topic-Based Subscription profile.
Each event entry in the SubscriptionStatus
includes a focus
element, which SHALL reference the primary resource within the notification Bundle
that triggered the event. Aside from the SubscriptionStatus
entry, all other resources included in the notification SHALL conform to CA Core+ profiles.
For the current release, HALO does not impose additional profiling constraints on these resources. However, future releases may introduce a defined profile set to further standardize Subscription notifications.
In the FHIR R5 Subscriptions specification, the Subscription.content
element allows subscribers to define the structure of the notifications they receive. Specifically, a subscribing client can request notifications in one of the following formats:
To enable this functionality within the FHIR R4 Subscription resource, the FHIR R5 Backport specification extends the Subscription.payload element with the introduction of the Backport R5 Subscription Payload Content Information extension.
The officially recommended approach for jurisdictions is to implement support for these notifications using either the full-resource
or id-only
types, with the latter requiring additional requests from the PoC to retrieve the referenced resources.
An exact use case for the empty
payload type in the context of the SoFA has yet to be identified; however, it has been kept in scope to remain flexible to unforeseen jurisdictional requirements.
For more information on the notification payload types, see the Payload Types section of the FHIR R5 Subscription page.
Upon receiving a notification, the PoC is responsible for ensuring that events are processed correctly and in a way that maintains data integrity.
PoC vendors SHALL follow the guidance in the Event Continuity section to process events in the correct order and ensure that no events are skipped.
To determine whether an incoming resource represents a creation or an update, subscribers SHALL use the mappings they maintain between the logical IDs (Resource.id
) of their local resources and the resources created within the SoFA during context initialization. As described in the $set-context
operation, this mapping allows the PoC to identify whether a resource in the notification already exists within their system. If a mapping exists for the resource’s ID, it indicates an update to an existing record; otherwise, the PoC can assume the resource is to be created within its system.
Similar to the Resource ID Mapping section in the Operation: $set-context page, when receiving notifications that create new resources, PoC vendors SHALL store a mapping between the SoFA's logical ID and the resource instances they create within their system. When sending notifications for resources, the SoFA SHALL include the logical ID for each resource. This ensures the relationships between the resources on the SoFA and the resources within the PoC are maintained, providing the basis for reconciliation of future update notifications.
Beyond these core processing steps, the exact manner in which updates are applied is the responsibility of the PoC and is subject to its internal implementation, as well as any policies or regulatory requirements defined by the jurisdiction in which it operates.
In FHIR R5 Subscriptions, the events included in a notification are defined within the SubscriptionStatus.notificationEvent
element. Each event is assigned a unique position within the notification sequence using the integer64
-typed SubscriptionStatus.notificationEvent.eventNumber
element. This numbering system enables subscribers to ensure events are processed in the correct order, preventing gaps or duplicate processing.
Additionally, the SubscriptionStatus.eventsSinceSubscriptionStart
element provides the total count of events that have been triggered for a given Subscription since its creation. This value corresponds to the eventNumber
of the most recent event.
Since the SubscriptionStatus
resource does not exist in FHIR R4, it is implemented in the R5 Backport specification using the R4 Backported R5 SubscriptionStatus, which profiles the Parameters
resource. In this implementation:
eventNumber
element is represented as Parameters.parameter:notificationEvent.part:eventNumber
.eventsSinceSubscriptionStart
element is represented as Parameters.parameter:eventsSinceSubscriptionStart
.Note: In FHIR R5, the notification eventNumber-related elements were represented as integer64
values. However, in the FHIR R5 Backport specification, these elements are represented as strings. While not explicitly documented, this difference is likely due to the absence of the integer64
data type in FHIR R4 and R4B.
As it pertains to the HALO SoFA Content Update topic, PoC vendors are responsible for tracking event numbers to ensure continuity between received events. At a minimum, PoC vendors SHOULD:
eventNumber
of the last processed event for their current Subscription, updating it whenever a new event (or batch of events) is processed.In FHIR R5, when a client creates a Subscription for a given topic, it can specify the maximum number of events it is willing to accept in a single notification using the Subscription.maxCount
element. This provides a mechanism for clients to manage the volume of data received in a single notification, ensuring that event processing remains manageable and aligned with system capabilities. For FHIR R4 implementations, this capability is supported through the Backported R5 Subscription MaxCount
extension, which is applied to the Subscription.channel
element by the R4/B Topic-Based Subscription
profile.
In the context of the SoFA Content Update topic, subscribing PoC systems MAY use this element to limit the volume of events transported in a single notification.
When a SoFA server receives a Subscription with a maxCount
value, it SHALL honor the requested limit by ensuring that no more than the specified number of events are included in a single notification. If there are more events to be delivered than the specified limit allows, the SoFA server SHALL distribute them across multiple successive notifications.
As described in the Subscription Lifecycle section below, Subscriptions to the SoFA Content Update topic are created at the system/instance level. As such, notifications may contain multiple events spanning different users, patients, applications, and launch contexts. Unlike user-specific Subscriptions, these system-wide notifications do not inherently include information identifying the specific app by which the event was generated.
At present, HALO does not define a mechanism for the SoFA to provide this contextual information within individual notifications. As a result, PoCs will not be able to determine which specific app, user, or session triggered a given event. This represents a known limitation that vendors should account for when processing notifications.
Ongoing discussions within the HALO working group are exploring potential approaches to address this gap in a future release. Until then, jurisdictional SoFA providers SHALL assume full responsibility for ensuring that only notifications from trusted and authorized sources are delivered to PoCs. This guarantees that PoCs can trust the validity of received notifications, even if the precise origin within their system is not explicitly provided.
HALO defines requirements for state-changing interactions (i.e., creates and updates) performed by a SMART App against a SoFA server to ensure that Subscription notifications are sent before the interaction is considered complete. The handling of these interactions depends on whether the SoFA server processes notifications synchronously or asynchronously.
If a SoFA implementation sends Subscription notifications as part of the same process that commits the resource change, the SoFA server SHALL NOT return a success response (2XX
) until both of the following conditions are met:
If the notification cannot be sent, the SoFA SHALL roll back the resource change and SHALL return an error response instead of a success status.
If a SoFA implementation uses an asynchronous delivery mechanism (e.g., queuing notifications for later processing), the SoFA server SHALL use the FHIR Asynchronous Interaction Request Pattern to process the request. Specifically:
202 Accepted
HTTP status code.Content-Location
header with a polling URL that the client MAY use to check the status of the request.The SoFA SHALL NOT finalize the resource save until the notification has been sent to the PoC system. If the notification cannot be sent, the SoFA SHALL reject the operation in accordance with the FHIR Asynchronous API specification.
For more detailed information on how FHIR handles asynchronous interactions, see the Asynchronous Request Pattern section of the FHIR R4 specification.
While not officially described in FHIR until the R5 release, Subscription notifications are inherently best effort. In other words, once the SoFA server sends a notification, there is no way to track or guarantee whether the Point of Care (PoC) system has successfully received, processed, or stored the information.
In the SoFA content update notification workflow, HALO ensures that state-changing interactions are not marked as complete until a notification has been sent to the PoC informing it of the change. However, this does not guarantee that the PoC has successfully processed the update—only that the notification was dispatched. Once the notification has been sent, it is the responsibility of the PoC system to handle and process it as needed. Refer to the Processing Notifications section of this page for more detailed guidance.
For further details on notification reliability, refer to the Guaranteed vs. Best-Effort Delivery section of the FHIR Subscriptions R5 specification.
To maximize compatibility across different jurisdictions, SMART applications SHOULD be designed to support both synchronous and asynchronous processing models. Some jurisdictions may implement one approach exclusively, while others may support both, making flexibility a key factor in ensuring broad adoption and easier deployment.
SMART applications MAY use the Prefer
HTTP header to indicate a preference for either synchronous or asynchronous processing. However, whether the SoFA server honors this request is implementation dependent. To accommodate both mechanisms, applications should not assume a specific behavior but instead handle the response dynamically:
If the SoFA server responds with a standard 2XX
success code, the operation has been processed synchronously, and no further action is required.
If the SoFA server responds with 202 Accepted
and a Content-Location header, the operation is being processed asynchronously, and the application must follow the FHIR Asynchronous Interaction Request Pattern to track its completion.
SoFA implementers should carefully weigh the trade-offs between compatibility and performance when selecting an approach for handling Subscription notifications.
The FHIR Subscriptions specification defines notification channels, which represent the mechanisms by which Subscription notifications are delivered to a subscriber. These channels include REST-based webhooks, WebSockets, email, SMS, and other transport mechanisms.
For the purposes of SoFA content update, HALO constrains supported channels to only REST-based webhooks (rest-hook
) and WebSockets (websocket
). When creating a Subscription for this topic, Subscribers MUST set Subscription.channel.type to either rest-hook
or websocket
based on the capabilities of their system.
The other channels are considered out of scope for this release of HALO. Support for additional channels may be considered in future versions of the HALO specification based on community feedback and evolving use cases.
For more detailed information on the various channels supported by FHIR, see the Channels page of the FHIR Subscriptions R5 Backport specification.
Out-of-Scope
During the development of this specification, the HALO team explored the possibility of introducing a custom polling channel within FHIR Subscriptions to provide an alternative mechanism for PoC vendors to retrieve updates. While several use cases were considered, the decision was made to table this approach for now.
Although polling has not been included in the current release, it remains under consideration for future iterations. The HALO team will continue to evaluate its potential based on community feedback and further working sessions to determine if and how it should be incorporated.
The REST-hook notification channel allows event notifications to be delivered via HTTP/S POST requests to a subscriber’s specified endpoint. Clients subscribing with this method set Subscription.channel.type to rest-hook and provide a publicly accessible URL where notifications will be sent. Before activating the subscription, the server performs an initial handshake to verify the endpoint. Once active, the server sends notifications as FHIR Bundles according to the Backported R5 Notification Bundle Profile. For more detailed information, refer to the REST-Hook section of the Channels page within FHIR Subscriptions R5 Backport specification.
Actor | Descriptions |
---|---|
Point-of-Care (PoC) | A local system (e.g., an EMR), used by healthcare professionals to support the delivery of care. |
SMART Apps | Healthcare applications that adhere to the SMART on FHIR specification, allowing them to be launched from within point of care applications and integrate seamlessly to enhance clinical workflows and patient care. |
SMART on FHIR Accelerator (SoFA) | The SMART on FHIR® Accelerator is intended to be used when a PoC system does not have the ability to make a FHIR® server available for access by external systems. Jurisdictions are expected to provide a Context Management System and a FHIR® server to support its use. |
Diagram
Steps
System Startup: The PoC system initializes.
Check for prior Subscription: The PoC checks its records (e.g., its database) to determine if it had an active Subscription before the last shutdown.
Get /Subscription/[id]/$events?eventsSince=[lastEventNumber]: If the PoC had a prior Subscription, it calls the $events
operation on the SoFA to retrieve any missed notifications since it was last active.
Return Bundle of missed events: The SoFA responds with a Parameters
resource containing a Bundle
parameter named return
, which includes all events that occurred since the specified eventNumber
.
Process and store missed events: The PoC processes the received events, applies any relevant data updates, and updates its list of processed event numbers.
GET /Subscription/[id]/$status: The PoC requests the current status of its Subscription from the SoFA by calling the $status
operation.
Return Subscription Status: The SoFA responds with a Parameters
resource containing a Bundle
parameter named return
, which includes the subscription status for the PoC's Subscription.
POST /Subscription (status=requested, type=rest-hook): If no existing Subscription is found, the PoC creates a new one in the requested
state.
PUT /Subscription (status=requested, type=rest-hook): If a Subscription exists but is not active
, the PoC updates it on the SoFA, setting its status to requested
.
Store/Update Subscription: The SoFA creates or updates the Subscription resource in the requested
state based on the PoC’s request.
Return 2xx: The SoFA confirms that the Subscription was successfully created or updated.
Send Handshake notification to PoC (Subscription.endpoint): The SoFA sends a handshake notification to the PoC at the endpoint specified in the Subscription.
Acknowledge (200 OK): The PoC responds with an HTTP 200 OK, confirming its ability to receive notifications as configured.
Update Subscription (status=active): Upon successful handshake confirmation, the SoFA updates the Subscription status to active
, enabling notifications to be sent.
Send heartbeat notification: The SoFA initiates periodic heartbeat notifications to the PoC based on the Subscription.heartbeatPeriod
setting.
Acknowledge (200 OK): The PoC responds with an HTTP 200 OK to acknowledge receipt of each heartbeat notification.
Diagram
Steps
Write FHIR resource: The SMART App performs a write operation (e.g., create
, update
, delete
) on the SoFA FHIR server.
Send notification: Upon processing the write request, the SoFA immediately sends a notification containing the modified resource to the PoC at its Subscription endpoint.
Track eventNumber: The PoC records the eventNumber
value(s) from the received notification.
If the PoC accepts the write:
Store FHIR resource: The PoC processes and stores the resource from the notification.
Acknowledge (200 OK): The PoC responds with an HTTP 200 OK to confirm receipt and processing of the notification.
Store FHIR resource (200 OK): The SoFA stores the resource in its local FHIR server.
Return 2xx: The SoFA confirms successful completion of the request to the SMART App by returning an HTTP 2xx
response.
If the PoC rejects the write:
Discard FHIR resource: The PoC does not store the resource from the notification.
Reject (4xx or 5xx): The PoC returns an appropriate 4xx
or 5xx
response to indicate rejection of the notification.
Discard FHIR resource: The SoFA does not store the resource in its local FHIR server.
Return 4xx or 5xx: The SoFA informs the SMART App that the request was unsuccessful by returning a 4xx
or 5xx
response.
Diagram
Steps
Write FHIR resource: The SMART App performs a write operation (e.g., create
, update
, delete
) on the SoFA FHIR server.
Queue notification: The SoFA schedules the notification for later delivery (e.g., by adding it to a queue).
Return 202 Accepted (Location: [statusUrl]): The SoFA acknowledges the request with a 202 Accepted
response, providing a URL that the SMART App can poll for status updates.
GET [statusUrl]: The SMART App begins polling the provided URL by sending an HTTP GET request.
Return 202 Accepted: If the notification is still pending, the SoFA responds with a 202 Accepted
.
Return 200 or 201: If the notification has been successfully delivered and processed, the SoFA returns a 200
or 201
response.
Return 4xx or 5xx: If the notification processing fails (e.g., due to rejection by the PoC), the SoFA returns an appropriate HTTP 4xx
or 5xx
response.
Send notification: When ready, the SoFA attempts to deliver the notification to the PoC.
Track eventNumber: Upon receiving the notification, the PoC records the eventNumber
for tracking purposes.
If the PoC accepts the write:
Store FHIR resource: The PoC processes and stores the resource from the notification.
Acknowledge (200 OK): The PoC confirms receipt of the notification by returning an HTTP 200 OK
response.
Store FHIR resource: The SoFA stores the resource in its local FHIR server.
If the PoC rejects the write:
Discard FHIR resource: The PoC does not store the resource from the notification.
Reject (4xx or 5xx): The PoC returns an HTTP 4xx
or 5xx
response to indicate rejection.
Discard FHIR resource: The SoFA does not store the resource in its local FHIR server.
In FHIR Subscriptions, the handshake mechanism for REST-hook notifications follows a request-response pattern, ensuring that the subscribing PoC can receive notifications before activating the Subscription.
When a PoC creates a Subscription
resource with a status of requested
, the SoFA MUST initiate the handshake by sending an HTTP request to the endpoint specified in the Subscription. This request SHALL contain a Subscription notification Bundle
with a type
of handshake
.
Upon receiving the handshake request, the PoC MUST respond with an HTTP 200 OK
status code to confirm successful receipt of the notification. If the PoC returns this confirmation, the SoFA SHALL transition the Subscription status to active
, allowing event notifications to be sent.
If the PoC fails to acknowledge the handshake request, the SoFA SHALL NOT activate the Subscription and MAY attempt a retry based on its internal retry policies. The PoC MUST ensure that its designated notification endpoint is reachable and capable of processing handshake requests as expected.
In the rest-hook
channel approach, authentication and authorization can be handled using common HTTP-compatible mechanisms. When creating a Subscription for the rest-hook
channel, the Subscription resource provides a mechanism for the subscriber to supply a list of HTTP header strings in the format "{header}: {value}"
. When sending notifications, the SoFA SHALL include any headers provided in the Subscription.channel.header element, enabling the client application (PoC) to supply bearer tokens and/or cookies for authentication. If the client chooses this approach, it is the client’s responsibility to manage the lifespan of its tokens with the lifespan of the Subscriptions it creates.
Some jurisdictions may require Mutual Transport Layer Security (mTLS) as an alternative or supplementary method to secure the communication of notifications from the SoFA to the PoC. In such cases, certificate management and mutual trust are established out of band, typically as part of the jurisdictional onboarding process for PoC vendors.
The WebSocket notification channel enables event notifications to be delivered over a persistent WebSocket connection, allowing subscribers to receive updates without requiring an outward-facing HTTP server. Clients using this method set Subscription.channel.type to websocket and establish a connection using a binding token obtained through the $get-ws-binding-token operation. Once connected, the client authenticates by sending the token over the WebSocket, after which the server begins sending notifications. Unlike REST-hook notifications, WebSocket clients do not acknowledge receipt of messages. For more detailed information, refer to the WebSockets section of the Channels page within FHIR Subscriptions R5 Backport specification.
Actor | Descriptions |
---|---|
Point-of-Care (PoC) | A local system (e.g., an EMR), used by healthcare professionals to support the delivery of care. |
SMART Apps | Healthcare applications that adhere to the SMART on FHIR specification, allowing them to be launched from within point of care applications and integrate seamlessly to enhance clinical workflows and patient care. |
SMART on FHIR Accelerator (SoFA) | The SMART on FHIR® Accelerator is intended to be used when a PoC system does not have the ability to make a FHIR® server available for access by external systems. Jurisdictions are expected to provide a Context Management System and a FHIR® server to support its use. |
Diagram
Steps
System Startup: The PoC system initializes.
Check for prior Subscription: The PoC checks its records (e.g., its database) to determine if it had an active Subscription before the last shutdown.
Get /Subscription/[id]/$events?eventsSince=[lastEventNumber]: If the PoC had a prior Subscription, it calls the $events
operation on the SoFA to retrieve any missed notifications since it was last active.
Return Bundle of missed events: The SoFA responds with a Parameters
resource containing a Bundle
parameter named return
, which includes all events that occurred since the specified eventNumber
.
Process and store missed events: The PoC processes the received events, applies any relevant data updates, and updates its list of processed event numbers.
GET /Subscription/[id]/$status: The PoC requests the current status of its Subscription from the SoFA by calling the $status
operation.
Return Subscription Status: The SoFA responds with a Parameters
resource containing a Bundle
parameter named return
, which includes the subscription status for the PoC's Subscription.
POST /Subscription (status=requested, type=websocket): If no existing Subscription is found, the PoC creates a new one in the requested
state.
PUT /Subscription (status=requested, type=websocket): If a Subscription exists but is not active
, the PoC updates it on the SoFA, setting its status to requested
.
Store/Update Subscription: The SoFA creates or updates the Subscription resource in the requested
state based on the PoC’s request.
Return 2xx: The SoFA confirms that the Subscription was successfully created or updated.
POST /Subscription/[id]/$get-ws-binding-token: The PoC calls the $get-ws-binding-token
operation to obtain a WebSocket binding token.
Return 200 OK (token, expiry, WebSocket endpoint, etc.): The SoFA responds with the WebSocket binding token, its expiration time, and the WebSocket connection endpoint.
Connect to WebSocket endpoint: The PoC establishes a WebSocket connection to the provided endpoint.
Send “bind-with-token: [token]” message: The PoC sends the binding token over the WebSocket to authenticate and establish the connection.
Validate token: The SoFA verifies the token and retrieves the associated Subscription.
Send handshake notification for requested Subscription: The SoFA sends a handshake notification over the WebSocket to confirm the Subscription.
Update Subscription (status=active): The SoFA updates the Subscription status from requested
to active
, enabling notifications.
Send heartbeat notification: The SoFA periodically sends heartbeat notifications to the PoC at intervals specified in the Subscription.heartbeatPeriod
element.
Diagram
Steps
Write FHIR resource: The SMART App performs a write operation (e.g., create
, update
, delete
) on the SoFA FHIR server.
Send notification: Upon receiving the request, the SoFA immediately pushes a notification with the updated resource to the PoC over the WebSocket connection.
Track eventNumber: The PoC records the eventNumber
of the received notification.
Store FHIR resource: The PoC processes the notification and updates its records with the new or modified FHIR resource.
Store FHIR resource: Since WebSockets do not require explicit acknowledgments, the SoFA assumes the PoC has received the notification and commits the FHIR resource.
Return 2xx Created: The SoFA confirms the operation's success by returning a 2xx
status code to the SMART App.
Diagram
Steps
Write FHIR resource: The SMART App performs a write operation (e.g., create
, update
, delete
) on the SoFA FHIR server.
Queue notification: The SoFA schedules the notification for later delivery.
Return 202 Accepted (Location: [statusUrl]): The SoFA acknowledges the request by returning a 202 Accepted
status code and provides a statusUrl
for the SMART App to check its progress within the Location
header of the response.
GET [statusUrl]: The SMART App starts polling the provided URL to track the notification’s status.
Return 202 Accepted: If the notification is still pending, the SoFA returns a 202 Accepted
response.
Return 200 or 201: Once the notification has been delivered and the resource stored, the SoFA responds with 200 OK
or 201 Created
.
Return 4xx or 5xx: If an error occurs (e.g., the PoC rejects the notification), the SoFA returns an appropriate 4xx
or 5xx
error code.
Send notification: The SoFA eventually delivers the notification to the PoC over the WebSocket connection.
Store FHIR resource: Since WebSockets do not require explicit acknowledgments, the SoFA assumes the PoC has received the notification and commits the FHIR resource.
Track eventNumber: Upon receiving the notification, the PoC logs the associated eventNumber
.
Store FHIR resource: The PoC processes the notification and updates its records with the new or modified FHIR resource.
Unlike REST-based interactions, WebSockets do not support standard authentication mechanisms such as Authorization headers. To address this, FHIR defines a token-based handshake process using the $get-ws-binding-token
operation.
To initiate a WebSocket connection, the PoC MUST first invoke $get-ws-binding-token
on the SoFA, specifying the target Subscription ID.
SoFA vendors MUST support $get-ws-binding-token
at the instance level and enforce access control for both token requests and bind-with-token
messages, ensuring PoCs can only bind to their own Subscriptions.
In response to the $get-ws-binding-token
operation, the SoFA SHALL return a response containing a binding token that is valid for the requested Subscription, an expiration timestamp for the token, and the WebSocket URL.
The PoC MUST establish a WebSocket connection using the exact URL provided. Upon connection, it SHALL send a binding message in the format: bind-with-token: <token>
.
The SoFA SHALL handle the bind-with-token
message by performing the necessary token validation and then completing the handshake by sending a single handshake notification back to the client over the socket.
Subscription |
id : sofa-content-update-rest-hook-subscription-example |
meta |
profile : http://hl7.org/fhir/uv/subscriptions-backport/StructureDefinition/backport-subscription |
status : active |
reason : Subscription to receive updates to the SoFA content |
criteria : http://fhir.infoway-inforoute.ca/io/HALO/SubscriptionTopic/sofa-content-update |
channel |
extension |
url : http://hl7.org/fhir/uv/subscriptions-backport/StructureDefinition/backport-heartbeat-period |
value : 86400 |
extension |
url : http://hl7.org/fhir/uv/subscriptions-backport/StructureDefinition/backport-timeout |
value : 60 |
type : rest-hook |
endpoint : https://poc.example.com/notification-callback-endpoint |
payload : application/fhir+json |
extension |
url : http://hl7.org/fhir/uv/subscriptions-backport/StructureDefinition/backport-payload-content |
value : full-resource |
header : Authorization: Bearer secret-token-abc-123 |
{ "resourceType": "Subscription", "id": "sofa-content-update-rest-hook-subscription-example", "meta": { "profile": [ "http://hl7.org/fhir/uv/subscriptions-backport/StructureDefinition/backport-subscription" ] }, "status": "active", "reason": "Subscription to receive updates to the SoFA content", "criteria": "http://fhir.infoway-inforoute.ca/io/HALO/SubscriptionTopic/sofa-content-update", "channel": { "extension": [ { "url": "http://hl7.org/fhir/uv/subscriptions-backport/StructureDefinition/backport-heartbeat-period", "valueUnsignedInt": 86400 }, { "url": "http://hl7.org/fhir/uv/subscriptions-backport/StructureDefinition/backport-timeout", "valueUnsignedInt": 60 } ], "type": "rest-hook", "endpoint": "https://poc.example.com/notification-callback-endpoint", "payload": "application/fhir+json", "_payload": { "extension": [ { "url": "http://hl7.org/fhir/uv/subscriptions-backport/StructureDefinition/backport-payload-content", "valueCode": "full-resource" } ] }, "header": [ "Authorization: Bearer secret-token-abc-123" ] } }
<Subscription xmlns="http://hl7.org/fhir"> <id value="sofa-content-update-rest-hook-subscription-example" /> <meta> <profile value="http://hl7.org/fhir/uv/subscriptions-backport/StructureDefinition/backport-subscription" /> </meta> <status value="active" /> <reason value="Subscription to receive updates to the SoFA content" /> <criteria value="http://fhir.infoway-inforoute.ca/io/HALO/SubscriptionTopic/sofa-content-update" /> <channel> <extension url="http://hl7.org/fhir/uv/subscriptions-backport/StructureDefinition/backport-heartbeat-period"> <valueUnsignedInt value="86400" /> </extension> <extension url="http://hl7.org/fhir/uv/subscriptions-backport/StructureDefinition/backport-timeout"> <valueUnsignedInt value="60" /> </extension> <type value="rest-hook" /> <endpoint value="https://poc.example.com/notification-callback-endpoint" /> <payload value="application/fhir+json"> <extension url="http://hl7.org/fhir/uv/subscriptions-backport/StructureDefinition/backport-payload-content"> <valueCode value="full-resource" /> </extension> </payload> <header value="Authorization: Bearer secret-token-abc-123" /> </channel> </Subscription>
Subscription |
id : sofa-content-update-websocket-subscription-example |
meta |
profile : http://hl7.org/fhir/uv/subscriptions-backport/StructureDefinition/backport-subscription |
status : active |
reason : Subscription to receive updates to the SoFA content |
criteria : http://fhir.infoway-inforoute.ca/io/HALO/SubscriptionTopic/sofa-content-update |
channel |
extension |
url : http://hl7.org/fhir/uv/subscriptions-backport/StructureDefinition/backport-heartbeat-period |
value : 86400 |
extension |
url : http://hl7.org/fhir/uv/subscriptions-backport/StructureDefinition/backport-timeout |
value : 60 |
type : websocket |
endpoint : wss://poc.example.com/notification-socket-endpoint |
payload : application/fhir+json |
extension |
url : http://hl7.org/fhir/uv/subscriptions-backport/StructureDefinition/backport-payload-content |
value : full-resource |
{ "resourceType": "Subscription", "id": "sofa-content-update-websocket-subscription-example", "meta": { "profile": [ "http://hl7.org/fhir/uv/subscriptions-backport/StructureDefinition/backport-subscription" ] }, "status": "active", "reason": "Subscription to receive updates to the SoFA content", "criteria": "http://fhir.infoway-inforoute.ca/io/HALO/SubscriptionTopic/sofa-content-update", "channel": { "extension": [ { "url": "http://hl7.org/fhir/uv/subscriptions-backport/StructureDefinition/backport-heartbeat-period", "valueUnsignedInt": 86400 }, { "url": "http://hl7.org/fhir/uv/subscriptions-backport/StructureDefinition/backport-timeout", "valueUnsignedInt": 60 } ], "type": "websocket", "endpoint": "wss://poc.example.com/notification-socket-endpoint", "payload": "application/fhir+json", "_payload": { "extension": [ { "url": "http://hl7.org/fhir/uv/subscriptions-backport/StructureDefinition/backport-payload-content", "valueCode": "full-resource" } ] } } }
<Subscription xmlns="http://hl7.org/fhir"> <id value="sofa-content-update-websocket-subscription-example" /> <meta> <profile value="http://hl7.org/fhir/uv/subscriptions-backport/StructureDefinition/backport-subscription" /> </meta> <status value="active" /> <reason value="Subscription to receive updates to the SoFA content" /> <criteria value="http://fhir.infoway-inforoute.ca/io/HALO/SubscriptionTopic/sofa-content-update" /> <channel> <extension url="http://hl7.org/fhir/uv/subscriptions-backport/StructureDefinition/backport-heartbeat-period"> <valueUnsignedInt value="86400" /> </extension> <extension url="http://hl7.org/fhir/uv/subscriptions-backport/StructureDefinition/backport-timeout"> <valueUnsignedInt value="60" /> </extension> <type value="websocket" /> <endpoint value="wss://poc.example.com/notification-socket-endpoint" /> <payload value="application/fhir+json"> <extension url="http://hl7.org/fhir/uv/subscriptions-backport/StructureDefinition/backport-payload-content"> <valueCode value="full-resource" /> </extension> </payload> </channel> </Subscription>
Bundle |
id : sofa-content-update-subscription-notification-example |
meta |
profile : http://hl7.org/fhir/uv/subscriptions-backport/StructureDefinition/backport-subscription-notification |
type : history |
entry |
fullUrl : https://sofa.example.com/fhir/SubscriptionStatus/sofa-content-update-subscription-status-example |
resource |
id : sofa-content-update-subscription-status-example |
meta |
profile : http://hl7.org/fhir/uv/subscriptions-backport/StructureDefinition/backport-subscription-status-r4 |
parameter |
name : subscription |
value |
reference : Susbcription/sofa-content-update-rest-hook-subscription-example |
parameter |
name : topic |
value : http://fhir.infoway-inforoute.ca/io/HALO/SubscriptionTopic/sofa-content-update |
parameter |
name : status |
value : active |
parameter |
name : type |
value : event-notification |
parameter |
name : events-since-subscription-start |
value : 1 |
parameter |
name : notification-event |
part |
name : event-number |
value : 1 |
part |
name : focus |
value |
reference : Observation/ce87b61b-7b93-4d69-9d23-524dd5badc15 |
type : Observation |
part |
name : timestamp |
value : 2025-03-21T12:00:00+00:00 |
entry |
fullUrl : https://sofa.example.com/fhir/Observation/ce87b61b-7b93-4d69-9d23-524dd5badc15 |
resource |
id : ce87b61b-7b93-4d69-9d23-524dd5badc15 |
status : preliminary |
code |
coding |
system : http://loinc.org |
code : 8310-5 |
display : Body temperature |
effective : 2025-03-21T12:00:00Z |
value |
value : 37.1 |
unit : C |
system : http://unitsofmeasure.org |
code : Cel |
request |
method : POST |
url : Observation |
{ "resourceType": "Bundle", "id": "sofa-content-update-subscription-notification-example", "meta": { "profile": [ "http://hl7.org/fhir/uv/subscriptions-backport/StructureDefinition/backport-subscription-notification" ] }, "type": "history", "entry": [ { "fullUrl": "https://sofa.example.com/fhir/SubscriptionStatus/sofa-content-update-subscription-status-example", "resource": { "resourceType": "Parameters", "id": "sofa-content-update-subscription-status-example", "meta": { "profile": [ "http://hl7.org/fhir/uv/subscriptions-backport/StructureDefinition/backport-subscription-status-r4" ] }, "parameter": [ { "name": "subscription", "valueReference": { "reference": "Susbcription/sofa-content-update-rest-hook-subscription-example" } }, { "name": "topic", "valueCanonical": "http://fhir.infoway-inforoute.ca/io/HALO/SubscriptionTopic/sofa-content-update" }, { "name": "status", "valueCode": "active" }, { "name": "type", "valueCode": "event-notification" }, { "name": "events-since-subscription-start", "valueString": "1" }, { "name": "notification-event", "part": [ { "name": "event-number", "valueString": "1" }, { "name": "focus", "valueReference": { "reference": "Observation/ce87b61b-7b93-4d69-9d23-524dd5badc15", "type": "Observation" } }, { "name": "timestamp", "valueInstant": "03/21/2025 12:00:00" } ] } ] } }, { "fullUrl": "https://sofa.example.com/fhir/Observation/ce87b61b-7b93-4d69-9d23-524dd5badc15", "resource": { "resourceType": "Observation", "id": "ce87b61b-7b93-4d69-9d23-524dd5badc15", "status": "preliminary", "code": { "coding": [ { "system": "http://loinc.org", "code": "8310-5", "display": "Body temperature" } ] }, "effectiveDateTime": "03/21/2025 12:00:00", "valueQuantity": { "value": 37.1, "unit": "C", "system": "http://unitsofmeasure.org", "code": "Cel" } }, "request": { "method": "POST", "url": "Observation" } } ] }
<Bundle xmlns="http://hl7.org/fhir"> <id value="sofa-content-update-subscription-notification-example" /> <meta> <profile value="http://hl7.org/fhir/uv/subscriptions-backport/StructureDefinition/backport-subscription-notification" /> </meta> <type value="history" /> <entry> <fullUrl value="https://sofa.example.com/fhir/SubscriptionStatus/sofa-content-update-subscription-status-example" /> <resource> <Parameters> <id value="sofa-content-update-subscription-status-example" /> <meta> <profile value="http://hl7.org/fhir/uv/subscriptions-backport/StructureDefinition/backport-subscription-status-r4" /> </meta> <parameter> <name value="subscription" /> <valueReference> <reference value="Susbcription/sofa-content-update-rest-hook-subscription-example" /> </valueReference> </parameter> <parameter> <name value="topic" /> <valueCanonical value="http://fhir.infoway-inforoute.ca/io/HALO/SubscriptionTopic/sofa-content-update" /> </parameter> <parameter> <name value="status" /> <valueCode value="active" /> </parameter> <parameter> <name value="type" /> <valueCode value="event-notification" /> </parameter> <parameter> <name value="events-since-subscription-start" /> <valueString value="1" /> </parameter> <parameter> <name value="notification-event" /> <part> <name value="event-number" /> <valueString value="1" /> </part> <part> <name value="focus" /> <valueReference> <reference value="Observation/ce87b61b-7b93-4d69-9d23-524dd5badc15" /> <type value="Observation" /> </valueReference> </part> <part> <name value="timestamp" /> <valueInstant value="2025-03-21T12:00:00+00:00" /> </part> </parameter> </Parameters> </resource> </entry> <entry> <fullUrl value="https://sofa.example.com/fhir/Observation/ce87b61b-7b93-4d69-9d23-524dd5badc15" /> <resource> <Observation> <id value="ce87b61b-7b93-4d69-9d23-524dd5badc15" /> <status value="preliminary" /> <code> <coding> <system value="http://loinc.org" /> <code value="8310-5" /> <display value="Body temperature" /> </coding> </code> <effectiveDateTime value="2025-03-21T12:00:00Z" /> <valueQuantity> <value value="37.1" /> <unit value="C" /> <system value="http://unitsofmeasure.org" /> <code value="Cel" /> </valueQuantity> </Observation> </resource> <request> <method value="POST" /> <url value="Observation" /> </request> </entry> </Bundle>