Skip to main content

Subscriptions

Fire Arrow Server supports FHIR Subscriptions for real-time notifications when resources change. Define criteria-based subscriptions to receive webhooks, emails, websocket messages, or Azure Storage Queue messages whenever matching resources are created or updated.

Supported Channels

Channel TypeDescriptionUse Case
rest-hookHTTP POST to a URLBackend integrations, webhooks
emailSend email notificationAlerts to practitioners, administrators
websocketPush via WebSocket connectionReal-time UI updates in web apps
messageAzure Storage Queue messageAsync processing, decoupled architectures

Creating a Subscription

Create a FHIR Subscription resource with your desired criteria and channel:

REST Hook

curl -X POST http://localhost:8080/fhir/Subscription \
-H "Content-Type: application/fhir+json" \
-H "Authorization: Bearer <your-token>" \
-d '{
"resourceType": "Subscription",
"status": "requested",
"criteria": "Observation?patient=Patient/123",
"channel": {
"type": "rest-hook",
"endpoint": "https://your-app.example.com/webhooks/observations",
"payload": "application/fhir+json",
"header": ["Authorization: Bearer webhook-secret-123"]
},
"end": "2026-06-28T00:00:00Z"
}'

The criteria field uses the same search syntax as FHIR REST search. When a resource matching the criteria is created or updated, Fire Arrow Server sends an HTTP POST to the specified endpoint.

Email

curl -X POST http://localhost:8080/fhir/Subscription \
-H "Content-Type: application/fhir+json" \
-H "Authorization: Bearer <your-token>" \
-d '{
"resourceType": "Subscription",
"status": "requested",
"criteria": "Task?status=failed",
"channel": {
"type": "email",
"endpoint": "mailto:[email protected]",
"payload": "application/fhir+json"
},
"end": "2026-06-28T00:00:00Z"
}'

WebSocket

curl -X POST http://localhost:8080/fhir/Subscription \
-H "Content-Type: application/fhir+json" \
-H "Authorization: Bearer <your-token>" \
-d '{
"resourceType": "Subscription",
"status": "requested",
"criteria": "Encounter?patient=Patient/123&status=in-progress",
"channel": {
"type": "websocket"
},
"end": "2026-06-28T00:00:00Z"
}'

Connect to the WebSocket endpoint at ws://localhost:8080/websocket to receive notifications for this subscription.

Azure Storage Queue

For decoupled, asynchronous processing, Fire Arrow Server can deliver subscription notifications to an Azure Storage Queue:

curl -X POST http://localhost:8080/fhir/Subscription \
-H "Content-Type: application/fhir+json" \
-H "Authorization: Bearer <your-token>" \
-d '{
"resourceType": "Subscription",
"status": "requested",
"criteria": "DiagnosticReport?status=final",
"channel": {
"type": "message"
},
"end": "2026-06-28T00:00:00Z"
}'

By default, messages are delivered to the queue configured in fire-arrow.subscription.azure-queue.queue-name. Each message contains the resource reference and subscription ID, allowing your queue consumer to fetch the full resource.

Per-Subscription Queue Routing

To fan notifications out to different queues from a single Fire Arrow Server, set the Subscription's channel.endpoint to the well-known prefix (channel:fire-arrow-azure-queue by default) followed by :<queue-name>. The endpoint suffix names a separate Azure Storage Queue resource in the same storage account — not a sub-channel inside one queue — so suffix-based routing is deny-by-default: operators must add each acceptable queue name to allowed-override-queues before suffix-based routing takes effect.

fire-arrow:
subscription:
azure-queue:
enabled: true
connection-string: "${AZURE_STORAGE_CONNECTION_STRING}"
queue-name: "fhir-subscription-events"
allowed-override-queues:
- identity-resolution-events
- whatsapp-events
curl -X POST http://localhost:8080/fhir/Subscription \
-H "Content-Type: application/fhir+json" \
-H "Authorization: Bearer <your-token>" \
-d '{
"resourceType": "Subscription",
"status": "requested",
"criteria": "CommunicationRequest?status=active&category=https://example.org/cr-category|identity-resolution-required",
"channel": {
"type": "message",
"endpoint": "channel:fire-arrow-azure-queue:identity-resolution-events",
"payload": "application/fhir+json"
},
"end": "2026-06-28T00:00:00Z"
}'

Endpoint resolution rules:

