Subscriptions

This IG page includes summarized guidance derived from the base SMART App Launch v2.1.0 and Subscriptions R5 Backport v1.2.0-ballot specifications. Please note that this guidance is intended to be informative and provide a simplified overview of the information contained in the base specification. In the event of any discrepancies or conflicts between the summary presented here and the relevant base specification, the base specification shall prevail. Users are encouraged to refer to the base specifications for detailed and complete guidance.

Overview

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:

  • Existing resources registered via $set-context are modified, ensuring the PoC remains aware of updates relevant to patient care.
  • New resources are created within the SoFA, enabling the PoC to capture additional clinical data introduced by SMART apps.

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.

FHIR Subscriptions R5 Backport

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.

Subscription Topic: SoFA Content Update

R5 Backport Subscription Topic Definitions

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).

Topic Definition

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.

Subscription Requirements for Launch

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.

Topic Discovery

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.

The Subscription Lifecycle

Subscription Creation

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

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.

The Heartbeat

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.

End of Life (EOL)

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.

Recovery

Missed Notifications

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,

  1. 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.

  2. 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.

  3. 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.

Error Recovery

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.

Notifications & Events

Event Triggers

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.

Notification Bundle Format

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.

Notification Payload Types

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:

  • empty: No resource content is included in the notification payload.
  • id-only: Only the resource ID is included in the notification payload.
  • full-resource: The entire resource is included in the notification payload.

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.

Processing Notifications

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.

Event ID Tracking

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:

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:

  • Use event numbers to determine the correct processing order.
  • Store the eventNumber of the last processed event for their current Subscription, updating it whenever a new event (or batch of events) is processed.
  • Upon receiving notifications, compare the last known event number with the event numbers in newly received notifications to verify that no events are missing and that no events processed more than once.

Event Paging

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.

Gap: Event Origin

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.

Asynchronous SoFA Interactions

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.

Synchronous Notifications (Standard Flow)

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:

  1. The resource has been successfully stored in the SoFA.
  2. A Subscription notification has been sent to the PoC system.

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.

Asynchronous Notifications (FHIR Async API)

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:

  • The SoFA server SHALL return a 202 Accepted HTTP status code.
  • The response SHALL include a 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.

Best-Effort Notifications

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.

Expectations for the SMART Applications

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 Considerations: Compatibility vs. Performance

SoFA implementers should carefully weigh the trade-offs between compatibility and performance when selecting an approach for handling Subscription notifications.

  • The synchronous approach aligns with common FHIR client expectations, which may improve adoption and interoperability. However, because notifications are sent and processed immediately, this approach can introduce performance overhead on the SoFA server, particularly in high-traffic jurisdictions.
  • The asynchronous approach enables greater scalability and performance optimization by decoupling notification processing from resource updates. However, fewer applications may support this model out of the box, potentially requiring client-side modifications before deployment, and/or limiting the number of SMART applications that are available to SoFA-enabled vendors.

Subscription Channels

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.

Polling 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.

REST-Hooks

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.

Actors

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.

Creating a REST Hook Subscription

Diagram

Steps

  1. System Startup: The PoC system initializes.

  2. 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.

  3. 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.

  4. 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.

  5. Process and store missed events: The PoC processes the received events, applies any relevant data updates, and updates its list of processed event numbers.

  6. GET /Subscription/[id]/$status: The PoC requests the current status of its Subscription from the SoFA by calling the $status operation.

  7. 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.

  8. POST /Subscription (status=requested, type=rest-hook): If no existing Subscription is found, the PoC creates a new one in the requested state.

  9. 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.

  10. Store/Update Subscription: The SoFA creates or updates the Subscription resource in the requested state based on the PoC’s request.

  11. Return 2xx: The SoFA confirms that the Subscription was successfully created or updated.

  12. Send Handshake notification to PoC (Subscription.endpoint): The SoFA sends a handshake notification to the PoC at the endpoint specified in the Subscription.

  13. Acknowledge (200 OK): The PoC responds with an HTTP 200 OK, confirming its ability to receive notifications as configured.

  14. Update Subscription (status=active): Upon successful handshake confirmation, the SoFA updates the Subscription status to active, enabling notifications to be sent.

  15. Send heartbeat notification: The SoFA initiates periodic heartbeat notifications to the PoC based on the Subscription.heartbeatPeriod setting.

  16. Acknowledge (200 OK): The PoC responds with an HTTP 200 OK to acknowledge receipt of each heartbeat notification.

Receiving Notifications via the REST Hook Channel (Synchronous Delivery)

Diagram

Steps

  1. Write FHIR resource: The SMART App performs a write operation (e.g., create, update, delete) on the SoFA FHIR server.

  2. Send notification: Upon processing the write request, the SoFA immediately sends a notification containing the modified resource to the PoC at its Subscription endpoint.

  3. Track eventNumber: The PoC records the eventNumber value(s) from the received notification.

If the PoC accepts the write:

  1. Store FHIR resource: The PoC processes and stores the resource from the notification.

  2. Acknowledge (200 OK): The PoC responds with an HTTP 200 OK to confirm receipt and processing of the notification.

  3. Store FHIR resource (200 OK): The SoFA stores the resource in its local FHIR server.

  4. 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:

  1. Discard FHIR resource: The PoC does not store the resource from the notification.

  2. Reject (4xx or 5xx): The PoC returns an appropriate 4xx or 5xx response to indicate rejection of the notification.

  3. Discard FHIR resource: The SoFA does not store the resource in its local FHIR server.

  4. Return 4xx or 5xx: The SoFA informs the SMART App that the request was unsuccessful by returning a 4xx or 5xx response.

