Netherlands - Generic Functions for data exchange Implementation Guide
0.3.0 - ci-build
Netherlands - Generic Functions for data exchange Implementation Guide, published by Stichting Nuts. This guide is not an authorized publication; it is the continuous build for version 0.3.0 built by the FHIR (HL7® FHIR® Standard) CI Build. This version is based on the current content of https://github.com/nuts-foundation/nl-generic-functions-ig/ and changes regularly. See the Directory of published versions
| Page standards status: Draft |
Notified Pull is a data-availability exchange pattern: a Sender publishes a notification saying that one or more resources are available, and the Receiver pulls those resources on its own terms.
In its base form, described on this page, Notified Pull is fire-and-forget. The Sender does not expect the Receiver to accept, reject, report progress, or signal completion. The data exists at the Sender; the notification points the Receiver at it; what happens after that is the Receiver's concern. Workflows that need acceptance, lifecycle and cancellation negotiation (referrals, transfers of care) layer the HL7 Clinical Order Workflow (COW) IG on top of this pattern; that combination is specified separately.
This page builds on the generic Notification transport (Subscription, SubscriptionTopic, notification Bundle, notification-event). It adds three things:
notification-event.focus for data-availability events,notification-authorization-hint carried on the transport is used to scope the pull.The pattern fits when:
It does not fit when:
Use cases of that shape layer the COW workflow on top of Notified Pull.
The fire-and-forget stance has explicit, deliberate costs:
410 Gone, 404 Not Found, or 403 Forbidden).Implementers and use-case owners should weigh these costs explicitly. If any of them is unacceptable, the use case belongs in a workflow profile, not in plain Notified Pull.
The simplest pattern: one notification, one resource.
notification-event.focus references the clinical resource directly:
focus → Observation/abc123focus → DocumentReference/def456focus → MedicationDispense/ghi789The Receiver performs a standard FHIR read against the Sender's endpoint. Patient identity comes from the resource's .subject field (see Subject identification).
When the Sender wants to make a coherent set of resources available in a single event, notification-event.focus references a container resource that aggregates the references. Three FHIR resources are reasonable containers; the choice is driven by what semantics best match the use case:
| Container | Best for | Notes |
|---|---|---|
List |
Generic collection of resources (recommended default) | List.subject carries the patient; List.entry.item carries each reference; List.code can label the collection (e.g. "BgZ snapshot") |
Composition |
Document-style aggregates with a narrative structure | Use when the set represents a coherent clinical document (e.g. a transfer-of-care summary). Composition.section.entry carries the references |
DocumentReference |
Document or binary content (e.g. PDF/A handoff document) | Use when the actual payload is a document rather than discrete resources |
For most data-availability events the List container is sufficient and the recommended default.
List
status: current
mode: snapshot
code: <use-case-bound code, e.g. "BgZ snapshot">
subject: Reference(Patient with identifier = BSN)
date: 2026-05-11T10:30:00+02:00
entry:
- item: Reference(Observation/...)
- item: Reference(Condition/...)
- item: Reference(MedicationStatement/...)
A container instance lives at the Sender's FHIR endpoint and is read by the Receiver like any other resource. The Receiver MAY pull the container alone (for routing or display) and pull entries on demand, or pull everything eagerly with _include.
Multiple notification-event.focus references (without a container) are also valid for small ad-hoc groups; the trade-off is that the Receiver loses the explicit aggregation and any metadata the container would carry.
When a previously notified resource set evolves (an item changes, an item is added, or an item is removed), the Sender SHOULD send a new notification on the same Subscription with an incremented event-number and the same notification-event.focus. The Receiver detects the new event and decides what to pull again. Pulling everything is always safe but rarely necessary; two FHIR mechanisms let the Receiver minimise what it pulls.
meta.lastUpdated on individual resources. Every FHIR resource instance carries meta.lastUpdated, which the server sets whenever the resource is modified. After a read the Receiver compares this against its cached value and refreshes only when newer. The corresponding _lastUpdated search parameter does the same job for a set: GET [base]/Observation?patient=X&_lastUpdated=ge<timestamp> returns only resources updated since the timestamp. This works for any resource the Receiver has seen before, including each entry in a container.
_history for explicit version chains. Retrieving the history of a resource (GET [base]/List/abc/_history) returns every version with its own meta.lastUpdated. For a container focus this is the only reliable way to detect membership changes: an entry that was removed does not produce an updated version of the now-missing resource, so a _lastUpdated query against the items alone cannot surface a removal. History requires the Sender to retain past versions, which is the FHIR default behaviour.
The two patterns combine. For a single-resource focus, a read followed by a meta.lastUpdated comparison is usually sufficient. For a container focus, the Receiver typically reads the container (or its _history) to detect membership changes and then uses _lastUpdated-filtered searches or per-item reads to refresh the items it still wants.
The patient identity (BSN, in the Dutch context) is not carried on the notification wire. Two complementary mechanisms apply:
Resource.subject.identifier for a clinical resource focus, List.subject.identifier / Composition.subject.identifier / DocumentReference.subject.identifier for a container focus. The Receiver learns the BSN at the moment it reads the focus resource (or via _include when fetching it). This is the standard FHIR pattern.notification-authorization-hint token. When the Sender needs to scope the pull to the patient without exposing the BSN to the Receiver in the clear, it encodes the BSN into the opaque token using a Sender-only key. The Receiver plays the token back; the Sender's authorization server decodes it and constrains the pull. See Authorization base.Routing the notification internally before pulling (to a specific department, mailbox or user) is a separate concern from subject identification, and is covered by a routing label rather than by exposing the subject. See Open questions.
The transport layer's notification-authorization-hint extension carries an opaque token from the Sender to the Receiver. The Receiver plays it back in subsequent access-token requests; the Sender's authorization server decodes it and authorizes the pull. The token is opaque to the Receiver and is not interpreted by it.
For Notified Pull, a practical pattern is to encode the patient identifier (BSN), the scope of resources made available, and a validity window into the token, using a Sender-only key:
additional-context;Costs of this approach:
The token's wire format and processing rules are out of scope of this page and belong to the access-control specification agreed between partners. Inclusion of the hint is optional; whether a use case requires it depends on the access-control model in play.
When the Sender decides the data should no longer be retrievable (because it was wrong, superseded, withdrawn or expired), it either removes the resource from its FHIR endpoint or revokes the access that the notification-authorization-hint token grants. Subsequent read requests receive a standard HTTP response that doubles as the withdrawal signal:
410 Gone: the resource was here and has been deliberately removed. Use this when the Sender is willing to confirm that the withdrawal happened.404 Not Found: the URL does not resolve to a resource. Use this when the Sender does not want to disclose whether the resource ever existed.403 Forbidden: the Receiver's authorization (typically the authorization-base) has been rejected. This response implies nothing about whether the resource exists; the existence check sits behind the access step.There is no cancel operation in Notified Pull and no follow-up notification announcing withdrawal. The HTTP response on the next pull is the signal. A Receiver that never re-pulls will not learn of the withdrawal; that is the deliberate price of fire-and-forget.
Per-case workflow cancellation with an acknowledged handshake between Sender and Receiver requires the COW workflow profile.
Under discussion. This section captures working-group suggestions for the middle ground between pure fire-and-forget and a full COW workflow. Nothing here is normative yet.
Some use cases want lightweight tracking of whether the Receiver has fetched, viewed or processed the data, without committing to a full COW workflow lifecycle. Three approaches are on the table:
Server-side audit only. The Sender already logs every read request on the focus resource and its referenced entries; "was it pulled" is derivable from those access logs without any Receiver-side participation. No protocol additions, no Receiver obligation. Tells the Sender only about fetches, not about consumption or downstream action.
Receiver-updated status on the focus resource. The Sender exposes a status field on the focus resource (for example, a processing-status extension on the List container) that the Receiver updates from pending to viewed or processed. State lives on the resource the Receiver already pulled. The Sender accepts and validates the updates, and decides which Receivers may set which values. Couples state semantics to the focus resource type.
A separate Task carrying the acknowledgement. The Sender creates a Task with focus on the data and status = requested; the Receiver updates the Task's status to received or completed as it makes progress. Conceptually this is COW Lite: a single Task, no Request resource, no multi-candidate solicitation, no cancellation negotiation. It re-uses the FHIR Task state machine and is unambiguous from the Sender's perspective. The risk is conceptual drift: an acknowledgement Task is shape-wise similar to a COW Coordination Task and the two should not be confused in mixed deployments.
In all three approaches, the Sender's intent (whether tracking is expected at all, and which mechanism applies) should be discoverable from the SubscriptionTopic, so a Receiver knows up-front whether a notification implies an obligation to update state somewhere.
notification-event mechanics that Notified Pull uses unchanged. This page extends that transport only by giving notification-event.focus a data-availability meaning and by describing how the notification-authorization-hint is used.notification-event.focus points at a Coordination Task rather than a clinical resource or container; the workflow page adds the Task lifecycle, multi-candidate solicitation, and cancellation negotiation on top.HealthcareService, Location and optionally ActivityDefinition published in an mCSD Directory. See Pre-pull routing for how NP carries a routing hint that points the Receiver at one of those primitives.notification-authorization-hint token are specified by the partners' access-control agreement.See also the Subscription Topics Exploration, a working-group discussion document on the granularity of subscriber-initiated subscriptions.
List as the default multi-resource container, with Composition and DocumentReference as use-case-driven alternatives. Working group to confirm.notification-event.additional-context, on a separate resource, etc.) and how it relates to the BSN-in-authorization-base pattern.