Skip to main content

Inviting an existing patient to become a user

Use Case Description

There may be some cases where a Healthcare Professional wants to invite one of his patients to the platform. If the Patient already exists in the platform, the MedTech SDK provides a method to automatically invite the User, link it to the existing Patient and then ask all the Healthcare Professionals that manage their data to share those with them.
The following diagrams summarizes the operations performed by the different actors.

Use Case Implementation

note

For this example, we assume that your database contains at least one Healthcare Professional, one Patient and one Data Sample associated to them.

The Doctor Invites the Patient

The first step in inviting a User is to create an instance of a class that implements the EHRLiteMessageFactory interface. This interface specify the methods to define the template of the email or SMS message that will be sent to the user when registering. It also allows to specify whether prefer to send the user an email or an SMS message.
The SDK also provides a pre-made implementation of this interface that you can use: iCureEHRLiteMessageFactory.
The class defined as such must be provided to the builder when instantiating the SDK.

class InvitationMessageFactory implements EHRLiteMessageFactory {
readonly preferredMessageType = 'email'

getPatientInvitationEmail(
recipientUser: User,
recipientPatient: Patient,
recipientPassword: string,
invitingUser: User,
invitingDataOwner: Organisation | Practitioner,
): EmailMessage {
return {
from: 'nobody@nowhere.boh',
subject: `${recipientUser.login}|${recipientPassword}`,
html: `User: ${recipientUser.id}`,
}
}

getPatientInvitationSMS(
recipientUser: User,
recipientPatient: Patient,
recipientPassword: string,
invitingUser: User,
invitingDataOwner: Organisation | Practitioner,
): SMSMessage {
return {
message: `${recipientUser.login}|${recipientPassword}`,
}
}
}

let apiAsDoctor = await new EHRLiteApi.Builder()
.withICureBaseUrl(host)
.withUserName(userName)
.withPassword(password)
.withCrypto(webcrypto as any)
.withCryptoStrategies(new SimpleEHRLiteCryptoStrategies([]))
.withMsgGwUrl(msgGtwUrl)
.withMsgGwSpecId(specId)
.withMessageFactory(new InvitationMessageFactory())
.withAuthProcessByEmailId(authProcessId)
.build()

Then, it is possible to invite the User using the newly created factory object and the existing Patient.

await apiAsDoctor.userApi.createAndInviteFor(patient, 3600)

The User Logs in and Asks for Access

After that, the User will receive an email or a SMS message that contains their login and a short-lived authentication token that they can use to log in.

const anonymousMedTechApi = await new AnonymousEHRLiteApi.Builder()
.withICureBaseUrl(host)
.withMsgGwUrl(msgGtwUrl)
.withMsgGwSpecId(specId)
.withCrypto(webcrypto as any)
.withAuthProcessByEmailId(authProcessId)
.withAuthProcessBySmsId(authProcessId)
.withCryptoStrategies(new SimpleEHRLiteCryptoStrategies([]))
.build()

const authenticationResult =
await anonymousMedTechApi.authenticationApi.authenticateAndAskAccessToItsExistingData(
patientUsername,
patientToken,
)
const apiAsPatient = authenticationResult.api

The authenticateAndAskAccessToItsExistingData method will set up the User private and public key, it will create a long-lived authentication token, and will send a Notification to all the Healthcare Professionals that have a delegation for the Patient to ask access to their data.

The Patient has Limited Permissions Until he is Given Access to Existing Data

The patient can now start using iCure, but there are still some limitations to what he can do until the doctor gives him access to the existing data.

The patient can't:

  • access any existing observations and health elements
  • access encrypted data in his Patient entity

but the patient can:

  • create new observations and health elements
  • share newly created medical data with his doctor
  • access and modify non-encrypted data in his Patient entity

Initially, the patient won't be able to decrypt his own Patient entity, and for this reason the method PatientApi.get will fail. Instead, the patient needs to use the method PatientApi.getAndTryDecrypt which returns a PotentiallyEncryptedPatient which is an interface implemented by both Patient and EncryptedPatient. This method will try to decrypt the retrieved Patient, and if not successful instead of failing the method will just return the patient without decrypting it as an EncryptedPatient.

