FHIR Topic-Based Subscriptions
FHIR Topic-Based Subscriptions support for standard FHIR R4/R5 subscription interoperability.
This functionality requires FHIR Schema validation engine to be enabled. R4-backported subscriptions are available in Aidbox version 2512 or later.
R5 and R4B-backported subscriptions require Aidbox version 2601 or later.
Overview
Aidbox provides full support for the FHIR Topic-Based Subscriptions Framework
according to the specification. This allows external systems to create and manage FHIR Subscription resources to receive notifications about data changes.
When to use FHIR Subscriptions
Use FHIR Topic-Based Subscriptions when you:
- Need conformance to the FHIR subscription specification
- Need to provide the ability for external systems or customers to create their own subscriptions
- Expect many subscribers (tens to thousands) with different filters to the same topic
When to use Aidbox Topic-Based Subscriptions instead
For simple subscriptions used for internal integrations, Aidbox Topic-Based Subscriptions (Kafka, Webhook, GCP Pub/Sub, etc.) may work better as they require less setup and are managed server-side. |
Key Components
FHIR Topic-Based Subscriptions build on the Aidbox Topic-Based Subscriptions infrastructure, using the same core resources with additional configuration for FHIR-standard subscription support.
SubscriptionTopic
resource is modeled as two Aidbox resources: AidboxSubscriptionTopic and AidboxTopicDestination.
- 1.
AidboxSubscriptionTopic- implements the topic's triggers and filters - 2.
AidboxTopicDestination- binds it to the canonical FHIR SubscriptionTopic URL
Once configured, clients can create standard FHIR Subscription resource instances referencing the topic URL.
AidboxSubscriptionTopic
AidboxSubscriptionTopic is a custom Aidbox resource that models the FHIR SubscriptionTopic resource.
Why a custom resource? FHIR SubscriptionTopic is not always fully computable — it often uses codes to describe event triggers that require human interpretation. Aidbox uses FHIRPath expressions to make triggers computable.
For example, the DaVinci PAS SubscriptionTopic defines an event trigger using a code:
"eventTrigger": [{
"description": "When a new result is made ready by the intermediary system",
"event": {
"coding": [{
"system": "http://hl7.org/fhir/us/davinci-pas/CodeSystem/PASTempCodes",
"code": "result-available"
}]
},
"resource": "http://hl7.org/fhir/StructureDefinition/ClaimResponse"
}]
In Aidbox, you would model this with a computable FHIRPath expression:
{
"resourceType": "AidboxSubscriptionTopic",
"url": "http://hl7.org/fhir/us/davinci-pas/SubscriptionTopic/PASSubscriptionTopic",
"status": "active",
"trigger": [{
"resource": "ClaimResponse",
"fhirPathCriteria": "status = 'active'"
}]
}
What "result-available" means may vary from installation to installation. The FHIRPath expression should match your specific business logic.
canFilterBy
The canFilterBy element in AidboxSubscriptionTopic defines what filter parameters subscribers can use when creating their subscriptions. This allows subscribers to receive only events that match their specific criteria.
| Field | Description |
|---|---|
filterParameter | Name of the filter parameter that subscribers will use |
filterDefinitionFhirPathExpression | FHIRPath expression to extract the filter value from the resource |
resource | Resource type this filter applies to |
comparator | Comparison operators allowed for this filter. Currently only eq (equals) is supported. |
description | Human-readable description of the filter |
{
"resourceType": "AidboxSubscriptionTopic",
"url": "http://example.org/SubscriptionTopic/claim-response-topic",
"status": "active",
"trigger": [{
"resource": "ClaimResponse",
"fhirPathCriteria": "status = 'active'"
}],
"canFilterBy": [{
"description": "Filter by insurer organization",
"resource": "ClaimResponse",
"filterParameter": "insurer",
"filterDefinitionFhirPathExpression": "insurer",
"comparator": ["eq"]
}]
}
Subscribers can then specify filter criteria in their Subscription resource to receive only events matching their filters (e.g., only ClaimResponses for a specific insurer).
AidboxTopicDestination
AidboxTopicDestination enables an AidboxSubscriptionTopic to be available for FHIR Subscriptions. It acts as a bridge between the topic definition and the FHIR Subscription API.
For FHIR Subscriptions, use the fhir-native-topic-based-subscription kind with the following parameters:
| Parameter | Type | Description |
|---|---|---|
fhir-topic | valueCanonical | Canonical URL of the FHIR SubscriptionTopic this destination implements. |
subscription-specification-version | valueString | FHIR version of the Subscription spec to support. This value determines the format of notification bundles, $status and $events operation responses. Supported values: R5, R4-backported, R4B-backported. |
keep-events-for-period | valueUnsignedInt | How long events remain available for the $events operation (in seconds). If not specified, events are stored indefinitely. |
number-of-deliverer | valueUnsignedInt | Number of parallel delivery workers (default: 4). |
{
"resourceType": "AidboxTopicDestination",
"meta": {
"profile": ["http://aidbox.app/StructureDefinition/aidboxtopicdestination-fhir-native-topic-based-subscription"]
},
"kind": "fhir-native-topic-based-subscription",
"topic": "http://example.org/SubscriptionTopic/claim-response-topic",
"parameter": [
{
"name": "fhir-topic",
"valueUrl": "http://hl7.org/fhir/us/davinci-pas/SubscriptionTopic/PASSubscriptionTopic"
},
{
"name": "subscription-specification-version",
"valueString": "R4-backported"
},
{
"name": "keep-events-for-period",
"valueInteger": 86400
},
{
"name": "number-of-deliverer",
"valueInteger": 2
}
]
}
FHIR Subscription
Once the topic and destination are configured, clients can create standard FHIR Subscription resources to subscribe to events.
Supported Fields
| Field | R4 Backport | R5 | Description |
|---|---|---|---|
| Topic | criteria | topic | Canonical URL of the SubscriptionTopic |
| Filter criteria | backport-filter-criteria | filterBy | Filter events using parameters defined in canFilterBy |
| Expiration | end | end | DateTime when subscription expires and stops receiving events |
| Channel type | channel.type | channelType | Only rest-hook is supported |
| Endpoint | channel.endpoint | endpoint | URL to receive notifications |
| Content type | channel.payload | contentType | MIME type for notification payload (e.g., application/fhir+json) |
| Content level | backport-payload-content | content | Payload detail: empty, id-only, or full-resource |
| Headers | channel.header | parameter | Custom HTTP headers sent with notifications |
| Heartbeat | backport-heartbeat-period | heartbeatPeriod | Interval in seconds for keep-alive notifications during inactivity |
| Max count | backport-max-count | maxCount | Max events per notification bundle before sending |
| Timeout | backport-timeout | timeout | Max seconds to wait for endpoint response before failing delivery |
Subscription Lifecycle
- 1.
Requested: Client creates a Subscription resource with
status: requested. The subscription is not yet active. - 2.
Handshake: Server immediately sends a handshake notification to the configured endpoint to validate it can receive notifications. The handshake bundle contains a
SubscriptionStatuswithtype: handshake. - 3.
Active: If the endpoint responds with HTTP 2xx, the subscription status changes to
activeand starts receiving events. If handshake fails, status changes toerror. - 4.
Event notifications: When resources matching the topic trigger criteria are created/updated/deleted, notification bundles are sent to the endpoint containing the
SubscriptionStatusand triggered resources. Events are batched up tomax-countbefore sending, or sent earlier if heartbeat period is reached. - 5.
Heartbeat: If configured via
backport-heartbeat-period, periodic empty notifications are sent during inactivity to confirm the connection is alive. - 6.
Expiration/Off: Subscription stops receiving events when:
- The
enddate is reached - The subscription is deleted
- The
Operations
Only instance-level operations are supported (not the batch/server-level variants).
$status
Returns the current status of a subscription including event counts. Response format depends on the subscription-specification-version configured in AidboxTopicDestination.
URL: GET|POST [base]/Subscription/[id]/$status
Parameters: None
Response: Bundle with SubscriptionStatus containing:
status- current subscription status (active,error)events-since-subscription-start- total event counterror- error details if delivery failed
| Version | Specification |
|---|---|
R5 | Subscription $status |
R4-backported | Subscription Status Operation |
$events
Allows clients to query past events for recovery purposes. Response format depends on the subscription-specification-version configured in AidboxTopicDestination.
URL: POST [base]/Subscription/[id]/$events
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
eventsSinceNumber | string | eventsUntilNumber - 100 | Start of event range (inclusive) |
eventsUntilNumber | string | Last event number | End of event range (inclusive) |
content | code | Subscription's content level | full-resource, id-only, or empty |
Defaults: If no parameters provided, returns up to 100 most recent events.
| Version | Specification |
|---|---|
R5 | Subscription $events |
R4-backported | Subscription Events Operation |
Notification Bundle
The notification bundle format depends on the subscription-specification-version configured in AidboxTopicDestination.
| Version | Specification |
|---|---|
R5 | SubscriptionStatus , Notification Bundle |
R4B-backported | SubscriptionStatus |
R4-backported | Notification Bundle Example , All Artifacts |
Internals
This section describes internal implementation details useful for debugging and operations.
Database Tables
Events and delivery statuses are stored in the tds schema:
| Table | Description |
|---|---|
tds.fhir_subscription_topic_event | Stores triggered events. Events are inserted within the same database transaction that processes the FHIR request, ensuring consistency. |
tds.fhir_subscription_delivery_status | Stores delivery facts for each event-subscription pair. A record is created after delivery attempt with error_code set to NULL on success or error type on failure. |
Event Processing
- 1.
When a FHIR resource is created/updated/deleted and matches a topic's trigger criteria, an event is inserted into
fhir_subscription_topic_eventwithin the same transaction. - 2.
A background enqueuer process dispatches undelivered events to subscription processors. Events are filtered by each subscription's
filterBycriteria — only matching events are delivered to that subscription. - 3.
Each subscription has a lightweight processor that batches events according to
max-countandheartbeat-periodsettings. - 4.
Deliverer workers (controlled by
number-of-deliverer) perform the actual HTTP delivery to subscription endpoints.
Event Cleanup
If keep-events-for-period is specified, a periodic cleanup process deletes events older than the retention period from both fhir_subscription_topic_event and fhir_subscription_delivery_status tables.
Multi-Instance Coordination
In multi-instance deployments, PostgreSQL advisory locks ensure only one Aidbox instance runs the delivery process for each AidboxTopicDestination. Other instances wait and take over if the lock becomes available.
Tutorials
For a step-by-step guide to setting up FHIR Topic-Based Subscriptions, follow one of the tutorials below based on your FHIR version:
| Version | Tutorial |
|---|---|
R4 | FHIR R4 Backport Subscription Setup |
R4B | FHIR R4B Backport Subscription Setup |
R5 | FHIR R5 Subscription Setup |
Last updated: