Skip to main content

API principles and walkthrough

This page explains the general functionality and the typical use (flow) of the DVPA-r.

The Dienstverlener Push Authorization - recipient (DVPA-r) is a software component that acts as an interface for managing authorizations received by the Huisartsenpost (HAP). The DVPA-r is an abstraction layer around the HAPbox, a component owned by the HAP which stores and manages decentralized authorizations. These authorizations are issued by general practitioners (GP's, huisartsen) that own and make use of a Whitebox. The Whitebox is also called a DVPA-s, for DVPA-source. The Whitebox is a 'security gateway', an authorization system owned by the GP, which is connected to the HIS so that it can retrieve patient information directly. The Whitebox generates authorizations for individual patients and registers them in the HAPbox.

The DVPA-r provides an API to the HAPIS to search for available authorizations of patients. It provides a simple and convenient interface to select authorizations of specific patients, and to use these authorizations to retrieve patient information from the source that issued the authorization.

Figure 1

Figure 1: the GP domain and the HAP domain. From the GP domain, a Whitebox (WB) connects to the Hapbox (HBX) at the HAP to register authorizations to patient records (arrow). De DVPA-r positions between the HAPbox and the HAPIS.

info

The HAPbox is a software component, running on Whitebox hardware owned by the HAP. The HAPbox stores and manages authorizations regstered by GP Whiteboxes. The DVPA-r provides an interface to the HAPbox. Initially, it will run on the HAPbox hardware. Eventually, the HAPbox will be replaced by a stand-alone DVPA-r component managed by the HAP or HAPIS on their own servers.

The DVPA-r is designed to support different types of authorizations and authentication mechanisms. This allows for future extensions and implementations of the DVPA-r to support multiple decentralized authorization mechanisms and standards.

In this document we describe the API of the DVPA-r component. The focus of this document is on descriptions and examples to understand the principles and concepts of the DVPA-r. Some trivial details (e.g., irrelevant HTTP headers) are omitted.

Base URL and versioning of the DVPA-r API

The DVPA-r API is a versioned API and its version is represented in its URL. For now the only available version is version v1 so the pathname in the base URL is /dvpa-r/v1/.

1. Concepts

Patient records and the professional summary

A patient record is the record that contains patient information. Patient records can be represented in different ways. In the DVPA-r, patient records have a type and can have multiple representational formats. In the context of the HAP, the patient record type indicates a professional summary (PS) presented in a format accepted by the HAPIS.

Each authorization refers to a single specific patient and a specific patient record type. Example patient record types are NHG-PS or AMO for a professional summary or a current medication overview (AMO), respectively. Example representational formats are application/HL7v3 or application/MEDEUR. The relevant format can be selected (by the HAPIS) from a list of formats that is registered with the patient record metadata (see below), and should be used for content negotiation when retrieving the patient record using its authorizationInstance endpoint (see section 3).

Patient and patient record metadata

The DVPA-r provides a resource holding the metadata for each authorization. Metadata is associated with an authorization and refers to the the patient record and the patient's metadata. A source system provides patient metadata along with the authorization: name, date of birth, address, zip code, BSN if available. It also provides the type of information disclosed through the authorization, i.e., the patient record type, as metadata. This way, the receiving system knows what patient the authorization concerns and how to retrieve and decode the result.

Source system (patient record holder, authorization issuer)

The holder of a record is the original creator and maintainer of a patient record, typically a general practitioner. It is the source system which, on behalf of the holder, issues an authorization. To do so, the source system uses an authorization system (DVPA-s) to generate an authorization and register it with the HAPbox/DVPA-r. In practice, the DVPA-s is a Whitebox connected to the GP / source information system.

Authorization holder (recipient of an authorization)

The authorization holder or -recipient is the party, usually a healthcare organization, that receives and holds an authorization for subsequent use. If an authorization (token, authorization code) is passed to or via a patient/citizen, this patient may be a (temporary) holder (keeper) of the authorization

User

The user is the person (typically, a doctor) who, through the HAPIS, uses the DVPA-r and therewith the HAPbox to search authorizations and retrieve patient records.

Authorization

An authorization is a right to access information. Different authorization systems exist, from from role-based access control (RBAC) systems that authorize parties based on group-wide properties (e.g., "all GP's may access this resource"), to fine-grained decentralized authorizations that are issued individually by one healthcare professional to a specific other healthcare professional (or a patient) to allow specific information to be viewed or retrieved by this recipient. Examples of decentralized authorizations are authorizations provided by the Nuts system, and Whitebox push authorizations. The DVPA-r is designed to support the latter types of decentralized authorizations.

A specific type of decentralized authorization is a capability: a special 'authorization token' that may be issued decentrally from one party to another, which gives this specific recipient (authorization holder) access to specific information. In the push authorization model, authorizations are identity-based capabilities, which are capabilities (in the form of unique, per-authorization URLs) that are tied to specific organizations or users. Only the user or organization bound to a capability may use this capability.

Push authorization

Whitebox authorizations have the form of push authorization URLs (PA-URLs). PA-URLs are essentially a combination of an identity-based capability, i.e., a token that provides the recipient with access rights (see above); and a reference to locate the source where the patient record resides.

A PA-URL is a protected URL that gives access to a specific patient record. Before granting access (e.g., by responding to a GET request), a number of checks take place. For example, the party using the authorization may be required to have an UZI smartcard with a particular role. Also, checks may take place at the side of the Whitebox (source side), to verify that the party that uses the authorization is an employee of the specific organization associated with this URL. For this, the source may consult this organization's identity management (IAM) system.

info

As part of implementing the DVPA-r in Health Connected, we must agree on an API (and a way to connect to this API) provided by Health Connected, with which the DVPA-r can request a list of UZI ID's of assistents and GP's from the HAPIS. This is needed to implement the identity management functionality to allow abovementioned check at the Whitebox - DVPA-r level.

The PA model uses a concept called binding to associate identity information (of a recipient organization or healthcare professional) with the URL. This mechanism is described in section 2. Binding means that a PA-URL is uniquely associated with one specific party or entity. If a PA-URL is bound, only the bound party may use the URL.

info

Whitebox authorizations are push authorization URLs or PA-URLs, as explained above. By design, the DVPA-r is not authorized to use the PA-URLs it stores to access patient records itself. Only a user of the organisation hosting the DVPA-r can use the PA-URLs to access patient records. See authorization instance for more information.

Authorization instance

An authorization instance is an object that provides a specific user access to the underlying patient record. Before performing an action on the authorization, such as retrieving a patient record or requesting a new authorization to delegate access to another party, a user should create an instance of an authorization and use that authorization instance to perform actions upon.

info

Internally to the DVPA-r, when instantiating an authorization, the DVPA-r/HAPbox uses the PA-URL that was originally registered in the DVPA-r/HAPbox to request a copy of the original URL from the source. This copy is a new, unique PA-URL, that will be bound to the requesting user on first use (see section 2).

The original PA-URL is bound to the HAPbox/DVPA-r, and is only allowed to request copy URLs.

Authentication token

An authentication token is a proof that allows the source system to authenticate a user. For example, a JWT signed by a user's UZI certificate.

2. Whitebox decentralized authorization

In this section, we describe the principle of decentralized push authorization, as implemented by Whitebox.

Figure 2 shows a situation where a GP (the source system, left) wants to exchange information with the HAP. For the system to work, the GP system must first be connected to the HAPbox/DVPA-r that is part of the HAP and integrated in the HAPIS. The result is a permanent authenticated, encrypted channel between the source system and DVPA-r.

info

The GP typically makes a connection to a HAP that it has a contractual relation with. This contractual relation concerns out-of-hours care, and it provides the legal basis to register authorizations with the HAP. The connection is made by the GP when the GP acquires and installs a Whitebox. The registration involves a bidirectional protocol, as part of which the HAP has to accept the connection by sending an authorization code to the GP by registered mail. Details of the protocol and its implementation are out of scope for this document.

Having a permanent (trusted) connection between source systems and the HAP/DVPA-r system is specific to how Whitebox works. However, other decentralized authorization systems similarly allow source systems to connect directly to specific (trusted) peers that they want to communicate with. For example, using a https endpoint.

Figure 2 shows the steps taken by a Whitebox/DVPA-s to register an authorization, and the steps taken by the DVPA-r and the DVPA-s when an authorization is used to retrieve a patient record. Steps are explained below.

Figure 2

Figure 2: a source system connected to a HAP/DVPA-r (dashed lines), and interactions between the source system and the DVPA-r (solid arrows). Steps 0-3 are explained in the text below.

Using push authorization, the authorization and authorization usage flow is as follows.

In step 0, a GP system creates an authorization for a given patient (record), and registers this authorization together with metadata describing the subject (patient) and the patient record type in the DVPA-r. All source systems can register authorizations in the DVPA-r this way. This step is a prerequisite before step 1 can start.

Some time between step 0 and step 1, the HAP admits a patient and registers a call in the HAPIS. When the patient's details are known, the HAPIS queries the DVPA-r on whether it holds an authorization for this patient. If no patient is found, the HAPIS will continue. If the patient is found (with an appropriate patient record type or PS), the HAPIS goes to step 1.

In step 1, the HAPIS instantiates the authorization. In step 1, as part of instantiating an authorization (invisble to the HAPIS), a push authorization URL is requested from the source.

An instantiated authorization is only usable by one person (GP or assistent) at the HAP. This means that when a call is opened multiple times by different people, different authorization instances must be created - one for each person that opens the call, except if the HAPIS has kept the result (patient record) of an earlier succesful request.

info

The copying principle is unique to Whitebox PA-URLs. However, the concept of authorization instantiation and the flow of operations that the DVPA-r provides is identical for other authorization systems.

The process of copying an authorization as part of authorization instantiation helps the source system that issued the original authorization to "track and trace" the authorization flow and the usage of authorizations at the HAP. It also triggers that at the source system, the patient record is 'prepared' (i.e., fetched at the source and encoded in the appropriate format) for retrieval, so that at a later time, the patient record can be delivered to the requesting party quickly.

At the time of instantiating an authorization, the authorization instance and the underlying PA-URL is not bound to a specific user yet. It is bound on first use.

In step 2, a user at the HAP invokes a method to obtain the patient record using the instantiated authorization. At the time of using an instantiated authotrization, if the user is authorized (subject to checks at the source), the underlying PA-URL is bound to the user, such that only this user can subsequently use this instance.

If another user needs so access the patient record using the original authorization, the process must start again with step 2, so that this other user can be vetted and bound to a new authorization instance.

Note that, for privacy reasons, the patient record retrieved by an assistent may be different from the patient record seen by a doctor, depending on how a GP has configured its Whitebox. It is also possible that an assistent gets to see no PS when a doctor (GP) does. It is therefore recommended to allocate space in a call's datastructure for two authorization instances, and two patient record states/patient records: one for when the call is opened by an assistent, and one for when a call is opened by a doctor.

info

Internal to Whitebox/the source, as part of step 2, a number of checks take place. When a call comes in, the source system first checks if the caller is bound; next, it implements a callback on the DVPA-r - or another component of the recipient provided for this - in order to check whether the party (person/GP) who invoked the request for obtaining the record is known to the HAP and (thus) authorized. Also, the source system checks the role, e.g., to check whether the user is a GP or an assistent using logic internal to the source system. This check determines if and what information may be returned as part of the professional summary (PS).

In step 3, the result of these checks is either an authorization failure, or that a patient record summary is delivered to the DVPA-r, which returns it to the calling HAPIS which presents the patient summary to the user. An error leaves the authorization in unbound 'ready' state (see call description below).

3. Flow of use

The typical flow of use of the DVPA-r is described below.

The flow consists of 5 steps:

0. Register an authorization
1. Search and filter available authorizations
2. Instantiate an authorization
3. Create an authentication token
4. Retrieve the patient record using the authorization instance

Step 0 is a preparatory phase, required before step 1 can be succesful. In step 0, a source system registers an authorization (i.e., a PA-URL) with the DVPA-r. The source system also registers metadata properties (e.g., BSN, name, date of birth) that can be searched using the DVPA-r. Normally this step is implicit and invisible to the user of the DVPA-r.

As an alternative to the source system registering an authorization, an authorization can be registered at HAP/HAPIS side. Using a DVPA-r call described in section 3.0, it is possible to enter an authorization code and a BSN. This authorization code allows the DVPA-r to 'fetch' an authorization and store this authorization in the DVPA-r, assuming the combination of BSN and authorization code checks out. Subsequently, the authorization is searchable using the BSN, and instantiatable using the same mechanisms (described below) as for an authorization registered by the source.

info

Authorization codes can go by different names, corresponding to different use case scenario's. The underlying mechanisms are identical, irrespective of the name. For example, a "verwijscode" may be given to an ambulance and/or placed in an "overdracht" (transfer message) by the GP. Also, the patient may carry an 'emergencycode' in his/her wallet, or put it in an SOS section on a mobile phone. Verwijscodes may even be passed over the phone, e.g., by a GP when sending in a patient. A phone may also be used to transfer an authorization when a GP possibly, at the HAP wants to authorize an SEH doctor access a professional summary of the patient. See onward authorization, sec. 3.5.

After entering an authorization code and a BSN in the DVPA-r, the DVPA-r obtains a PA-URL through a process involving the verwijscode.nl server. Assuming that the DVPA-r is connected to verwijscode.nl (by default this is the case) and the DVPA-r is authorized to query verwijscode.nl, verwijscode.nl returns the appropriate PA-URL, which next is registered in the DVPA-r. Details of verwijscode.nl are out of scope for this document.

In step 1, the HAPIS searches the DVPA-r for authorizations. This may be done using the BSN -- this is preferred since this works with authorization codes and also because the patient's details should normally be verified using the SVB-Z service for BSN verification -- or using other metadata stored in the DVPA-r.

In step 2, if an authorization (match) is found, this authorization must be instantiated before it can be used to, in particular, retrieve the patient record that the authorization is associated with.

info

Besides for obtaining a patient record, an instantiated authorization is required to send information for the corresponding patient back to the GP (pushback) - think of, e.g., transcripts of consultations including issued medication. An instantiated authorization is also needed to be able to obtain an authorization code (verwijscode) to provide an authorization to a subsequent party (e.g., when transfering a patient from the HAP to the emergency ward / SEH). Any such actions may only be done by a user, e.g., a GP or a (mandated) assistent, and require an instantiated authorization.

Step 3 is a preparatory step before retrieving a patient record (or for invoking another operation) using an instantiated authorization. In step 3, a token is created that must be signed and passed to the DVPA-r in subsequent operations.

info

Steps 1-3 are executed by the system (HAPIS) that interacts directly with the DVPA-r, using system authentication. The HAPIS can use the DVPA-r using a client certificate (over a mTLS connection) that is pinned at DVPA-r installation time. (see onboarding). Step 4, or more specifically, all operations that make use of an instantiated authorization, require a signed token for authenticating and identifying a user.

Steps 4 require an instantiated authorization and an authentication token to identify the user which invokes the operation. This token is passed by the DVPA-r to the source system so that it can authenticate the user at the time it invokes an operation. This token will in practice be signed using an UZI pass, using SafeSign or ZorgID. The token format and the mechanism to create a token for signing is described in detail in section 3.7.

The operations corresponding to the steps above are discussed in detail below.

3.0 Inserting an authorization using an authorization code

The DVPA-r provides a way to insert an authorization to the registry in the using an authorization code. This is an alternative to where a source system registers an authorization directly with the DVPA-r.

If a user receives an authorization code, it can instruct a client (the HAPIS) to send it, together with the patients BSN to the /dvpa-r/v1/authorization-codes endpoint using an HTTP POST request.

The DVPA-r in response checks with the authorization code service if the authorization code, in combination with the provided BSN, is valid. If all checks out the DVPA-r retrieves the underlying PA-URL from the authorization service and adds an Authorization to the registry which can be queried and instantiated like all other Authorizations (see step #3.1).

Note that the resulting Authorization lacks patient metadata besides the provided BSN, so it can be only queried by BSN.

Input:

The body of the request is in the form of a JSON object with the following fields:

  • authorizationCode (string) required
    The authorizationCode field should contain the received authorization code
  • patientBSN (string) required
    The patient's BSN to which the authorization code belongs to

Output:

If everything goes well a 201 Created statuscode is returned togheter with a Location header pointing to the newly created authorization

If something goes wrong a 400 status code togheter with an ErrorResponse is returned with more details on the error that occured

Possible errors are:

The invalid-arguments error type can indicate that one or more fields are missing.

The invalid-auth-code error type indicates that the provided authorizationCode is invalid (i.e., the combination of BSN and authorization code, resulting in a unique pseudonym, does not yield a result/URL when querying verwijscode.nl).

Example:

If a patient with BSN 99998883 provided a user of the HAPIS with an authorization code: 128315 the following request should me made:

POST /dvpa-r/v1/authorization-codes

{
"authorizationCode": "128315",
"patientBSN": "99998883"
}

Assuming the authorization code is valid the following response can be expected

201 Created
Location: /dvpa-r/v1/authorizations/2345-fghj
...

3.1 Search available authorizations

A user can search for authorizations stored in the DVPA-r by making a HTTP GET request to the /dvpa-r/v1/authorizations endpoint. To filter the results, the user can provide one or more query parameters.

Input:

Supported query parameters:

  • patientRecordType (string)
    Type of the associated patient record
  • patientRecordFormat (string)
    Format of the associated patient record
  • patientBSN (string)
    The patient's BSN
  • patientName (string)
    The patient's full name
  • patientBirthdate (string)
    The patient's date of birth (in yyyy-mm-dd format)
  • patientStreet (string)
    The patient's streetname
  • patientHouseNumber (string)
    The patient's housenumber
  • patientPostalCode (string)
    The patient's postal code (in 1234AA format)
  • patientCity (string)
    The patient's city
  • patientGender (string)
    The patient's gender
  • mostRecent (boolean: true|false)
    mostRecent=true selects the most recent record if patientRecordType=NHG-PS

Result

Depending of the available authorizations, zero or more metadata structs describing zero or more authorizations that match the search criteria are returned.

These structs are returned formatted as JSON objects containing the following fields:

  • createdOn (number, Epoch Unix timestamp)
    Date of creation of this authorization in the DVPA-r
  • authorizationId (string)
    A unique identifier for this authorization
  • authorizationType (string)
    Type of the underlying authorization primitive. For now only PA-URL
  • source (object)
    An object that provides information about the source of the undelying patient record
  • source.systemUrl (string)
    The URL of the system that provided this authorization to the DVPA-r
  • source.organisationName (string)
    Name of the organisation that provided this authorization to the DVPA-r
  • patientRecordType (string)
    Type of the record this authorization provides access to
  • patientRecordFormats ([]string)
    List of possible representation formats of the record this authorization provides access to. The formats are expressed as a list of MIMEtypes
  • pushbackTypes ([]string)
    List of supported pushback formats for the underlying patient record. The formats are expressed as a list of MIMEtypes. This list can be empty which indicates that pushback is not supported for this patient record
  • expiresAt (number, Epoch Unix timestamp)
    Epoch Unix timestamp on which this authorization expires
  • patient (object)
    Metadata about the patient who is the subject of the record this authorization provides access to
  • patient.registrationDate (number, Epoch Unix timestamp)
    Epoch Unix timestamp indicating the date the patient was registered in the source system
  • patient.birthdate (string)
    The patient's birthdate
  • patient.bsn (string)
    The patient's BSN
  • patient.city (string)
    The patient's city
  • patient.gender (string, male|female|unknown|other)
    The patient's gender
  • patient.houseNumber (string)
    The patient's housenumber
  • patient.name (string)
    The patient's full name
  • patient.postalCode (string)
    The patient's postal code (in 1234AA format)
  • patient.street (string)
    The patient's streetname

Example

If a user wants to search for authorizations for a patient with BSN 999998882 it can make the following call:

GET /dvpa-r/v1/authorizations?patientBSN=99998882

In this example there are authorizations available for this patient which results in the following response.

200 OK

[
{
"createdOn": 1674471353,
"authorizationId": "1234-abcd",
"authorizationType": "PA-URL",
"source": {
"uri": "https://vrbld-amsterdam.mcs-net.nl",
"organisationName": "Voorbeeld Praktijk X."
},
"patientRecordType": "AMO",
"patientRecordFormats": ["application/EDIFACT"],
"pushbackTypes": [],
"expiresAt": 1675680953,
"patient": {
"bsn": "99998882",
"name": "Jan B. Test",
"birthdate": "11-11-1980",
"gender": "male",
...
}
},
{
"createdOn": 1674471372,
"authorizationId": "2345-bcde",
"authorizationType": "PA-URL",
"source": {
"systemUrl": "https://vrbld-amsterdam.mcs-net.nl",
"organisationName": "Voorbeeld Praktijk X."
},
"patientRecordType": "NHG-PS",
"patientRecordFormats": ["application/vnd.wbx.hl7v3"],
"pushbackTypes": ["application/vnd.wbs.soep+json"],
"expiresAt": 167568097,
"patient": {
"bsn": "99998882",
"name": "Jan B. Test",
"birthdate": "11-11-1980",
"gender": "male",
...
}
}
]

Note that in this case, two metadata structs are returned for the same patient (BSN), for the different patientRecordTypes AMO and NHG-PS. The client (HAPIS) should select the best suitable format (patientRecordType) and instantiate this authorization using the authorizationId (sect. 3.2).

In case of a patientRecordType NHG-PS, there should normally only be one result, since one patient only has one GP. In case multiple authorizations with patientRecordType NHG-PS exist, the most recent PS should be used. (This situation may occur when, for example, a patient moves from one GP to another and is not yet deregistered from the old GP). It is not straightforward to determine which of two PS'es is the most recent one, since several (erroneous) edge cases may exist. Therefore, the DVPA-r provides a query parameter mostRecent=true; when this parameter is used, the DVPA-r will attempt to determine and return the authorization of (only) the most recent PS, using an internal heuristic based on a combination of creationOn and when the PS was last modified. Please note that we do not guarantee the correctness of this heuristics, failure conditions may occur that may result in the wrong PS to be returned by the call. If this possibility is to be excluded, the user should be involved in verifying/selecting the correct PS from a list, and/or by showing multiple PS'es to the user.

Please note: the query parameter mostRecent may be used only in combination with the parameter patientRecordType=NHG-PS; else, the call will return an error (invalid-arguments).

3.2 Instantiate an authorization

Before an operation can be executed on an authorization (e.g. to retrieve the underlying patient record or push data back to the source), a user must search for an authorization for that patient record using the /dvpa-r/v1/authorizations endpoint and create an instance of that authorization. This is realised by making a HTTP POST request to the /dvpa-r/v1/authorizations endpoint with query parameters.

A requirement is that when the query parameters are evaluated (applied as a filter on all available authorizations) results in exactly one authorization. If the evaluation of the query parameters results in multiple authorizations the response will return an error indicating if there was no match (HTTP 404) or if there were multiple matches, an HTTP 400 with an ErrorResponse.

The query can be narrowed down to ensure a single match by making a HTTP GET request the /dvpa-r/v1/authorizations endpoint and search for a single authorization. See step #3.1 for more information.

If the desired authorization is found it can be targeted by providing its authorizationId as a query parameter. See the examples below for an example of this scenario.

On a successfull request a 201 Created response is returned and a Location header is provided with a link to the created instance.

Input:

Supported query parameters:

  • authorizationId (string)
    The unique identifier for an authorization
  • patientRecordType (string)
    Type of the associated patient record
  • patientRecordFormat (string)
    Format of the associated patient record
  • patientBSN (string)
    The patient's BSN
  • patientName (string)
    The patient's full name
  • patientBirthdate (string)
    The patient's date of birth (in yyyy-mm-dd format)
  • patientStreet (string)
    The patient's streetname
  • patientHouseNumber (string)
    The patient's housenumber
  • patientPostalCode (string)
    The patient's postal code (in 1234AA format)
  • patientCity (string)
    The patient's city
  • patientGender (string)
    The patient's gender

Example of a call where the id of the to be instantiated authorization is already known:

If the following request is done and assuming there is an authorization with the id of abcd-12234

POST /dvpa-r/v1/authorizations?authorizationId=abcd-1234

The following result can be expected, indicating that an authorisation instance with an id of 5678-efgh is created and that it can be accessed on the URL: /dvpa-r/v1/authorizationInstances/5678-efgh.

201 Created
Location: /dvpa-r/v1/authorizationInstances/5678-efgh

Example of one match:

A request is made to obtain an authorization for a patient record which has a subject with a BSN of 99998883.

POST /dvpa-r/v1/authorizations?patientBSN=9998883

The call returns a valid response because the query matches exactly one patient record.

201 Created
Location: /dvpa-r/v1/authorizationInstances/1234-abcd

Example of multiple matches:

A request is made to obtain an authorization for a patient record with BSN 99998882.

POST /dvpa-r/v1/authorization?patientBSN=9998882

The system holds multiple authorizations where the underlying patient record have a subject with a BSN of 99998882 so it returns an error response.

400 Bad Request

{
"type: "multiple-matches"
"title": "Multiple matches were found",
"status": 400,
"detail": "Multiple matches were found, query evalutation must result in exactly one match"
}

The user calls the /dvpa-r/v1/authorizations endpoint to collect information to refine its query.

GET /dvpa-r/v1/authorizations?patientBSN=99998882

The call returns all available records that matches this query (patientBSN=99998882).

200 OK

[
{
"authorizationId": "1234-abcd",
"patient": {
"bsn": "99998882",
"name": "Jan B. Test",
"birthdate": "11-11-1980",
"gender": "male",
...
}
...
},
{
"authorizationId": "2345-bcde",
"patient": {
"bsn": "99998882",
"name": "Jan B. Test",
"birthdate": "11-11-1980",
"gender": "male",
...
}
...
}
]

The user selects the desired authorization and updates the query to select the authorization with an id of 1234-abcd.

POST /dvpa-r/v1/authorization?authorizationId=1234-abcd

Now the call returns a valid response because after the update the query matches exactly one authorization.

201 Created
Location: /dvpa-r/v1/authorizationInstances/9012-jklm

3.3 Inspecting an authorizationInstance

Once an instance is created, the resulting instance can be inspected by making a HTTP GET request to the /dvpa-r/v1/authorizationInstances/{id} endpoint. As a result to that call a JSON representation of that instance is returned.

Using this call is not required.

A instance has the following properties:

  • createdOn (int, Epoch Unix timestamp)
    Epoch Unix timestamp of when this instance was created
  • instanceId (string)
    Identifier of this specific instance
  • authorizationId (string)
    Identifier of the authorization this resource is an instance of
  • state (string)
    Indicates in which state this instance currently is in. There are four possible values: ready, bound, expired, invalidated. ready means that this instance is ready to be used (and bound); expired means that this instance cannot be used anymore because it is expired; and invalidated means that the authorization this resource in an instance of is not valid anymore and this instance cannot be used anymore. bound is a state set by the DVPA-r: once a user has made use of an authorizationInstance, only this user may invoke subsequent operations on this instance. If another user has to make use of an authorization, first a new instance most be created. Note that binding is implicit: binding takes place on first use of an authorizationInstance, so there is no explicit "bind" operation available to programmers. Practically, the bound state may be interpreted as 'ready'.
  • boundToType (string)
    A PA-URL can be bound to several properties, depending on context: a key or certificate fingerprint, a common name (implicitly assuming a CA or identity management system to validate the idenity purveyed by this name), a UZI-ID. etc. boundToType indicates what property or properties a PA-URL is bound to (e.g., organization name ("CN"), organization URA ("URA"), healthcare professional UZI ("UZI"), key fingerprint, etc.)
  • boundTo (object)
    Depending on boundToType, boundTo contains the actual information that a PA-URL is bound to; the boundTo object is represented as a JSON struct with always at least the properties subject (to contain a key fingerprint, an uzi-id etc), and a description. Depending on boundToType, this JSON struct may be extended.
  • boundTo.subject (string)
    The actual value of the property this instance is bound to. E.g. the actual key-fp or uzi-id
  • boundTo.description (string)
    (Human-readable) name of the subject, typically the CN as present on a UZI pass.
  • boundTo. (any)
    As stated before the boundTo object can have different fields depending on the different boundToTypes.
  • expiresAt (int, Epoch Unix timestamp)
    Epoch Unix timestamp of when this instance expires

Example:

If an instance with the id of 9012-jklm is created it can be inspected by making the following request:

GET /dvpa-r/v1/authorizationInstances/9012-jklm

This will result in the following response:

200 OK

{
"createdOn": 1673969928
"instanceId": "9012-jklm",
"authorizationId": "abcd-1234",
"state": "ready", // unbound, i.e., ready for use and binding
"expiresAt": 1673970928
}

3.4 Patient record retrieval

To retrieve a patient record a client can make a HTTP GET request to the /dvpa-r/v1/authorizationInstances/{id}/patient-record endpoint where it should provide the instanceId of the authorization instance associated with the patient record it wants to retrieve. With that request the client must provide proof of authorization (an authorization token) via the HTTP Authorization header.

If the authorization token is invalid an HTTP 401 Unauthorized statuscode is returned.

The client SHOULD also provide an Accept header indicating the desired format it wants to receive the patient record in. If the client requests an invalid format an HTTP 415 Unsupported Media Type statuscode is returned.

Example

GET  /dvpa-r/v1/authorizationInstances/9012-jklm
Accept: application/fhir+json
Authorization: Bearer eyJ4NWMiOlsiTUlJRXZUQ0NBdytnQ...

As stated before, the /dvpa-r/v1/authorizationInstances/{id}/patient-record endpoint requires a signed authorization token. In this example the Authorization header contains a WhiteboxJWT typed JWT serialized following the JWS Compact Serialization encoding mechanism described in RFC7515 as proof of authorization.

The structure of an authorization token is explained in section 3.7.

3.5 Obtaining a new authorization URL or -code for onward authorization.

Whitebox push authorizations (PA-URLs) provide a mechanism that allows a user to create a new authorization using the existing authorization. This allows a user to create a new authorization that may be forwarded to another party. An example where this is useable, is when a doctor at the HAP refers a patient to the emergency ward, assuming the emergency ward is another organization. By sending an authorization code, the recipient can claim the authorization (using an operation of the DVPA-r or by going to verwijscode.nl directly) and subsequently obtain the patient's information. It is important to note that issuing an authorization (in PA-terms called a copy) is done by the source system, and that the subsequent (forwarded) authorization also refers to this source. Essentially, all security (and privacy) critical operations go via the source.

Onward authorisation is subject to several security and policy checks at the source, and may yield an error if not allowed. In the 'happy flow', invoking a copy operation on an authorizationInstance results in a push-authorization URL or, more likely, in an authorization code that may be forwarded to another party, in one of several ways (by phone, in a referral letter, ...).

A client can copy an authorization by making a HTTP POST request to the /dvpa-r/v1/authorizationInstances/{id}/copy-v1 endpoint.

Input:

This endpoint expects a JSON body with the following field:

  • resultType (string)
    In this field the client can specify what type of result it desires. At this moment there are 2 possible types: PA-URL for a push authorization url and AUTH-CODE for an authorization code (AUTH-CODE) that can be resolved using verwijscode.nl
info

In later versions of this call, additional arguments may be specified, such as the name of an organization that an onward authorization should be pre-bound to, or an endpoint to which the authorization should be sent electronically. Such arguments are future work.

Output:

Depending on the provided resultType the copy-v1 call returns different output.

If the provided resultType was PA-URL, a JSON object with the following fields can be expected:

  • url (string, url)
    The actual push authorization URL

If the provided resultType was AUTH-CODE a JSON object with the following fields is returned:

  • serviceURL (string, url)
    The URL of the service where the authorization code can de redeemed by the receiver; this is relevant for printing on a referral letter, for example.
  • authorizationCode (string)
    The actual authorization code

Example using resultType as PA-URL

POST /dvpa-r/v1/authorizationInstances/1234-abcd/copy-v1

{
"resultType": "PA-URL"
}
200 OK

{
"url": "https://vrbld-source/mdlink/LKJDLKF987..."
}

The URL that is returned is an unbound (late bound) PA-URL. This method (URL) is only to be used (forwarded to another party) if the transport path to this party is secure - for example, when sending an emergency referral ("spoedverwijzing") to a known SEH using a secure transfer service.

Example using resultType as AUTH-CODE

POST /dvpa-r/v1/authorizationInstances/1234-abcd/copy-v1

{
"resultType": "AUTH-CODE"
}
200 OK

{
"serviceURL": "https://verwijscode.nl"
"authorizationCode": "87123456"
}

The result can be printed on a referral note or -letter, or passed to the recipient in another way.

info

Verwijscode.nl is the default server voor resolving authorization codes (as implemented in the DVPA-r). The server may differ for particular contexts, e.g., think of zorggroepdesnoek.verwijscode.nl for use in a zorggroep-wide referral system, or emergencycode.nl specific for emergency care. The default server is verwijscode.nl; this is configured for use in the DVPA-r. In case of another service than verwijscode.nl (e.g., in a 'zorggroep'), this other service can be configured in the respective DVPA's. In case of an authorization code that cannot be resolved using verwijscode.nl, a fallback is that the dossier corresponding to this code can always be retrieved (opened) using a web browser. Emergencycode.nl authorization codes can be used by verwijscode.nl and thus by the default DVPA-r.

The serviceURL (and the authorizationCode) can, for example, be printed on a referral letter issued to the patient. Note that if an authorization code is printed out, the BSN MUST NOT be printed on the same referral letter.

info

Onward authorisation is a complex, policy governed operation. Most of the details are hidden inside the PA-URL mechanisms and the DVPA-r.

Aspects that are evaluated by a policy are strict checks on who may request a copy, for example a general practitioner at a HAP may request an authorization but an assistent may not; and on who may subsequently use the authorization (e.g., the recipient may be required to be a specialist at the SEH). Finally, the recipient of an onward authorization may be able to view less information - also subject to role-based access control - typically, a medication overview and allergy information will be visible as a minimum, but other information (which may be visible to the referring general practitioner at the HAP, e.g., journal entries and the episode list) may not be visible to a specialist at the SEH.

We versioned the copy call as the current version is relatively simple, with policy aspects set to default values. Later versions of this call may add arguments or semantics.

3.6 Pushback

Pushback allows a HAPIS to push back information to the source system regarding a specific patient. This normally happens after consultation. Supported messages are simple JSON structs that contain zero or more SOEP lines or one message containing only free text (intended as a free-text note).

POST /dvpa-r/v1/authorizationInstances/{id}/pushback

This operation should be implemented with care, as not all source systems can process return messages. Therefore, an error code may be returned which must be consulted by the caller; if an error occurs, the return message should be submitted in the regular way (e.g., using an Edifact/mail message service).

[The definition of this call and its arguments will be provided at later time.]

3.7 Token structure and content

To retrieve the patient record the user needs to authenticate itself.

How a user authenticates itself depends on the type of patient record and which system holds the data. At the moment the only supported system is a Whitebox and for a Whitebox PA-URL a user needs to be a careprovider subscribed to the dutch UZI-register. Members of the UZI-register get a smartcard, a so called UZI-card. To proof that the user is actually in the UZI-register they have to sign a token with that the key that this card holds.

The token in the case of a Whitebox is a JWT with the following header and payload.

This JWT contains the following header fields:

  • x5c (string, base64 encoded)
    Certificate chain of UZI certificate which is used to sign this token in PEM format (excluding UZI-register CA)
  • alg (string)
    Type of algoritm used for the signing of this JWT. Allowed values are: PS512 | PS256 | PS384 | RS512 | RS384 | RS256

This JWT contains the following claims:

  • url (string)
    The actual underlying PA-URL
  • challenge (string)
    The challenge that is linked to the PA-URL, Whitebox specific implementation detail
  • timestamp (string)
    Timestamp of signing
  • authorizationSubject (string)
    BSN of patient of whom the patient record holds data
  • user (string)
    A JSON object contaning information about the user that tries to retrieve information with this authorization
  • user.uzi_id (string)
    UZI id contained in UZI-card certificate
  • user.uzi_ura (string)
    URA contained in UZI-card certificate
  • system (string)
    Information about the DVPA-r
  • system.key_fp (string)
    Fingerprint of DVPA-r's system certificate

An example of these claims:

{
"url": "https://vrbld-amsterdam.mcs-net.nl/a6516we98r4w9e8/HL7v3-PS",
"challenge": "4257+pGn6EDeoiHYGKckFQ",
"timestamp": 1441493365,
"authorizationSubject": "urn:oid:2.16.840.1.113883.2.4.6.3:99998883",
"user": {
"uzi_id": "90035499",
"uzi_ura": "90000383"
},
"system": {
"key_fp": "sha256:4dc4e1d1625a05bc5a51c7cf51002615679502c73ae7637d4bcee30e93408f50"
}
}

There are two ways to acquire the necessary data to construct such an authorization token.

  1. Fetch raw authorization data and construct the token from scratch
  2. Fetch a partially prefilled token and fill in the missing data

1. Retrieval of raw (authorization) data for token creation

If the consumer of the DVPA-r is aware of the details of the underlying authorization primitive it can make an HTTP GET request to the /dvpa-r/v1/authorizationInstances/{id}/raw-data endpoint to retrieve the raw data of the underlying authorization object. This data can be used to construct an authorization token if the caller knows how to construct all the claims for that token.

In the case of an authorization of type PA-URL call will return the following fields:

  • url (string)
    The actual underlying PA-URL
  • challenge (string)
    A challenge is a unique string, generated by the source Whitebox that the URL points to, which must be included in the claims of the JWT.

Example

In the case there is an authorization with the id of 1234-abcd

GET /dvpa-r/v1/authorizationInstances/1234-abcd/raw-data

It will result in the following response

200 OK

{
"url": "https://vrbld-amsterdam.mcs-net.nl/a6516we98r4w9e8/HL7v3-PS",
"challenge": "4257+pGn6EDeoiHYGKckFQ"
}

2. Using the tokenData endpoint to obtain a pre-formatted, partially filled-in token

To prevent that the client has to obtain and format all the information by itself, the DVPA-r provides a utility method that provides a (partially) pre-filled token:

To make use of this utility method the user makes a HTTP GET request to the /dvpa-r/v1/authorizationsInstances/{id}/token-data endpoint. Where {id} is the id of the authorization instance for which an authorization token is required.

info

To mitigate the risk that the DVPA-r as a system can request whatever record it wants by modifying the claims, and also because the DVPA-r can't prefill all fields (e.g. the x5c and alg properties in the header) the client has to complement some properties, such as the BSN of the patient for whom it requests a patient record.

The response of this call returns a JSON object with the following fields

  • toComplement (string)
    The toComplement holds a list of of JSON pointers (https://www.rfc-editor.org/rfc/rfc6901) to properties that need to be complemented by the client
  • tokenType (string)
    The tokenType is the type of the to be created token. Currently, only "JWT" is supported. The format refers to the general shape of the data property. For instance the "JWT" format indicates that the data property contains the 'allowedAlg', 'header' and 'claims' members. The tokenType also dictates how the token needs to be constructed.
  • tokenSubType (string)
    The tokenSubType indicates which fields the various data properties have. Currently only "WhiteboxJWT" is supported. The "WhiteboxJWT" subtype indicates that the data.claims property o.a. contains the 'url' and 'challenge' properties.
  • data (object)
    The data field holds the actual data that is needed as input for token creation. The content of the data property is dependent on the indicated tokenType and tokenSubtype

tokenType JWT details

If the tokenType is JWT the data property will contain the following fields:

  • allowedAlg ([]string)
    The allowedAlg field contains a list of allowed signing algorithms as defined in rfc7518
  • header (object)
    The header field contains the fields that should be present in the JWT's header
  • claims (object)
    The header field contains the fields that should be present in the JWT's claims

tokenSubType WhiteboxJWT details

If the tokenSubType is WhiteboxJWT the data.header and data.claims properties will contain the following fields:

data.header

  • data.x5c (string, base64 encoded)
    Certificate chain of UZI certificate which is used to sign this token in PEM format (excluding UZI-register CA)
  • data.alg (string)
    Type of algoritm used for the signing of this JWT. Allowed values are: PS512 | PS256 | PS384 | RS512 | RS384 | RS256

data.claims

  • claims.url (string)
    The actual underlying PA-URL
  • claims.challenge (string)
    The challenge that is linked to the PA-URL, Whitebox specific implementation detail
  • claims.timestamp (string)
    Epoch Unix timestamp of the moment of signing
  • claims.authorizationSubject (string)
    BSN of patient of whom the patient record holds data
  • claims.user (string)
    A JSON object contaning information about the user that tries to retrieve information with this authorization
  • claims.user.uzi_id (string)
    UZI id contained in UZI-card certificate
  • claims.user.uzi_ura (string)
    URA contained in UZI-card certificate
  • claims.system (string)
    Information about the DVPA-r
  • claims.system.key_fp (string)
    Fingerprint of DVPA-r's system certificate

Example:

If a client want to retreive a token template for an authorizationInstance with an id of 1234-abcd it can make the following call:

GET /dvpa-r/v1/authorizationInstances/1234-abcd/token-data

Assuming the authorizationInstance is valid and the underlying authorization is a PA-URL the following result is returned:

200 OK

{

"toComplement": [
"#/data/header/x5c", "#/data/header/alg",
"#/data/claims/timestamp", "#/data/claims/authorizationSubject",
"#/data/claims/user/uzi_id", "#/data/claims/user/uzi_ura"
],
"tokenType": "JWT",
"tokenSubtype": "WhiteboxJWT",
"data": {
"allowedAlg": ["PS512","PS256","PS384","RS512","RS384","RS256"],
"header": {
"x5c": "",
"alg": "",
},
"claims": {
"url": "https://vrbld-amsterdam.mcs-net.nl/a6516we98r4w9e8/HL7v3-PS",
"challenge": "4257+pGn6EDeoiHYGKckFQ",
"timestamp": 0,
"authorizationSubject": "",
"user": {
"uzi_id": "",
"uzi_ura": ""
},
"system": {
"key_fp": "sha256:4dc4e1d1625a05bc5a51c7cf51002615679502c73ae7637d4bcee30e93408f50"
}
}
}
}

As you can see some data (e.g. data.claims.url and data.claims.challenge) is already prefilled and some data (e.g. data.claims.timestamp and data.claims.user) must be complemented by the client.

As user should fill in the missing header and claim fields and construct and sign the JWT with that header and claim fields.

4 Additional calls

4.1-a Read transfer messages (overdrachtsberichten)

The DVPA-r can receive transfer messages (overdrachtsberichten) from other systems. These messages can be retrieved / viewed by a client my making a HTTP GET request to the /dvpa-r/v1/messages endpoint. The messages can also be searched by providing a subjectBSN query parameter.

Input:

Supported query parameters:

  • subjectBSN (string)
    BSN of patient this message is about

Output:

A HTTP GET request to the /dvpa-r/v1/messages endpoint returns a list of message objects containing zero or more, JSON formatted, message objects. A message object contains the following fields:

  • id (string, uuid) Unique identifier for this message
  • senderId (string) Unique identifier assigned by the sender
  • subjectBsn (string) BSN of subject (patient) of this message
  • date (number, Epoch Unix timestamp)
    Epoch Unix timestamp of when this event occured
  • sender (string) Name of the sender of this message
  • senderOrganisation (string) Name of the sender's organisation
  • messageFormat (string) MIMEtype that indicates the type of content the message field contains. For now the only supported MIMEtype is 'text/plain;charset=UTF-8'
  • message (string) Raw message data

Example:

To retreive a list of available messages a client makes the following call:

GET /dvpa-r/v1/messages

This will yield the following result:

200 OK

[
{
"id": "47307085-da23-4898-b145-e6b2436fef59",
"senderId": "229116a0-9b51-11ed-9cb0-00163e5e6c00",
"subjectBSN": "99998882",
"date": 1678686125
"sender": "J. Test",
"senderOrganisation": "Praktijk J. Test",
"messageFormat": "text/plain;charset=UTF-8",
"message": "Example message..."
}, {
"id": "1e20de55-fc9a-4f39-a8d2-97fe4a156b9c",
"senderId": "2d5e63ee-9b51-11ed-a4b9-00163e5e6c00",
"subjectBSN": "99998883",
"date": 1678686125,
"sender": "J. Test",
"senderOrganisation": "Praktijk J. Test",
"messageFormat": "text/plain;charset=UTF-8",
"message": "Example message..."
}
]

4.1-b Delete transfer messages (overdrachtsberichten)

Transfer messages can de deleted by making a HTTP DELETE request to the /dvpa-r/v1/messages/{id} endpoint. The deletes are 'hard' deletes meaning data deleted this way cannot be retrieved. On a successful delete a HTTP 200 OK statuscode is returned.

Note that undeleted read messages may be deleted after a DVPA-r-defined period of time, e.g., a month.

4.2 Logging

The DVPA-r provides a way to inspect its behaviour and the behaviour of its clients. It does this by implementing structured logging of events when they occur and by providing access to these logged events by implementing an endpoint where clients can search end view these events.

The caller (HAPIS) SHOULD be able present DVPA-r logging information to authorizes users.

A client can retreive logged events by making a HTTP GET request to the /dvpa-r/v1/logging endpoint it can search for specific events by providing query parameters to the request.

Input:

The returned events are paginated meaning that not all event are returned at once by default but they are devived in "pages" of a certain size.

The following query parameters can be used:

  • minTimestamp (string)
    Filter events based on a minimal timestamp. Meaning return only events with a timestamp equal or greater than the provided timestamp. Timestamp being a Epoch Unix timestamp
  • maxTimestamp (string)
    Filter events based on a maximum timestamp. Meaning return only events with a timestamp equal or smaller than the provided timestamp. Timestamp being a Epoch Unix timestamp
  • eventType (string)
    Filter events based on its type. Multiple event types can be provided by providing the eventType parameter multiple times. E.i. ?eventType=pushbackSuccess&eventType=pushbackFailed
  • perPage (number, default = 50)
    Indicates how many results should be returned per page. When omitted a default value of 50 is assumed
  • page (number, default = 1)
    Indicates which page should be returned. When omitted a default value of 1 is assumed, resulting in returning the first page. If a higher page than available is selected a HTTP 404 error code is returned indicating that this page does not exists.

Output:

As a result pagination metadata and a list of events is returned.

  • pagination (object)
    Metadata object that contains data about the pagination
  • pagination.currentPage (number)
    Current page
  • pagination.totalPages (number)
    Total number of pages for this specific query
  • pagination.totalResults (number)
    Total found events for this specific query
  • pagination.perPage (number)
    Inicates how many events are returned for a single "page"
  • events ([]object, list of events)
    List of event objects

An event object has the following fields:

  • id (string, uuid)
    Unqiue ientifier for this event
  • timestamp (number, Epoch Unix timestamp)
    Epoch Unix timestamp of when this event occured
  • type (string)
    Type of this event. For a full overview of types of events see the logging section
  • subject (object)
    Object that contains information about the subject of this event. E.g. a patient or a system
  • subject.type (string)
    Type of subject
  • subject.identifier (string)
    Identifier of this subject
  • subject.description (string)
    Human readable description of this subject
  • initiator (object)
    Object that contains information about the initiator of this event. E.g. a system or a user
  • initiator.type (string)
    Type of initiator
  • initiator.identifier (string)
    Identifier of this initiator
  • initiator.description (string)
    Human readable description of this initiator
  • details (object)
    A key value map with details about this event, which keys are available depend on the type of event. For a full list of event types and details that are available for those event types see the logging section
  • description (string)
    Human readable description of the event
info

The human-readable description of logging events should in most cases be sufficient to present these to users. It is recommended to log both structured details and human-readable events in the HAPIS in case these are needed later.

For some events (particularly, at the system level), manually logging in into the HAPbox or DVPA-r as a user or as an administrator may provide more detail than available via the calls listed here.

Example:

A client searches for events of type patientRecordViewedSuccess

GET /dvpa-r/v1/logging?eventType=patientRecordViewedSuccess

It will result in the following response

200 OK

{
"pagination": {
"currentPage": 1,
"totalPages": 2,
"totalResults": 96,
"perPage": 50,
},
"events": [
{
"id": "66a20a6c-4d64-4303-b879-78fe218a0340",
"timestamp": 1674138673,
"type": "patientRecordViewedSuccess",
"subject": {
"type": "patient",
"identifier": "99998883",
"description": "Jan B. Test (BSN=99998883)"
},
"initiator": {
"type": "uzi-identified-user",
"identifier": "90035499",
"description": "User with UZI card with ID 90035499"
}
"details": {
"authorizationId": "1234-abcd",
"patientRecordType": "NHG-PS",
...
}
"description": "A patient record of type NHG-PS for patient Jan B. Test (BSN=99998883) is viewed by user (UZI_ ID=90035499)"
},
...
]
}