const patientUser = await apiAsPatient.userApi.getLogged()
// apiAsPatient.patientApi.getPatient would fail
const patientDetails = await apiAsPatient.patientApi.getAndTryDecrypt(patientUser.patientId)
patientDetails
{
"patient": {
"id": "97011d2d-4e31-4b0c-b659-179354669f7e",
"rev": "3-062cc635aa8a98fdc8b57bfbd9c179cc",
"identifiers": [],
"created": 1700058617470,
"modified": 1700058617470,
"author": "17e392da-8e36-4e4e-abd7-7eef3e434395",
"responsible": "3ed06450-17e5-47b6-ba4f-7a0a084df56b",
"tags": {},
"codes": {},
"names": [
{
"family": "Specter",
"given": [
"Marc"
],
"prefix": [],
"suffix": [],
"text": "Specter Marc",
"use": "official"
}
],
"languages": [],
"addresses": [
{
"addressType": "home",
"description": "London",
"notes": [],
"telecoms": [
{
"system": "email",
"value": "1c87da27@icure.com"
}
]
}
],
"gender": "unknown",
"birthSex": "unknown",
"mergedIds": [],
"active": true,
"deactivationReason": "none",
"personalStatus": "unknown",
"notes": [],
"relatives": [],
"patientPractitioners": [],
"patientProfessions": [],
"properties": {},
"systemMetaData": {
"hcPartyKeys": {},
"privateKeyShamirPartitions": {},
"secretForeignKeys": [],
"cryptedForeignKeys": {},
"delegations": {},
"encryptionKeys": {},
"aesExchangeKeys": {},
"transferKeys": {},
"securityMetadata": {
"secureDelegations": {},
"keysEquivalences": {}
},
"encryptedSelf": "KvoUihjaV3va4gg8L/yAqj+4aQbiSUjhoiDDpF8LdDM=",
"publicKeysForOaepWithSha256": [
"30820122300d06092a864886f70d01010105000382010f003082010a0282010100bf758af3ae5a5a7e5a1fbe91154efaf0f97fab7b898cc5b38f5671b5bde8c29226e9bb7e4a5b40ef6c4a84f3fd422173518f39e8c4632af4dead70eb47a696b98566e09328926d2c31437ce0a0ac6e6d46190bc3018cb9d645c0ce099d56fc361960735dc50859a0b68259af92607384708c64291bcf8ea1f44ab4d0e3d8b9ca1eaa6e64b96b0a3e2c83801b11cfd2f36cd6b23c97b75a82761a2d632769b4f60515d60951f251e2e19ee49006aaf7d76b0d43e6c28e58bcae8f0bb48f3656a66b571340d384f9fb3c19b860ccca88f99087f306204fdf73d5d43c975b5d588730d2a7d6a5cc0376e9d1da27ef08e820e67fb3ecef716b1abd79a8040e4826230203010001"
],
"tags": {}
},
"firstName": "Marc",
"lastName": "Specter"
},
"decrypted": false
}

It is also possible to for the patient to modify his non-encrypted data using the PatientApi.modifyPotentiallyEncryptedPatient method. Note however that any attempt to change data which should be encrypted according to the api configuration will cause a runtime error.

patientDetails.patient.personalStatus = PatientPersonalStatusEnum.COMPLICATED
// patientDetails.note = 'This would make modify fail'
const modifiedPatientDetails = await apiAsPatient.patientApi.createOrModify(patientDetails.patient)
modifiedPatientDetails
{
"id": "97011d2d-4e31-4b0c-b659-179354669f7e",
"rev": "4-6b03d4a4c46cbd97b5df1d79aa11b867",
"identifiers": [],
"created": 1700058617470,
"modified": 1700058617470,
"author": "17e392da-8e36-4e4e-abd7-7eef3e434395",
"responsible": "3ed06450-17e5-47b6-ba4f-7a0a084df56b",
"tags": {},
"codes": {},
"names": [
{
"family": "Specter",
"given": [
"Marc"
],
"prefix": [],
"suffix": [],
"text": "Specter Marc",
"use": "official"
}
],
"languages": [],
"addresses": [
{
"addressType": "home",
"description": "London",
"notes": [],
"telecoms": [
{
"system": "email",
"value": "1c87da27@icure.com"
}
]
}
],
"gender": "unknown",
"birthSex": "unknown",
"mergedIds": [],
"active": true,
"deactivationReason": "none",
"personalStatus": "complicated",
"notes": [],
"relatives": [],
"patientPractitioners": [],
"patientProfessions": [],
"properties": {},
"systemMetaData": {
"hcPartyKeys": {},
"privateKeyShamirPartitions": {},
"secretForeignKeys": [],
"cryptedForeignKeys": {},
"delegations": {},
"encryptionKeys": {},
"aesExchangeKeys": {},
"transferKeys": {},
"securityMetadata": {
"secureDelegations": {},
"keysEquivalences": {}
},
"encryptedSelf": "KvoUihjaV3va4gg8L/yAqj+4aQbiSUjhoiDDpF8LdDM=",
"publicKeysForOaepWithSha256": [
"30820122300d06092a864886f70d01010105000382010f003082010a0282010100bf758af3ae5a5a7e5a1fbe91154efaf0f97fab7b898cc5b38f5671b5bde8c29226e9bb7e4a5b40ef6c4a84f3fd422173518f39e8c4632af4dead70eb47a696b98566e09328926d2c31437ce0a0ac6e6d46190bc3018cb9d645c0ce099d56fc361960735dc50859a0b68259af92607384708c64291bcf8ea1f44ab4d0e3d8b9ca1eaa6e64b96b0a3e2c83801b11cfd2f36cd6b23c97b75a82761a2d632769b4f60515d60951f251e2e19ee49006aaf7d76b0d43e6c28e58bcae8f0bb48f3656a66b571340d384f9fb3c19b860ccca88f99087f306204fdf73d5d43c975b5d588730d2a7d6a5cc0376e9d1da27ef08e820e67fb3ecef716b1abd79a8040e4826230203010001"
],
"tags": {}
},
"firstName": "Marc",
"lastName": "Specter"
}

