Short answer: a Subscription tells a FHIR server notify this endpoint when resources matching these conditions change. In FHIR R4 the conditions are a search string (criteria: "Observation?patient=123&code=85354-9") and the server sends the matching resource to a REST hook, a websocket, an email or a FHIR message. In R4B and R5 the model was rebuilt around SubscriptionTopic: the server publishes the events it can notify about, the client subscribes to a topic with filters, and notifications arrive as a Bundle of type subscription-notification that always starts with a SubscriptionStatus resource. The Subscriptions R5 Backport implementation guide lets R4 servers offer the new model.
R4: criteria-based subscriptions
In FHIR R4, a Subscription is criteria-based: the client creates a Subscription resource whose criteria is a search string such as Observation?code=…&patient=… and whose channel is rest-hook, websocket, email, sms or message. The server evaluates the criteria against every create and update and POSTs each matching resource, or an empty notification, to the endpoint.
{
"resourceType": "Subscription",
"status": "requested",
"reason": "Blood pressure alerts for the hypertension clinic",
"criteria": "Observation?code=http://loinc.org|85354-9&patient=Patient/123",
"channel": {
"type": "rest-hook",
"endpoint": "https://clinic.example.org/hooks/bp",
"payload": "application/fhir+json",
"header": ["Authorization: Bearer <token>"]
}
}
The server evaluates the criteria against every create and update. When a resource matches, it POSTs it to the endpoint (or POSTs an empty body if payload is omitted, so the client fetches the resource itself). It worked, but with three problems: servers could not say which criteria they supported, clients could not tell a missed notification from a quiet period, and "a resource matched a search" is not the same as "an event happened" (an Encounter ending is not expressible as a search).
R4B and R5: topic-based subscriptions
In FHIR R4B and R5, subscriptions are topic-based. The server publishes SubscriptionTopic resources describing events (the resource, a trigger such as create, update, delete or a FHIRPath condition, and allowed filters). A subscriber creates a Subscription pointing to a topic with filterBy values, a channel type, an endpoint, a heartbeat period and a content setting of empty, id-only or full-resource.
SubscriptionTopic is a resource the server publishes, describing an event: which resource, which trigger (create, update, delete, or a FHIRPath condition such as %previous.status != 'finished' and %current.status = 'finished'), and which filters a subscriber may use. The subscriber then creates a Subscription that points to the topic:
{
"resourceType": "Subscription",
"status": "requested",
"topic": "http://example.org/fhir/SubscriptionTopic/encounter-end",
"filterBy": [{"resourceType": "Encounter", "filterParameter": "patient", "value": "Patient/123"}],
"channelType": {"system": "http://terminology.hl7.org/CodeSystem/subscription-channel-type", "code": "rest-hook"},
"endpoint": "https://clinic.example.org/hooks/encounters",
"heartbeatPeriod": 60,
"timeout": 30,
"contentType": "application/fhir+json",
"content": "id-only"
}
content chooses how much travels: empty (just the event), id-only (references, fetch the rest with your own authorisation), or full-resource. heartbeatPeriod makes the server send a heartbeat so silence is detectable.
What a notification looks like
A FHIR subscription notification is a Bundle of type subscription-notification whose first entry is a SubscriptionStatus resource giving the subscription, the topic, the notification type (handshake, heartbeat, event-notification, query-status or query-event) and an eventsSinceSubscriptionStart counter for detecting missed events. Further entries carry the changed resources or their references, depending on the content setting.
Every notification is a Bundle of type subscription-notification. Its first entry is a SubscriptionStatus with the subscription reference, the topic, the type of notification (handshake, heartbeat, event-notification, query-status, query-event) and an eventsSinceSubscriptionStart counter; a gap in the counter means a missed event, and the client can call $events to recover it. Further entries carry the resources or their references, depending on content.
Channels: rest-hook (the server POSTs to your HTTPS endpoint), websocket (the client connects and receives notifications on the socket, useful for browser apps), email (notification only, not data), message (a FHIR messaging Bundle). The R4 sms channel was dropped.
Running it in production
Running FHIR Subscriptions in production requires idempotent handling keyed on event number and resource version, endpoint security that verifies the sender and never treats the payload as authorisation, tolerance for batched bundles with many events, a 2xx response to the initial handshake notification, and awareness that many servers still limit subscriptions to backend services rather than patient apps.
- Idempotency. Notifications can be duplicated after a retry; key on the event number and resource version.
- Security. The endpoint must verify the sender (a shared secret header, mutual TLS or signatures) and must never trust the payload as authorisation; fetch sensitive data back from the server with your own token when
contentisid-only. - Backpressure. Servers batch events and respect
maxCount; clients must accept bundles with many events. - Handshake. On activation the server sends a
handshakenotification and expects a 2xx; otherwise the subscription moves toerror. - Scopes. SMART on FHIR added subscription scopes slowly; many servers still restrict subscriptions to backend services (client credentials) rather than patient apps.
Webhooks that are not FHIR Subscriptions
Webhooks that are not FHIR Subscriptions are plain platform mechanisms: you register an HTTPS URL and receive signed JSON events such as resource written or consent changed, without any SubscriptionTopic. Anpheros offers such webhooks. They are simpler for one platform but not discoverable by a generic FHIR client, so integrations that must work against any FHIR server use Subscriptions.
Many platforms, including Anpheros, offer plain webhooks: you register an HTTPS URL and receive signed JSON events such as resource written, consent changed, document finalised. They solve the same need with less ceremony and no SubscriptionTopic, and they are not FHIR resources, so a generic FHIR client cannot discover them. If your integration must work against any FHIR server, use Subscriptions; if it targets one platform, its webhooks are usually simpler. Anpheros documents its webhooks on the developer site (link below).
Related pages
- FHIR R4 vs R5
- FHIR search explained
- FHIR Bundle explained
- CDS Hooks explained
- Remote patient monitoring with FHIR
- Anpheros webhooks (developers)
Sources
- HL7 FHIR R5: Subscription
- HL7 FHIR R5: SubscriptionTopic
- HL7 FHIR R5: SubscriptionStatus and notifications
- HL7 FHIR R4: Subscription
- Subscriptions R5 Backport implementation guide
Frequently asked questions
Can I use R5-style subscriptions on an R4 server?
Yes, if the server implements the Subscriptions R5 Backport implementation guide, which encodes the topic, filters and channel settings as extensions on the R4 Subscription resource and uses R4 Bundles for notifications.
What is the difference between a Subscription and polling with _lastUpdated?
Polling asks the server repeatedly and misses nothing but is slow and wasteful; Subscriptions push events as they happen. Robust clients combine both: subscribe for speed, poll or call $events to recover after downtime.
Does the notification contain the data?
Only if content is full-resource. The safer default is id-only: the notification tells you which resource changed, and you fetch it with your own authorisation.
Are FHIR Subscriptions the same as webhooks?
A rest-hook subscription is a webhook with a FHIR-defined shape: discoverable topics, standard notification bundles and status tracking. Plain webhooks offered by a platform do the same job for that platform only.
Try it free in the sandbox
Your first FHIR call in 5 minutes. An HL7 FHIR R4 API with the patient's consent built in: try it in the sandbox, then connect your own app or clinic system.
Free · 30 synthetic patients · no card

The more you log, the clearer your record
Your health history in one place, under your control.
Free · Works in your browser · No card