Receiving Notifications via the REST Hook Channel (Asynchronous Delivery)

Diagram

Steps

  1. Write FHIR resource: The SMART App performs a write operation (e.g., create, update, delete) on the SoFA FHIR server.

  2. Queue notification: The SoFA schedules the notification for later delivery (e.g., by adding it to a queue).

  3. 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.

  4. GET [statusUrl]: The SMART App begins polling the provided URL by sending an HTTP GET request.

  5. Return 202 Accepted: If the notification is still pending, the SoFA responds with a 202 Accepted.

  6. Return 200 or 201: If the notification has been successfully delivered and processed, the SoFA returns a 200 or 201 response.

  7. 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.

  8. Send notification: When ready, the SoFA attempts to deliver the notification to the PoC.

  9. Track eventNumber: Upon receiving the notification, the PoC records the eventNumber for tracking purposes.

If the PoC accepts the write:

  1. Store FHIR resource: The PoC processes and stores the resource from the notification.

  2. Acknowledge (200 OK): The PoC confirms receipt of the notification by returning an HTTP 200 OK response.

  3. Store FHIR resource: The SoFA stores the resource in its local FHIR server.

If the PoC rejects the write:

  1. Discard FHIR resource: The PoC does not store the resource from the notification.

  2. Reject (4xx or 5xx): The PoC returns an HTTP 4xx or 5xx response to indicate rejection.

  3. Discard FHIR resource: The SoFA does not store the resource in its local FHIR server.

The Handshake Mechanism for REST Hooks

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.

Security Considerations for REST Hooks

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.

WebSockets

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.

Actors

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.

Creating a WebSocket Subscription

Diagram

Steps

  1. System Startup: The PoC system initializes.

  2. 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.

  3. 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.

  4. 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.

  5. Process and store missed events: The PoC processes the received events, applies any relevant data updates, and updates its list of processed event numbers.

  6. GET /Subscription/[id]/$status: The PoC requests the current status of its Subscription from the SoFA by calling the $status operation.

  7. 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.

  8. POST /Subscription (status=requested, type=websocket): If no existing Subscription is found, the PoC creates a new one in the requested state.

  9. 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.

  10. Store/Update Subscription: The SoFA creates or updates the Subscription resource in the requested state based on the PoC’s request.

  11. Return 2xx: The SoFA confirms that the Subscription was successfully created or updated.

  12. POST /Subscription/[id]/$get-ws-binding-token: The PoC calls the $get-ws-binding-token operation to obtain a WebSocket binding token.

  13. Return 200 OK (token, expiry, WebSocket endpoint, etc.): The SoFA responds with the WebSocket binding token, its expiration time, and the WebSocket connection endpoint.

  14. Connect to WebSocket endpoint: The PoC establishes a WebSocket connection to the provided endpoint.

  15. Send “bind-with-token: [token]” message: The PoC sends the binding token over the WebSocket to authenticate and establish the connection.

  16. Validate token: The SoFA verifies the token and retrieves the associated Subscription.

  17. Send handshake notification for requested Subscription: The SoFA sends a handshake notification over the WebSocket to confirm the Subscription.

  18. Update Subscription (status=active): The SoFA updates the Subscription status from requested to active, enabling notifications.

  19. Send heartbeat notification: The SoFA periodically sends heartbeat notifications to the PoC at intervals specified in the Subscription.heartbeatPeriod element.

Receiving Notifications via the WebSocket Channel (Synchronous Delivery)

Diagram

Steps

  1. Write FHIR resource: The SMART App performs a write operation (e.g., create, update, delete) on the SoFA FHIR server.

  2. Send notification: Upon receiving the request, the SoFA immediately pushes a notification with the updated resource to the PoC over the WebSocket connection.

  3. Track eventNumber: The PoC records the eventNumber of the received notification.

  4. Store FHIR resource: The PoC processes the notification and updates its records with the new or modified FHIR resource.

  5. Store FHIR resource: Since WebSockets do not require explicit acknowledgments, the SoFA assumes the PoC has received the notification and commits the FHIR resource.

  6. Return 2xx Created: The SoFA confirms the operation's success by returning a 2xx status code to the SMART App.

Receiving Notifications via the WebSocket Channel (Asynchronous Delivery)

Diagram

Steps

  1. Write FHIR resource: The SMART App performs a write operation (e.g., create, update, delete) on the SoFA FHIR server.

  2. Queue notification: The SoFA schedules the notification for later delivery.

  3. 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.

  4. GET [statusUrl]: The SMART App starts polling the provided URL to track the notification’s status.

  5. Return 202 Accepted: If the notification is still pending, the SoFA returns a 202 Accepted response.

  6. Return 200 or 201: Once the notification has been delivered and the resource stored, the SoFA responds with 200 OK or 201 Created.

  7. 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.

  8. Send notification: The SoFA eventually delivers the notification to the PoC over the WebSocket connection.

  9. Store FHIR resource: Since WebSockets do not require explicit acknowledgments, the SoFA assumes the PoC has received the notification and commits the FHIR resource.

  10. Track eventNumber: Upon receiving the notification, the PoC logs the associated eventNumber.

  11. Store FHIR resource: The PoC processes the notification and updates its records with the new or modified FHIR resource.

The Handshake Mechanism for WebSockets

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.

Examples

REST Hook Subscription Example

Subscription
{
    "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>

WebSocket Subscription Example

Subscription
{
    "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>

Notification Bundle Example

Bundle
{
    "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>