The patient can also create and share observation and health elements as normal:

const newCondition = await apiAsPatient.conditionApi.createOrModify(
new Condition({
description: "I don't feel so well",
codes: new Set([
new CodingReference({
id: 'SNOMEDCT|617|20020131',
type: 'SNOMEDCT',
code: '617',
version: '20020131',
}),
]),
openingDate: new Date('2019-10-12').getTime(),
}),
modifiedPatientDetails.id,
)
const sharedCondition = await apiAsPatient.conditionApi.giveAccessTo(newCondition, hcp.id)
// The doctor can now access the healthcare element
await apiAsDoctor.conditionApi.get(newCondition.id) // HealthcareElement...
newHealthcareElement
{
"id": "917f2946-f322-4590-bb9c-4e447ff44d5c",
"identifiers": [],
"rev": "1-6a2e6aa1ea9254a3fb67e735a4b5e6ed",
"created": 1700058623224,
"modified": 1700058623224,
"author": "*",
"responsible": "*",
"bodySite": {},
"tags": {},
"codes": {},
"healthcareElementId": "917f2946-f322-4590-bb9c-4e447ff44d5c",
"recordedDate": 20231115143023,
"openingDate": 1570838400000,
"description": "I don't feel so well",
"notes": [],
"systemMetaData": {
"secretForeignKeys": [
"59b07cf5-c131-4341-9812-c63e5cf08318"
],
"cryptedForeignKeys": {},
"delegations": {},
"encryptionKeys": {},
"securityMetadata": {
"secureDelegations": {},
"keysEquivalences": {}
},
"encryptedSelf": "osZYE6JLpfh77sBBbPxizmp7k3RmqjlDbZuiWkoDmiH/8upDs31CzAgAgHv2eHTqSDK7vn70tnepKqdIW14sAw==",
"tags": {}
}
}
sharedHealthcareElement
{
"id": "917f2946-f322-4590-bb9c-4e447ff44d5c",
"identifiers": [],
"rev": "2-1fc0849a836edeeace1fa26d6e3c7edb",
"created": 1700058623224,
"modified": 1700058623224,
"author": "*",
"responsible": "*",
"bodySite": {},
"tags": {},
"codes": {},
"healthcareElementId": "917f2946-f322-4590-bb9c-4e447ff44d5c",
"recordedDate": 20231115143023,
"openingDate": 1570838400000,
"description": "I don't feel so well",
"notes": [],
"systemMetaData": {
"secretForeignKeys": [
"59b07cf5-c131-4341-9812-c63e5cf08318"
],
"cryptedForeignKeys": {},
"delegations": {},
"encryptionKeys": {},
"securityMetadata": {
"secureDelegations": {},
"keysEquivalences": {}
},
"encryptedSelf": "osZYE6JLpfh77sBBbPxizmp7k3RmqjlDbZuiWkoDmiH/8upDs31CzAgAgHv2eHTqSDK7vn70tnepKqdIW14sAw==",
"tags": {}
}
}

However, if you only share the medical data the doctor will not be able to find when using filters: this is because the new data is created using a new secret foreign key that only the patient knows. In order to allow the doctor to find this new medical data you will have to share the secret foreign key using the PatientApi.giveAccessToPotentiallyEncrypted method.

const filterForHcpWithoutAccessByPatient = await new ConditionFilter(apiAsDoctor)
.forDataOwner(hcp.id)
.forPatients([await apiAsDoctor.patientApi.get(patient.id)])
.build()
const notFoundHEs = await apiAsDoctor.conditionApi.filterBy(filterForHcpWithoutAccessByPatient)
console.log(notFoundHEs.rows.find((x) => x.id == newCondition.id)) // undefined
// The patient shares his secret foreign key with the doctor
await apiAsPatient.patientApi.giveAccessTo(modifiedPatientDetails, hcp.id)
// The doctor can now also find the healthcare element
const filterForHcpWithAccessByPatient = await new ConditionFilter(apiAsDoctor)
.forDataOwner(hcp.id)
.forPatients([await apiAsDoctor.patientApi.get(patient.id)])
.build()
const foundHEs = await apiAsDoctor.conditionApi.filterBy(filterForHcpWithAccessByPatient)
console.log(foundHEs.rows.find((x) => x.id == newCondition.id)) // HealthcareElement...
notFoundHEs
{
"rows": []
}
foundHEs
{
"rows": [
{
"id": "917f2946-f322-4590-bb9c-4e447ff44d5c",
"identifiers": [],
"rev": "2-1fc0849a836edeeace1fa26d6e3c7edb",
"created": 1700058623224,
"modified": 1700058623224,
"author": "*",
"responsible": "*",
"bodySite": {},
"tags": {},
"codes": {},
"healthcareElementId": "917f2946-f322-4590-bb9c-4e447ff44d5c",
"recordedDate": 20231115143023,
"openingDate": 1570838400000,
"description": "I don't feel so well",
"notes": [],
"systemMetaData": {
"secretForeignKeys": [
"59b07cf5-c131-4341-9812-c63e5cf08318"
],
"cryptedForeignKeys": {},
"delegations": {},
"encryptionKeys": {},
"securityMetadata": {
"secureDelegations": {},
"keysEquivalences": {}
},
"encryptedSelf": "osZYE6JLpfh77sBBbPxizmp7k3RmqjlDbZuiWkoDmiH/8upDs31CzAgAgHv2eHTqSDK7vn70tnepKqdIW14sAw==",
"tags": {}
}
}
]
}
note

The patient needs to share his new secret foreign key with the delegate (using PatientApi.giveAccessToPotentiallyEncrypted) only once, and after the delegate will be able to find all new medical data created by the patient as long as the medical data itself has been shared with him.

Multiple calls to PatientApi.giveAccessToPotentiallyEncrypted from the same data owner with the same patient and delegate will have no effect.

The Doctor Receives the Notification and Gives Access

Once the Notification is sent, on the Doctor side you can use the MedTech SDK to filter his Notifications and get the one related to the new User.

const newNotifications = await apiAsDoctor.notificationApi.getPendingAfter()
const patientNotification = newNotifications.filter(
(notification) =>
notification.type === NotificationTypeEnum.NewUserOwnDataAccess &&
notification.responsible === patient.id,
)[0]
newNotifications
[
{
"id": "0f4d17e0-000a-4293-8c01-a570098cf87f",
"rev": "1-c974724265430b245f93c739feed4e89",
"status": "pending",
"created": 1700058616254,
"identifiers": [],
"modified": 1700058616254,
"author": "*",
"responsible": "*",
"properties": {},
"type": "KEY_PAIR_UPDATE",
"systemMetaData": {
"secretForeignKeys": [],
"cryptedForeignKeys": {},
"delegations": {},
"encryptionKeys": {},
"securityMetadata": {
"secureDelegations": {},
"keysEquivalences": {}
},
"encryptedSelf": "FQVyGnTJMOnnnEsz1UnaYs86BmuneifvT8e7i143/W70IPD8wHsCtReqpju6JK+i",
"tags": {}
}
},
{
"id": "1437f725-00ef-43aa-a82d-e7138d21af30",
"rev": "1-f9d714ebf39772895c324fe48731c697",
"status": "pending",
"created": 1700058616477,
"identifiers": [],
"modified": 1700058616477,
"author": "17e392da-8e36-4e4e-abd7-7eef3e434395",
"responsible": "3ed06450-17e5-47b6-ba4f-7a0a084df56b",
"properties": {},
"type": "OTHER",
"systemMetaData": {
"secretForeignKeys": [],
"cryptedForeignKeys": {},
"delegations": {},
"encryptionKeys": {},
"securityMetadata": {
"secureDelegations": {},
"keysEquivalences": {}
},
"encryptedSelf": "mqAKgG/l8yCFp9zyI65k+6cp7HNpZKF0fBAedmGCWsaPmVDmdHuybQQdeO8rysM4",
"tags": {}
}
},
{
"id": "36854fcc-463d-462d-85cc-ed16d3aa7f8b",
"rev": "1-2a162788959b03d053262590640dbff0",
"status": "pending",
"created": 1700058616399,
"identifiers": [],
"modified": 1700058616399,
"author": "17e392da-8e36-4e4e-abd7-7eef3e434395",
"responsible": "3ed06450-17e5-47b6-ba4f-7a0a084df56b",
"properties": {},
"type": "OTHER",
"systemMetaData": {
"secretForeignKeys": [],
"cryptedForeignKeys": {},
"delegations": {},
"encryptionKeys": {},
"securityMetadata": {
"secureDelegations": {},
"keysEquivalences": {}
},
"encryptedSelf": "WPls2VA7JBrldN4lp8uUE/AZded4tLV4J8QZ8RZRZ1lDMnE0UUjQcqt0WERVNjjI",
"tags": {}
}
},
{
"id": "3c8397c1-99f1-4e01-bda9-182be7797f6c",
"rev": "1-9c70c95a146d1cbe0df3ddead7f61a0d",
"status": "pending",
"created": 1700058616368,
"identifiers": [],
"modified": 1700058616368,
"author": "17e392da-8e36-4e4e-abd7-7eef3e434395",
"responsible": "3ed06450-17e5-47b6-ba4f-7a0a084df56b",
"properties": {},
"type": "OTHER",
"systemMetaData": {
"secretForeignKeys": [],
"cryptedForeignKeys": {},
"delegations": {},
"encryptionKeys": {},
"securityMetadata": {
"secureDelegations": {},
"keysEquivalences": {}
},
"encryptedSelf": "Eki9vHWNA+6hgEwqBSEx7syjAMekr0RyrMNwOgTWdevxw8zqqtd39XGKCSe76OmT",
"tags": {}
}
},
{
"id": "45b1f8b2-49b1-4f44-a502-81ee9ecb58f9",
"rev": "1-23fd600d74bb44725a15897ecab1b2a8",
"status": "pending",
"created": 1700058616420,
"identifiers": [],
"modified": 1700058616420,
"author": "17e392da-8e36-4e4e-abd7-7eef3e434395",
"responsible": "3ed06450-17e5-47b6-ba4f-7a0a084df56b",
"properties": {},
"type": "OTHER",
"systemMetaData": {
"secretForeignKeys": [],
"cryptedForeignKeys": {},
"delegations": {},
"encryptionKeys": {},
"securityMetadata": {
"secureDelegations": {},
"keysEquivalences": {}
},
"encryptedSelf": "m8uADFLoThZDSV3hJFp3hNbQLYpDg93AuFG9s9EUfHwWEE/jeQCyy+rraV06Mzy+",
"tags": {}
}
},
{
"id": "4d6c0f6e-9e17-41d3-9023-caa0718ae386",
"rev": "1-f4e46c25156dd9c3480876a35ee8834b",
"status": "pending",
"created": 1700058616339,
"identifiers": [],
"modified": 1700058616339,
"author": "17e392da-8e36-4e4e-abd7-7eef3e434395",
"responsible": "3ed06450-17e5-47b6-ba4f-7a0a084df56b",
"properties": {},
"type": "OTHER",
"systemMetaData": {
"secretForeignKeys": [],
"cryptedForeignKeys": {},
"delegations": {},
"encryptionKeys": {},
"securityMetadata": {
"secureDelegations": {},
"keysEquivalences": {}
},
"encryptedSelf": "36lZD5XT5MHfZdyrzO/FUoR9Y4voOhzu+dbM5dp835+zh8FkgtYCh1cWKJjpRqv9",
"tags": {}
}
},
{
"id": "59fc8317-7b69-46b6-9c9b-b8545cfb25b9",
"rev": "1-167e34e95b17ee9bd13dd6e732a1d84e",
"status": "pending",
"created": 1700058616384,
"identifiers": [],
"modified": 1700058616384,
"author": "17e392da-8e36-4e4e-abd7-7eef3e434395",
"responsible": "3ed06450-17e5-47b6-ba4f-7a0a084df56b",
"properties": {},
"type": "OTHER",
"systemMetaData": {
"secretForeignKeys": [],
"cryptedForeignKeys": {},
"delegations": {},
"encryptionKeys": {},
"securityMetadata": {
"secureDelegations": {},
"keysEquivalences": {}
},
"encryptedSelf": "x9kkscGo+dUVhknoYwpjwOmEfW3MtV5326+AEk9hJNFOL2tTIy8XqTV6ushL6ORm",
"tags": {}
}
},
{
"id": "5b5cb4e7-ea0d-4ce1-8ed6-9c1b1523dafb",
"rev": "1-05a84d91b9b4d0ad3dc4c773d4608e58",
"status": "pending",
"created": 1700058616285,
"identifiers": [],
"modified": 1700058616285,
"author": "*",
"responsible": "*",
"properties": {},
"type": "OTHER",
"systemMetaData": {
"secretForeignKeys": [],
"cryptedForeignKeys": {},
"delegations": {},
"encryptionKeys": {},
"securityMetadata": {
"secureDelegations": {},
"keysEquivalences": {}
},
"encryptedSelf": "Ms80pzAxBlM6YKHEa07/UBjpfHCINfSsFO7ZG99CXzr4SeaP1P+i7O/axAgxRLtI",
"tags": {}
}
},
{
"id": "78ed553b-5e42-492f-aa9d-ffef10fa9f27",
"rev": "1-0b4941bdedf275ec95d17ac4e7ee4ee3",
"status": "pending",
"created": 1700058616456,
"identifiers": [],
"modified": 1700058616456,
"author": "17e392da-8e36-4e4e-abd7-7eef3e434395",
"responsible": "3ed06450-17e5-47b6-ba4f-7a0a084df56b",
"properties": {},
"type": "OTHER",
"systemMetaData": {
"secretForeignKeys": [],
"cryptedForeignKeys": {},
"delegations": {},
"encryptionKeys": {},
"securityMetadata": {
"secureDelegations": {},
"keysEquivalences": {}
},
"encryptedSelf": "KxVznl/7KnABMXlCoFqon5Cc0tZY5W5o13TnMgQLbDDbHdS5GxMgkRw4dsVYStdz",
"tags": {}
}
},
{
"id": "93695877-5aed-489f-8752-bb12701fa494",
"rev": "1-c6ed0e6b3a47ca2ec0b47b307a2efdfc",
"status": "pending",
"created": 1700058599261,
"identifiers": [],
"modified": 1700058599261,
"author": "*",
"responsible": "*",
"properties": {},
"type": "KEY_PAIR_UPDATE",
"systemMetaData": {
"secretForeignKeys": [],
"cryptedForeignKeys": {},
"delegations": {},
"encryptionKeys": {},
"securityMetadata": {
"secureDelegations": {},
"keysEquivalences": {}
},
"encryptedSelf": "ujjYdFpdUBZ6o0LdGwLGRvXGKpBtfSsQ8S18pfBKYeFRfFljjeqlMnGmB0cxPKH8EuXaR48DgAXYm9B4eIR4mUAh0tyIa1ApORojqw2mIHhf1wIqSVYGkhNLFS4I5kd9z/ekQAtZvID6etagcECNf5UCu2cFa1pi0hrs10hgpgPrxzfQR9Z6v+ZlMQW7MmechtGKLZLLUhsl6kf8FA1Ctgkv5ob9X70xBZpGLKhRoBCbVYfA/uvt4rGgCClvMY6e/riLQydnyOVBg0AwQmu4FHvnvxbT9YnO1urskpUTXJwYQgWJBZ1x9VHA/E71hzeY//SRQoX0KXy8W3kRTa5IX9CsbNs89YpWo8+CZfYSgJljwWR32k6Egva7P9MpmtfoaCYav8LCYD5PvNMsonE6L0ZO2qESYXuc+I/6Mf8EMVBD6UV6E4wqN5Ku2B1cKOB6yMgorpfLB4dpDECAUtAXV7HEf1qUPSwtJMWqKkLDQIuUy1q5SQbSnX0rdPNwHbhZS9Y3M2krVxf6JR5nUkbyVQxL0k55LxfuTIZqB/Hg48/wVgZd7AcylXxrLJujLbDCsXPE23FNFuHFvtzK3cjxVUQp0FFctaCDKtWzov7+qt+88hKr8hmz64V46M68FwOVGNu+2LeoANLLuXub5vjUsCpUhSOryjmiwG28NG4Ec+BA/BBT7g9pV0nCau0nFyr6hjJycq/q83vLHSvjdmrZT8iUO/fcOfBD3EV4yPigx/ipjBwKy14ESAlQndSmKjAXJIMP6BjvNzJIBJ8wcMn1zSeoHkU77iNC6ndKwM2AlZZqzGhHWIIyO2SO+HYl6CaPDP97FVtTtveUmiCTIE3XCOfMqXDWin2x4vz1evIrsCNkT2zaiDfcCmgOAen9Aj0/Tc0MBIe2xTTIcJ2XJV3dbNb0bw4ynT9YFjRbyVt0PK4jWYiepSXLgenWrkVY7N1pp43FbE8FUincqxHWlFouyl76eU/zznQmvoNUDBQmTv9zur75PFYKpOXFevlxwBfB4pU/8jJ1fWI9cXcgY02jY94E058r9FGOGk0QsDPSZR27gQb+AjhLyalO2dBY/UpwkVCu4vesyGHuH1T1LnEAbAk8pOaHk524GOuxgpRVbPGfGOVmhucP9CFzrldhdtIUFFVHTQhqTWvbp+5j89LwLA==",
"tags": {}
}
},
{
"id": "9d536e51-fd7b-48df-996d-9a9bdac182b9",
"rev": "1-6adc8fdc2eb932941c81170a16fa0fa6",
"status": "pending",
"created": 1700058616439,
"identifiers": [],
"modified": 1700058616439,
"author": "17e392da-8e36-4e4e-abd7-7eef3e434395",
"responsible": "3ed06450-17e5-47b6-ba4f-7a0a084df56b",
"properties": {},
"type": "OTHER",
"systemMetaData": {
"secretForeignKeys": [],
"cryptedForeignKeys": {},
"delegations": {},
"encryptionKeys": {},
"securityMetadata": {
"secureDelegations": {},
"keysEquivalences": {}
},
"encryptedSelf": "UUMGXdg2roMmScp41sVWU8hcG+jnyFFHFXcKJigExgG5FwsmZLJ3QrPeEp0dUVy5",
"tags": {}
}
},
{
"id": "c9583cab-4549-4889-9630-feaa11132c81",
"rev": "1-bdae316b9e7bd1420047b625adf5c61f",
"status": "pending",
"created": 1700058623099,
"identifiers": [],
"modified": 1700058623099,
"author": "2163c1e4-8255-487c-9c6d-20c5944efe1d",
"responsible": "97011d2d-4e31-4b0c-b659-179354669f7e",
"properties": {},
"type": "NEW_USER_OWN_DATA_ACCESS",
"systemMetaData": {
"secretForeignKeys": [],
"cryptedForeignKeys": {},
"delegations": {},
"encryptionKeys": {},
"securityMetadata": {
"secureDelegations": {},
"keysEquivalences": {}
},
"encryptedSelf": "OfBuEjxdinfUxohD95pAMxUYxu3gEOwf1y6NeuFKA5A=",
"tags": {}
}
},
{
"id": "dcca5e35-bb2a-4045-aa5e-abb5e1c99c11",
"rev": "1-d6b4120a7949fad0e388fd5f18524f53",
"status": "pending",
"created": 1700058616308,
"identifiers": [],
"modified": 1700058616308,
"author": "17e392da-8e36-4e4e-abd7-7eef3e434395",
"responsible": "3ed06450-17e5-47b6-ba4f-7a0a084df56b",
"properties": {},
"type": "OTHER",
"systemMetaData": {
"secretForeignKeys": [],
"cryptedForeignKeys": {},
"delegations": {},
"encryptionKeys": {},
"securityMetadata": {
"secureDelegations": {},
"keysEquivalences": {}
},
"encryptedSelf": "3qnJT6LA7YSzZ9VB/TWxaIJa6NKNbDOkIiH4zhr4Nz65qp65td9Jh8Ixu8nCcsQG",
"tags": {}
}
},
{
"id": "f33d5d53-da65-418e-9c2f-f71c40d8b58d",
"rev": "1-758b5be031cf497ff489af4302a4e1f3",
"status": "pending",
"created": 1700058616356,
"identifiers": [],
"modified": 1700058616356,
"author": "17e392da-8e36-4e4e-abd7-7eef3e434395",
"responsible": "3ed06450-17e5-47b6-ba4f-7a0a084df56b",
"properties": {},
"type": "OTHER",
"systemMetaData": {
"secretForeignKeys": [],
"cryptedForeignKeys": {},
"delegations": {},
"encryptionKeys": {},
"securityMetadata": {
"secureDelegations": {},
"keysEquivalences": {}
},
"encryptedSelf": "YL5LMosYA4tEmG1OOzXMQ6wNvacXD2jUIqkCg2ECvfZQcc+6bRC7rRHZiSNbUh+C",
"tags": {}
}
}
]

Then, you can change the status of the Notification to signal that the operation is being taken care of.

const ongoingStatusUpdate = await apiAsDoctor.notificationApi.updateStatus(
patientNotification,
MaintenanceTask.StatusEnum.Ongoing,
)
ongoingStatusUpdate
{
"id": "c9583cab-4549-4889-9630-feaa11132c81",
"rev": "2-10c7d27a6ebba1f29320bf7de4c6c49e",
"status": "ongoing",
"created": 1700058623099,
"identifiers": [],
"modified": 1700058623099,
"author": "2163c1e4-8255-487c-9c6d-20c5944efe1d",
"responsible": "97011d2d-4e31-4b0c-b659-179354669f7e",
"properties": {},
"type": "NEW_USER_OWN_DATA_ACCESS",
"systemMetaData": {
"secretForeignKeys": [],
"cryptedForeignKeys": {},
"delegations": {},
"encryptionKeys": {},
"securityMetadata": {
"secureDelegations": {},
"keysEquivalences": {}
},
"encryptedSelf": "5/EsOprx/+cagnrnjIQLsXLUC2X+DwZw9vaSeGaRIXGN8GQ+GFLukMMEOX+0etpS",
"tags": {}
}
}

To allow the new User to access all their own data, you can use the giveAccessToAllDataOf method.

const sharedData = await apiAsDoctor.patientApi.giveAccessToAllDataOf(patient.id)
sharedData
{
"patient": {
"id": "97011d2d-4e31-4b0c-b659-179354669f7e",
"rev": "6-3b03ba9906d8d29e02231f8f8b7e6b8b",
"identifiers": [],
"created": 1700058617470,
"modified": 1700058617470,
"author": "17e392da-8e36-4e4e-abd7-7eef3e434395",
"responsible": "3ed06450-17e5-47b6-ba4f-7a0a084df56b",
"tags": {},
"codes": {},
"names": [
{
"family": "Specter",
"given": [
"Marc"
],
"prefix": [],
"suffix": [],
"text": "Specter Marc",
"use": "official"
}
],
"languages": [],
"addresses": [
{
"addressType": "home",
"description": "London",
"notes": [],
"telecoms": [
{
"system": "email",
"value": "1c87da27@icure.com"
}
]
}
],
"gender": "unknown",
"birthSex": "unknown",
"mergedIds": [],
"active": true,
"deactivationReason": "none",
"personalStatus": "complicated",
"notes": [],
"relatives": [],
"patientPractitioners": [],
"patientProfessions": [],
"properties": {},
"systemMetaData": {
"hcPartyKeys": {},
"privateKeyShamirPartitions": {},
"secretForeignKeys": [],
"cryptedForeignKeys": {},
"delegations": {},
"encryptionKeys": {},
"aesExchangeKeys": {},
"transferKeys": {},
"securityMetadata": {
"secureDelegations": {},
"keysEquivalences": {}
},
"encryptedSelf": "KvoUihjaV3va4gg8L/yAqj+4aQbiSUjhoiDDpF8LdDM=",
"publicKeysForOaepWithSha256": [
"30820122300d06092a864886f70d01010105000382010f003082010a0282010100bf758af3ae5a5a7e5a1fbe91154efaf0f97fab7b898cc5b38f5671b5bde8c29226e9bb7e4a5b40ef6c4a84f3fd422173518f39e8c4632af4dead70eb47a696b98566e09328926d2c31437ce0a0ac6e6d46190bc3018cb9d645c0ce099d56fc361960735dc50859a0b68259af92607384708c64291bcf8ea1f44ab4d0e3d8b9ca1eaa6e64b96b0a3e2c83801b11cfd2f36cd6b23c97b75a82761a2d632769b4f60515d60951f251e2e19ee49006aaf7d76b0d43e6c28e58bcae8f0bb48f3656a66b571340d384f9fb3c19b860ccca88f99087f306204fdf73d5d43c975b5d588730d2a7d6a5cc0376e9d1da27ef08e820e67fb3ecef716b1abd79a8040e4826230203010001"
],
"tags": {}
},
"firstName": "Marc",
"lastName": "Specter"
},
"statuses": {
"dataSamples": {
"success": true,
"error": null,
"modified": 1
},
"healthcareElements": {
"success": true,
"error": null,
"modified": 1
},
"patient": {
"success": true,
"error": null,
"modified": 0
}
}
}

