OZO FHIR implementation guide
0.9.0 - ci-build

OZO FHIR implementation guide - Local Development build (v0.9.0) built by the FHIR (HL7® FHIR® Standard) Build Tools. See the Directory of published versions

CapabilityStatements

The OZO FHIR API defines three CapabilityStatements that together describe the full API contract. All three are available via the /metadata endpoint and in the Artifacts section.

CapabilityStatement Description
OZO-Server Server identity, security requirements (Nuts + DPoP + mTLS), and supported OZO profiles.
OZO-System Full CRUD access to all resources. For server-to-server integration (OzoSystemCredential).
OZO-Client Client access for Practitioner, RelatedPerson, and Patient roles. All three roles share the same resource types and interactions — the AAA proxy scopes access differently per role.

Access filtering by the AAA proxy

The OZO AAA proxy sits between clients and the FHIR server. It automatically applies search filters to scope results to the authenticated user's access level. Clients do not need to add these filters — the proxy rewrites the query parameters transparently.

For example, when a practitioner searches for GET /fhir/Patient, the proxy rewrites this to:

GET /fhir/Patient?_has:CareTeam:patient:participant={practitionerReference}

This ensures the practitioner only sees patients in their CareTeams, without the client needing to know the filtering logic.

Per-role scoping

The tables below show the search filters the proxy auto-applies for each role. The {self} placeholder represents the authenticated user's FHIR reference(s).

Practitioner

Authenticated via NutsOrganizationCredential + NutsEmployeeCredential. Access is scoped to CareTeams where the practitioner is a participant.

Resource Proxy auto-applied filter
Patient _has:CareTeam:patient:participant={self}
Practitioner _has:CareTeam:participant:participant={self}
RelatedPerson _has:CareTeam:participant:participant={self}
CareTeam participant={self}
CommunicationRequest participant={self OR careTeams} (addressed in recipient or initiating team in extension[senderCareTeam])
Communication part-of:CommunicationRequest.participant={self OR careTeams}
Task owner={self OR careTeams} (own Tasks and the team Tasks of the practitioner's organizational teams)
AuditEvent agent={careTeamPractitioners} (all practitioners in shared CareTeams)
Subscription criteria rewritten with above filters

RelatedPerson

Authenticated via OzoUserCredential. Access is scoped to the related person's own profile and their CareTeam memberships.

Resource Proxy auto-applied filter
Patient _has:RelatedPerson:patient:identifier={self}
Practitioner _has:CareTeam:participant:participant={self}
RelatedPerson identifier={self} (own profile only)
CareTeam participant={self}
CommunicationRequest participant={self OR careTeams}
Communication part-of:CommunicationRequest.participant={self OR careTeams}
Task owner={self}
AuditEvent agent={self OR careTeams}
Subscription criteria rewritten with above filters

Patient

Access is scoped to the patient's own record and CareTeams where the patient is the subject.

Resource Proxy auto-applied filter
Patient identifier={self} (own record only)
Practitioner _has:CareTeam:participant:patient={self}
RelatedPerson patient={self}
CareTeam patient={self}
CommunicationRequest participant={self OR careTeams}
Communication part-of:CommunicationRequest.participant={self OR careTeams}
Task patient={self}
AuditEvent agent={self OR careTeams}
Subscription criteria rewritten with above filters

Write validation

For write operations (POST/PUT), the proxy validates the resource content regardless of role:

Resource Validation rule
Communication sender must be the authenticated user; partOf must reference a thread the user is a party of; extension[senderCareTeam], when present, must name an organizational CareTeam of the thread the user participates in. Required from the release after 0.9.0 when the user participates in such a team; 0.9.0 infers it when absent
CommunicationRequest requester must be the authenticated user; extension[senderCareTeam] must be an organizational CareTeam the user participates in and should not be repeated in recipient (validation warning in 0.9.0, error from the next release)
AuditEvent agent[requestor=true].who must be the authenticated user; on a read receipt in a team thread, agent.extension[careTeam] follows the same rule as Communication.extension[senderCareTeam]
Subscription criteria is rewritten to scope results to the user's access

Custom search parameters

The IG defines two custom SearchParameters. They are advertised in the OZO-Client and OZO-System CapabilityStatements and installed on the HAPI FHIR server together with the profiles.

Parameter Resource Type Definition
participant CommunicationRequest reference ozo-communicationrequest-participant: union of recipient and the CareTeam in extension[senderCareTeam], every party of the thread. The proxy scopes thread and message access on it
sender-careteam CommunicationRequest reference ozo-communicationrequest-sender-careteam: the CareTeam in extension[senderCareTeam], the team that initiated the thread

Examples: GET /CommunicationRequest?participant=CareTeam/Pharmacy-A returns every thread Pharmacy A takes part in; GET /CommunicationRequest?sender-careteam=CareTeam/Pharmacy-A returns the threads it initiated. The participant parameter also works chained: GET /Communication?part-of:CommunicationRequest.participant=CareTeam/Pharmacy-A. Existing data must be reindexed once after a parameter is installed; see Installing OZO Package in HAPI FHIR Server.

Subscription support

All CapabilityStatements include Subscription support. Subscriptions use the notify-then-pull pattern required in Dutch healthcare:

  1. The FHIR server sends an empty notification (no resource payload) to the subscriber's endpoint
  2. The subscriber pulls the changed resource by performing a FHIR read or search

The proxy automatically rewrites Subscription criteria to scope notifications to the user's access level, similar to search query rewriting.

Because notifications are empty, the subscriber needs a valid access token to perform the pull — also when no user session is active at that moment. See Background access and subscription-driven pulls for how to keep the practitioner context alive using an IdP refresh token.

See the Individual Messaging and Team-to-Team Messaging pages for detailed Subscription examples and behavior.

CapabilityStatements

  • OZO-Server — Server identity, security, profiles
  • OZO-System — System access (full CRUD)
  • OZO-Client — Client access (Practitioner, RelatedPerson, Patient)