channel.endpointDestination queue
omitted / blank / channel:fire-arrow-azure-queueconfigured queue-name (default queue)
channel:fire-arrow-azure-queue:<queue-name> and <queue-name> is in allowed-override-queues<queue-name> (override)
channel:fire-arrow-azure-queue:<queue-name> and <queue-name> is not in allowed-override-queuesconfigured queue-name + a warning is logged
channel:fire-arrow-azure-queue:<invalid-name>configured queue-name + a warning is logged
anything else (e.g. an https:// REST Hook URL)configured queue-name (default queue)
Override queues are not auto-created by default

auto-create-queue controls only the configured default queue at boot. Suffix-derived override queues are not auto-created unless auto-create-override-queues: true is set explicitly. The recommended workflow is to provision per-tenant queues out-of-band (Bicep, Terraform, deploy script) and add their names to allowed-override-queues. If an override queue does not exist when a notification is delivered to it, the delivery is recorded as a permanent failure (counting toward delivery.failure-threshold) so it shows up in subscription health tracking.

Queue names must conform to the Azure Storage Queue naming rules (3-63 chars, lowercase alphanumeric and non-consecutive hyphens, starting and ending with a letter or digit).

The endpoint prefix can be customised via fire-arrow.subscription.azure-queue.endpoint-prefix if you want to carve out a tenant-specific endpoint namespace.

Subscription Lifecycle

Subscriptions go through the following statuses:

  1. requested - you create the Subscription with this status
  2. active - the server validates and activates the Subscription
  3. off - the Subscription's end date has passed
  4. error - too many consecutive delivery failures (configurable threshold)
Required End Date

The end field is required on all Subscriptions. The server enforces a maximum TTL - if your end date exceeds the allowed maximum, the server will reject the Subscription. Use a reasonable end date and renew subscriptions as needed.

Authorization

Subscription creation requires a subscribe authorization rule:

fire-arrow:
authorization:
validation-rules:
- client-role: "Practitioner"
resource: "Subscription"
operation: "subscribe"
validator: "LegitimateInterest"
- client-role: "Patient"
resource: "Subscription"
operation: "subscribe"
validator: "PatientCompartment"

Configuration

Enabling Subscriptions

Enable subscription support in your application.yaml:

hapi:
fhir:
subscription:
resthook_enabled: true
email:
enabled: true
websocket_enabled: true

Azure Storage Queue

To use the Azure Storage Queue channel, configure the queue connection:

fire-arrow:
subscription:
azure-queue:
enabled: true
connection-string: "${AZURE_STORAGE_CONNECTION_STRING}"
queue-name: "fhir-subscription-events"
endpoint-prefix: "channel:fire-arrow-azure-queue"
auto-create-queue: true
PropertyDescriptionDefault
enabledEnable the Azure Storage Queue channelfalse
connection-stringAzure Storage account connection string--
service-urlAzure Queue service URL (alternative to connection-string when using managed identity)--
credential-modeCredential type when using service-url (default or managed-identity)default
queue-nameDefault queue used when a Subscription doesn't specify an overridefhir-subscription-events
endpoint-prefixWell-known endpoint prefix that opts a Subscription into per-Subscription routingchannel:fire-arrow-azure-queue
allowed-override-queuesAllow-list of queue names that may be selected via the endpoint suffix. Empty means no overrides accepted (deny-by-default). See per-Subscription routing.[]
auto-create-queueAuto-create the default queue at boottrue
auto-create-override-queuesAuto-create suffix-derived override queues on first delivery. Off by default so that an authenticated subscription writer cannot drive Azure into provisioning new queue resources.false
override-queue-cache-max-sizeMaximum number of distinct override QueueClients kept cached in memory. Bounded LRU; eviction is essentially free.256
include-full-resourceInclude the full FHIR resource payload in each messagetrue
payload-formatSerialisation format for the payload (JSON or XML)JSON
channel-typeRestrict delivery to a single Subscription channel type (message, rest-hook, or all)message

Full Subscription Configuration

hapi:
fhir:
subscription:
resthook_enabled: true
websocket_enabled: true
email:
enabled: true

fire-arrow:
subscription:
azure-queue:
enabled: true
connection-string: "${AZURE_STORAGE_CONNECTION_STRING}"
queue-name: "fhir-subscription-events"

Environment Variable Overrides

YAML PathEnvironment Variable
hapi.fhir.subscription.resthook_enabledHAPI_FHIR_SUBSCRIPTION_RESTHOOK_ENABLED
hapi.fhir.subscription.websocket_enabledHAPI_FHIR_SUBSCRIPTION_WEBSOCKET_ENABLED
hapi.fhir.subscription.email.enabledHAPI_FHIR_SUBSCRIPTION_EMAIL_ENABLED
fire-arrow.subscription.azure-queue.connection-stringFIRE_ARROW_SUBSCRIPTION_AZURE_QUEUE_CONNECTION_STRING
fire-arrow.subscription.azure-queue.service-urlFIRE_ARROW_SUBSCRIPTION_AZURE_QUEUE_SERVICE_URL
fire-arrow.subscription.azure-queue.queue-nameFIRE_ARROW_SUBSCRIPTION_AZURE_QUEUE_QUEUE_NAME
fire-arrow.subscription.azure-queue.endpoint-prefixFIRE_ARROW_SUBSCRIPTION_AZURE_QUEUE_ENDPOINT_PREFIX
fire-arrow.subscription.azure-queue.allowed-override-queuesFIRE_ARROW_SUBSCRIPTION_AZURE_QUEUE_ALLOWED_OVERRIDE_QUEUES
fire-arrow.subscription.azure-queue.auto-create-queueFIRE_ARROW_SUBSCRIPTION_AZURE_QUEUE_AUTO_CREATE_QUEUE
fire-arrow.subscription.azure-queue.auto-create-override-queuesFIRE_ARROW_SUBSCRIPTION_AZURE_QUEUE_AUTO_CREATE_OVERRIDE_QUEUES
fire-arrow.subscription.azure-queue.override-queue-cache-max-sizeFIRE_ARROW_SUBSCRIPTION_AZURE_QUEUE_OVERRIDE_QUEUE_CACHE_MAX_SIZE
  • CarePlan Events - server-side scheduling of CarePlan activities into Task resources, with webhook notifications via subscriptions.
  • Custom Operations - $subscribe-due-events convenience operation for CarePlan subscriptions.