If the method runs successfully, it will return a report of all the shared objects:

Finally, you can update the Notification status again to signal that the operation was completed successfully. Updating the status of the Notification is important, as otherwise you will risk of getting old Notifications when filtering for the new pending ones.

const completedStatusUpdate = await apiAsDoctor.notificationApi.updateStatus(
ongoingStatusUpdate,
MaintenanceTask.StatusEnum.Completed,
)
completedStatusUpdate
{
"id": "c9583cab-4549-4889-9630-feaa11132c81",
"rev": "3-5f001767dee5a6d57d8c1a5090ecccbf",
"status": "completed",
"created": 1700058623099,
"identifiers": [],
"modified": 1700058623099,
"author": "2163c1e4-8255-487c-9c6d-20c5944efe1d",
"responsible": "97011d2d-4e31-4b0c-b659-179354669f7e",
"properties": {},
"type": "NEW_USER_OWN_DATA_ACCESS",
"systemMetaData": {
"secretForeignKeys": [],
"cryptedForeignKeys": {},
"delegations": {},
"encryptionKeys": {},
"securityMetadata": {
"secureDelegations": {},
"keysEquivalences": {}
},
"encryptedSelf": "r3MmYQOwgDf5bxBWdxrtuiDxgMTWNF1m4jgolXk4/5tLc26dfolcofL6Njm18Kc9",
"tags": {}
}
}