RDC Interoperability Guide
1.4.0

Patient Service

A client application can use the PatientService to manage patient data. The service provides methods to create, read, update and search patient records. The endpoints are secured with the PHDIAM policy which means the client must provide valid credentials and a PHDIAM certificate to access the service. It allows following operations.

  • Create a new patient record
  • Read an existing patient record by FHIR ID
  • Update an existing patient record by FHIR ID
  • Search a patient by the patient ID in managing organization's system

Create a new patient record

To create a new patient record, the client can POST a Patient record to the server's Patient endpoint. For example:

curl -sS -X POST --json @patient.json "https://${server}/fhir/r5/api/Patient" \
  -H "client_id: ${client_id}" \
  -H "client_secret: ${client_secret}" \
  -H "org_id: ${org_id}" \
  -H "certificate: ${certificate}" \
  -H "X-Email-Notification: true" | jq

Where the request body patient.json is a JSON file with information about the patient.

X-Email-Notification header

The optional X-Email-Notification header controls whether an email notification related to a data sharing invite is sent to the newly created patient. It accepts the following values:

Value Behaviour
true An email related to the data sharing invite will be sent to the created patient.
false An email related to the data sharing invite will NOT be sent to the created patient.
Request body

The structure of the request body JSON is defined by the Roche's FHIR profile https://roche.com/fhir/iop/StructureDefinition/rdc-Patient, which is part of this implementation guide. The following example is a minimal JSON to create a Patient record with name, email and identifier, which contains the mandatory fields for the patient creation. The identifier.system is a String which is registered within the Roche system to identify the managing organization's system used for patient identification, and the identifier.value is a unique identifier for the patient within that system. Provided email also has to be unique.

{
"resourceType": "Patient",
"id": "rdc-Patient-Peter-minimal",
"meta": {
"profile": [
"https://roche.com/fhir/iop/StructureDefinition/rdc-Patient"
]
},
{
"system": "urn:oid:2.16.724.4.41",
"value": "5e3b4d8f-2c3a-4c8b-bd3e-9a1f8e7c6a2d"
}
],
"name": [
{
"family": "Chalmers",
"given": [
"Peter"
]
}
],
"telecom": [
{
"system": "email",
"value": "peter@email.com"
}
]
}

The following example includes all the fields that can be specified in the Patient resource, where besides the mandatory field we can set also values for gender, birthDate, and another telecom (phone number). The telecom field can contain at most 2 entries, one of them having system set to email and the other one having system set to phone. The contained Condition resource describes the patient's diabetes type by the code value in the snomed system, the allowed values for this code are specified in ValueSet https://roche.com/fhir/iop/ValueSet/diabetes-type-codes, which is part of this implementation guide.

{
"resourceType": "Patient",
"id": "rdc-Patient-Peter",
"meta": {
"profile": [
"https://roche.com/fhir/iop/StructureDefinition/rdc-Patient"
]
},
{
"resourceType": "Condition",
"id": "diabetes-type",
"meta": {
"profile": [
"https://roche.com/fhir/iop/StructureDefinition/rdc-Condition-diabetes-type"
]
},
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/condition-clinical",
"code": "active",
"display": "Active"
}
]
},
"code": {
"coding": [
{
"system": "http://snomed.info/sct",
"code": "46635009",
"display": "Diabetes mellitus type 1"
}
]
},
"subject": {
"reference": "#"
}
}
],
{
"system": "urn:oid:2.16.724.4.41",
"value": "5e3b4d8f-2c3a-4c8b-bd3e-9a1f8e7c6a2d"
}
],
"name": [
{
"family": "Chalmers",
"given": [
"Peter"
]
}
],
"telecom": [
{
"system": "phone",
"value": "(03) 5555 6473"
},
{
"system": "email",
"value": "peter@email.com"
}
],
"gender": "male",
"birthDate": "1974-12-25"
}

In case the request is successful, the server will respond with a 201 Created status code and the FHIR ID of the created patient will be returned in the Location header of the response. In case the request contains a JSON that does not conform to the profile or the unique condition for identifier or email values is violated, the server will respond with a 422 Unprocessable Entity status code with the following error message:

{
  "resourceType": "OperationOutcome",
  "issue": [
    {
      "severity": "error",
      "code": "invalid",
      "diagnostics": "Patient.identifier: minimum required = 1, but only found 0 (from https://roche.com/fhir/iop/StructureDefinition/rdc-Patient|1.0.0)",
      "expression": [
        "Patient"
      ]
    }
  ]
}

Read an existing patient record by FHIR ID

To fetch an existing patient record by its FHIR ID, the client can send a GET request to the server's Patient endpoint with the FHIR ID of the patient. For example:

curl -sS "https://${server}/fhir/r5/api/Patient/${patient_fhir_id}" \
  -H "client_id: ${client_id}" \
  -H "client_secret: ${client_secret}" \
  -H "org_id: ${org_id}" \
  -H "certificate: ${certificate}" | jq

Where ${patient_fhir_id} is the FHIR ID of the patient to be fetched. The response will be a JSON of Patient resource in the FHIR R5 format conforming to the Roche's FHIR profile https://roche.com/fhir/iop/StructureDefinition/rdc-Patient, for example:

{
"resourceType": "Patient",
"id": "rdc-Patient-Peter",
"meta": {
"profile": [
"https://roche.com/fhir/iop/StructureDefinition/rdc-Patient"
]
},
{
"resourceType": "Condition",
"id": "diabetes-type",
"meta": {
"profile": [
"https://roche.com/fhir/iop/StructureDefinition/rdc-Condition-diabetes-type"
]
},
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/condition-clinical",
"code": "active",
"display": "Active"
}
]
},
"code": {
"coding": [
{
"system": "http://snomed.info/sct",
"code": "46635009",
"display": "Diabetes mellitus type 1"
}
]
},
"subject": {
"reference": "#"
}
}
],
{
"system": "urn:oid:2.16.724.4.41",
"value": "5e3b4d8f-2c3a-4c8b-bd3e-9a1f8e7c6a2d"
}
],
"name": [
{
"family": "Chalmers",
"given": [
"Peter"
]
}
],
"telecom": [
{
"system": "phone",
"value": "(03) 5555 6473"
},
{
"system": "email",
"value": "peter@email.com"
}
],
"gender": "male",
"birthDate": "1974-12-25"
}

Update an existing patient record by FHIR ID

Similarly to Patient record creation, to update a patient record, the client can send a PUT request to the server's Patient endpoint. For example:

curl -sS -X PUT --json @patient.json "https://${server}/fhir/r5/api/Patient/${patient_fhir_id}" \
  -H "client_id: ${client_id}" \
  -H "client_secret: ${client_secret}" \
  -H "org_id: ${org_id}" \
  -H "certificate: ${certificate}" | jq

Where ${patient_fhir_id} is the FHIR ID of the patient to be updated and patient.json is a JSON file with the updated information about the patient, the full JSON of patient resource should be provided, not just the fields to be updated. In successful case, the server responds with 200 OK status. In case the record with ${patient_fhir_id} FHIR ID does not exist, the server responds with a 404 Not Found status code. In case the patient.json JSON file contains invalid data, the 422 Unprocessable Entity status code is returned with an error message, for example:

{
  "resourceType": "OperationOutcome",
  "issue": [
    {
      "severity": "error",
      "code": "invalid",
      "diagnostics": "Patient.name: minimum required = 1, but only found 0 (from https://roche.com/fhir/iop/StructureDefinition/rdc-Patient|1.0.0)",
      "expression": [
        "Patient"
      ]
    }
  ]
}

Search a patient by the patient ID in managing organization's system

An EMR can search for a patient using the patient ID in managing organization's system (the external identifier stored in the identifier field during patient creation). The identifier query parameter accepts two formats:

Format Example Description
value 5e3b4d8f-2c3a-4c8b-bd3e-9a1f8e7c6a2d Pass the identifier value directly.
system|value urn:oid:2.16.724.4.41|5e3b4d8f-2c3a-4c8b-bd3e-9a1f8e7c6a2d Include the identifier system. The system must match the one configured for the managing organization; a mismatch results in 400 Bad Request.

Only a single identifier value is supported per request.

GET request

curl -sS "https://${server}/fhir/r5/api/Patient?identifier=urn:oid:2.16.724.4.41%7C5e3b4d8f-2c3a-4c8b-bd3e-9a1f8e7c6a2d" \
  -H "client_id: ${client_id}" \
  -H "client_secret: ${client_secret}" \
  -H "org_id: ${org_id}" \
  -H "certificate: ${certificate}" | jq

Notice in this example that the pipe character | needs to be URL encoded as %7C.

POST request (FHIR search via form-encoded body)

curl -sS -X POST \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "identifier=urn:oid:2.16.724.4.41|5e3b4d8f-2c3a-4c8b-bd3e-9a1f8e7c6a2d" \
  "https://${server}/fhir/r5/api/Patient/_search" \
  -H "client_id: ${client_id}" \
  -H "client_secret: ${client_secret}" \
  -H "org_id: ${org_id}" \
  -H "certificate: ${certificate}" | jq

Response — patient found

If the patient with given identifier is found for the organization of the request, the server responds with 200 OK and a Bundle of type searchset with total set to 1 and the matching Patient resource as a match entry.

{
"resourceType": "Bundle",
"id": "rdc-Bundle-R5-Patient-Search-Result",
"type": "searchset",
"total": 1,
"link": [
{
"relation": "self",
"url": "/fhir/r5/api/Patient?identifier=urn:oid:2.16.724.4.41|5e3b4d8f-2c3a-4c8b-bd3e-9a1f8e7c6a2d"
}
],
"entry": [
{
"fullUrl": "urn:uuid:a3e1c2d4-5f6b-4a7e-8c9d-0b1e2f3a4b5c",
"resourceType": "Patient",
"id": "rdc-Patient-Peter",
"meta": {
"profile": [
"https://roche.com/fhir/iop/StructureDefinition/rdc-Patient"
]
},
{
"resourceType": "Condition",
"id": "rdc-Condition-diabetes-type",
"meta": {
"profile": [
"https://roche.com/fhir/iop/StructureDefinition/rdc-Condition"
]
},
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/condition-clinical",
"code": "unknown",
"display": "Unknown"
}
]
},
"code": {
"coding": [
{
"system": "http://snomed.info/sct",
"code": "46635009",
"display": "Diabetes mellitus type 1"
}
]
},
"subject": {
"reference": "#"
}
}
],
{
"url": "https://roche.com/fhir/iop/StructureDefinition/ext-DiabetesType",
"reference": "#rdc-Condition-diabetes-type"
}
}
],
{
"system": "urn:oid:2.16.724.4.41",
"value": "5e3b4d8f-2c3a-4c8b-bd3e-9a1f8e7c6a2d"
}
],
"name": [
{
"family": "Chalmers",
"given": [
"Peter"
]
}
],
"telecom": [
{
"system": "phone",
"value": "(03) 5555 6473"
},
{
"system": "email",
"value": "peter@email.com"
}
],
"gender": "male",
"birthDate": "1974-12-25"
},
"search": {
"mode": "match"
}
}
]
}

Response — patient not found

When no patient is found for the given identifier, the server still responds with 200 OK and a Bundle of type searchset with total set to 0. The bundle contains a single outcome entry with an OperationOutcome resource.

{
"resourceType": "Bundle",
"id": "rdc-Bundle-R5-Patient-Search-Not-Found",
"type": "searchset",
"total": 0,
"link": [
{
"relation": "self",
"url": "/fhir/r5/api/Patient?identifier=unknown-id"
}
],
"entry": [
{
"resourceType": "OperationOutcome",
"issue": [
{
"severity": "information",
"code": "not-found",
"diagnostics": "No patient found for the given identifier."
}
]
},
"search": {
"mode": "outcome"
}
}
]
}

Error responses

If the value of identifier parameter in the request is invalid, the server responds with 400 Bad Request. The identifier parameter is invalid if it is missing, empty, contains a comma (multiple identifiers), has an invalid system|value format, or the provided system does not match the managing organization's configured identifier system.