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. 1.
    AidboxSubscriptionTopic - implements the topic's triggers and filters
  2. 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.

FieldDescription
filterParameterName of the filter parameter that subscribers will use
filterDefinitionFhirPathExpressionFHIRPath expression to extract the filter value from the resource
resourceResource type this filter applies to
comparatorComparison operators allowed for this filter. Currently only eq (equals) is supported.
descriptionHuman-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:

ParameterTypeDescription
fhir-topicvalueCanonicalCanonical URL of the FHIR SubscriptionTopic this destination implements.
subscription-specification-versionvalueStringFHIR 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-periodvalueUnsignedIntHow long events remain available for the $events operation (in seconds). If not specified, events are stored indefinitely.
number-of-deliverervalueUnsignedIntNumber 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

FieldR4 BackportR5Description
Topiccriteria topic Canonical URL of the SubscriptionTopic
Filter criteriabackport-filter-criteria filterBy Filter events using parameters defined in canFilterBy
Expirationend end DateTime when subscription expires and stops receiving events
Channel typechannel.type channelType Only rest-hook is supported
Endpointchannel.endpoint endpoint URL to receive notifications
Content typechannel.payload contentType MIME type for notification payload (e.g., application/fhir+json)
Content levelbackport-payload-content content Payload detail: empty, id-only, or full-resource
Headerschannel.header parameter Custom HTTP headers sent with notifications
Heartbeatbackport-heartbeat-period heartbeatPeriod Interval in seconds for keep-alive notifications during inactivity
Max countbackport-max-count maxCount Max events per notification bundle before sending
Timeoutbackport-timeout timeout Max seconds to wait for endpoint response before failing delivery

Subscription Lifecycle

  1. 1.

    Requested: Client creates a Subscription resource with status: requested. The subscription is not yet active.

  2. 2.

    Handshake: Server immediately sends a handshake notification to the configured endpoint to validate it can receive notifications. The handshake bundle contains a SubscriptionStatus with type: handshake.

  3. 3.

    Active: If the endpoint responds with HTTP 2xx, the subscription status changes to active and starts receiving events. If handshake fails, status changes to error.

  4. 4.

    Event notifications: When resources matching the topic trigger criteria are created/updated/deleted, notification bundles are sent to the endpoint containing the SubscriptionStatus and triggered resources. Events are batched up to max-count before sending, or sent earlier if heartbeat period is reached.

  5. 5.

    Heartbeat: If configured via backport-heartbeat-period, periodic empty notifications are sent during inactivity to confirm the connection is alive.

  6. 6.

    Expiration/Off: Subscription stops receiving events when:

    • The end date is reached
    • The subscription is deleted

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 count
  • error - error details if delivery failed
VersionSpecification
R5Subscription $status
R4-backportedSubscription 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:

ParameterTypeDefaultDescription
eventsSinceNumberstringeventsUntilNumber - 100Start of event range (inclusive)
eventsUntilNumberstringLast event numberEnd of event range (inclusive)
contentcodeSubscription's content levelfull-resource, id-only, or empty

Defaults: If no parameters provided, returns up to 100 most recent events.

VersionSpecification
R5Subscription $events
R4-backportedSubscription Events Operation

Notification Bundle

The notification bundle format depends on the subscription-specification-version configured in AidboxTopicDestination.

VersionSpecification
R5SubscriptionStatus , Notification Bundle
R4B-backportedSubscriptionStatus
R4-backportedNotification 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:

TableDescription
tds.fhir_subscription_topic_eventStores triggered events. Events are inserted within the same database transaction that processes the FHIR request, ensuring consistency.
tds.fhir_subscription_delivery_statusStores 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. 1.

    When a FHIR resource is created/updated/deleted and matches a topic's trigger criteria, an event is inserted into fhir_subscription_topic_event within the same transaction.

  2. 2.

    A background enqueuer process dispatches undelivered events to subscription processors. Events are filtered by each subscription's filterBy criteria — only matching events are delivered to that subscription.

  3. 3.

    Each subscription has a lightweight processor that batches events according to max-count and heartbeat-period settings.

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

VersionTutorial
R4FHIR R4 Backport Subscription Setup
R4BFHIR R4B Backport Subscription Setup
R5FHIR R5 Subscription Setup

Last updated: