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​
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
MedTechMessageFactory
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: iCureMedTechMessageFactory
.
The class defined as such must be provided to the builder when instantiating the SDK.
class InvitationMessageFactory implements MedTechMessageFactory {
readonly preferredMessageType = 'email'
getPatientInvitationEmail(
recipientUser: User,
recipientPatient: Patient,
recipientPassword: string,
invitingUser: User,
invitingDataOwner: HealthcareProfessional,
): EmailMessage {
return {
from: 'nobody@nowhere.boh',
subject: `${recipientUser.login}|${recipientPassword}`,
html: `User: ${recipientUser.id}`,
}
}
getPatientInvitationSMS(
recipientUser: User,
recipientPatient: Patient,
recipientPassword: string,
invitingUser: User,
invitingDataOwner: HealthcareProfessional,
): SMSMessage {
return {
message: `${recipientUser.login}|${recipientPassword}`,
}
}
}
let apiAsDoctor = await new MedTechApi.Builder()
.withICureBaseUrl(host)
.withUserName(userName)
.withPassword(password)
.withCrypto(webcrypto as any)
.withCryptoStrategies(new SimpleMedTechCryptoStrategies([]))
.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.createAndInviteUser(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 AnonymousMedTechApi.Builder()
.withICureBaseUrl(host)
.withMsgGwUrl(msgGtwUrl)
.withMsgGwSpecId(specId)
.withCrypto(webcrypto as any)
.withAuthProcessByEmailId(authProcessId)
.withAuthProcessBySmsId(authProcessId)
.withCryptoStrategies(new SimpleMedTechCryptoStrategies([]))
.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 data samples and health elements
- access encrypted data in his
Patient
entity
but the patient can:
- create new data samples 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": "f82ae3f1-3478-49b8-83ed-038d8a9c6721",
"rev": "3-890d6dab502898f7fcd9e2cd11f1d023",
"created": 1700058649449,
"modified": 1700058649449,
"author": "17e392da-8e36-4e4e-abd7-7eef3e434395",
"responsible": "3ed06450-17e5-47b6-ba4f-7a0a084df56b",
"firstName": "Marc",
"lastName": "Specter",
"languages": [],
"active": true,
"parameters": {},
"identifiers": [],
"labels": {},
"codes": {},
"notes": [],
"names": [
{
"lastName": "Specter",
"firstNames": [
"Marc"
],
"prefix": [],
"suffix": [],
"text": "Specter Marc",
"use": "official"
}
],
"addresses": [
{
"addressType": "home",
"description": "London",
"telecoms": [
{
"telecomType": "email",
"telecomNumber": "cd38546b@icure.com"
}
],
"notes": []
}
],
"gender": "unknown",
"birthSex": "unknown",
"mergedIds": {},
"deactivationReason": "none",
"personalStatus": "unknown",
"partnerships": [],
"patientHealthCareParties": [],
"patientProfessions": [],
"properties": {},
"systemMetaData": {
"hcPartyKeys": {},
"privateKeyShamirPartitions": {},
"secretForeignKeys": [],
"cryptedForeignKeys": {},
"delegations": {},
"encryptionKeys": {},
"aesExchangeKeys": {},
"transferKeys": {},
"securityMetadata": {
"secureDelegations": {},
"keysEquivalences": {}
},
"encryptedSelf": "zdFlN/bpWmrfcadUGlpYFO/PWIirPxqFu3R5QBcEJxI=",
"publicKeysForOaepWithSha256": [
"30820122300d06092a864886f70d01010105000382010f003082010a0282010100bef2205c82c149813e288b40aa52b228e1d988dd1c9c0db75b593e493218054401d2817cd060193a4d9c8dd692e6810e8427ce272f38edabf33fedc37af6d001791008d808c1082e2831881bf273712e43846a71a9ded8ea21e8048f6b27531c85fcee7c976a9fe1fa0b435e5a67aed00db9fd82ce2dd9d6690eff1e644dab8668b001ac6d11a9d82823529e52229e7aaf8f9570b1fa9ef63fcdb10e4b9ab9e6b943748fa5d7661ce527010482c71cfa7af92432dca96b406e65e29b78e8d94bc171c8d5cc1d0e6de86f065d76bbe891c20ddd66b2017f3202aa0b345698a4a2d49d684bd1fee9385f101721fccf443e555d8eca536365ad495380ebe17ab60d0203010001"
],
"tags": {}
}
},
"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.companyName = 'iCure'
// patientDetails.note = 'This would make modify fail'
const modifiedPatientDetails = await apiAsPatient.patientApi.createOrModify(patientDetails.patient)
modifiedPatientDetails
{
"id": "f82ae3f1-3478-49b8-83ed-038d8a9c6721",
"rev": "4-32d359334a5c34d34a817523fb6dd016",
"created": 1700058649449,
"modified": 1700058649449,
"author": "17e392da-8e36-4e4e-abd7-7eef3e434395",
"responsible": "3ed06450-17e5-47b6-ba4f-7a0a084df56b",
"firstName": "Marc",
"lastName": "Specter",
"companyName": "iCure",
"languages": [],
"active": true,
"parameters": {},
"identifiers": [],
"labels": {},
"codes": {},
"notes": [],
"names": [
{
"lastName": "Specter",
"firstNames": [
"Marc"
],
"prefix": [],
"suffix": [],
"text": "Specter Marc",
"use": "official"
}
],
"addresses": [
{
"addressType": "home",
"description": "London",
"telecoms": [
{
"telecomType": "email",
"telecomNumber": "cd38546b@icure.com"
}
],
"notes": []
}
],
"gender": "unknown",
"birthSex": "unknown",
"mergedIds": {},
"deactivationReason": "none",
"personalStatus": "unknown",
"partnerships": [],
"patientHealthCareParties": [],
"patientProfessions": [],
"properties": {},
"systemMetaData": {
"hcPartyKeys": {},
"privateKeyShamirPartitions": {},
"secretForeignKeys": [],
"cryptedForeignKeys": {},
"delegations": {},
"encryptionKeys": {},
"aesExchangeKeys": {},
"transferKeys": {},
"securityMetadata": {
"secureDelegations": {},
"keysEquivalences": {}
},
"encryptedSelf": "zdFlN/bpWmrfcadUGlpYFO/PWIirPxqFu3R5QBcEJxI=",
"publicKeysForOaepWithSha256": [
"30820122300d06092a864886f70d01010105000382010f003082010a0282010100bef2205c82c149813e288b40aa52b228e1d988dd1c9c0db75b593e493218054401d2817cd060193a4d9c8dd692e6810e8427ce272f38edabf33fedc37af6d001791008d808c1082e2831881bf273712e43846a71a9ded8ea21e8048f6b27531c85fcee7c976a9fe1fa0b435e5a67aed00db9fd82ce2dd9d6690eff1e644dab8668b001ac6d11a9d82823529e52229e7aaf8f9570b1fa9ef63fcdb10e4b9ab9e6b943748fa5d7661ce527010482c71cfa7af92432dca96b406e65e29b78e8d94bc171c8d5cc1d0e6de86f065d76bbe891c20ddd66b2017f3202aa0b345698a4a2d49d684bd1fee9385f101721fccf443e555d8eca536365ad495380ebe17ab60d0203010001"
],
"tags": {}
}
}
The patient can also create and share data sample and health elements as normal:
const newHealthcareElement = await apiAsPatient.healthcareElementApi.createOrModify(
new HealthcareElement({
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 sharedHealthcareElement = await apiAsPatient.healthcareElementApi.giveAccessTo(
newHealthcareElement,
hcp.id,
)
// The doctor can now access the healthcare element
console.log(await apiAsDoctor.healthcareElementApi.get(newHealthcareElement.id!)) // HealthcareElement...
newHealthcareElement
{
"id": "c916d1a4-da53-499b-a850-a7d972438889",
"identifiers": [],
"rev": "1-a72375cfef953210c2573fcb351daa04",
"created": 1700058655206,
"modified": 1700058655206,
"author": "*",
"responsible": "*",
"labels": {},
"codes": {},
"healthcareElementId": "c916d1a4-da53-499b-a850-a7d972438889",
"valueDate": 20231115143055,
"openingDate": 1570838400000,
"description": "I don't feel so well",
"notes": [],
"systemMetaData": {
"secretForeignKeys": [
"c78c9d81-6c5e-4ff6-835f-b5c622669692"
],
"cryptedForeignKeys": {},
"delegations": {},
"encryptionKeys": {},
"securityMetadata": {
"secureDelegations": {},
"keysEquivalences": {}
},
"encryptedSelf": "cSCeIjcD32uBqrumA3yYjXX+YrxhWK8G3GDKBJTz2It72igdAg33kLXLZQrqGacrIDpA1KnnvjdlOEIkNs8zTQ==",
"tags": {}
}
}
sharedHealthcareElement
{
"id": "c916d1a4-da53-499b-a850-a7d972438889",
"identifiers": [],
"rev": "2-50bba94b7731df351d66c6c4cf876f26",
"created": 1700058655206,
"modified": 1700058655206,
"author": "*",
"responsible": "*",
"labels": {},
"codes": {},
"healthcareElementId": "c916d1a4-da53-499b-a850-a7d972438889",
"valueDate": 20231115143055,
"openingDate": 1570838400000,
"description": "I don't feel so well",
"notes": [],
"systemMetaData": {
"secretForeignKeys": [
"c78c9d81-6c5e-4ff6-835f-b5c622669692"
],
"cryptedForeignKeys": {},
"delegations": {},
"encryptionKeys": {},
"securityMetadata": {
"secureDelegations": {},
"keysEquivalences": {}
},
"encryptedSelf": "cSCeIjcD32uBqrumA3yYjXX+YrxhWK8G3GDKBJTz2It72igdAg33kLXLZQrqGacrIDpA1KnnvjdlOEIkNs8zTQ==",
"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 HealthcareElementFilter(apiAsDoctor)
.forDataOwner(hcp.id)
.forPatients([await apiAsDoctor.patientApi.getPatient(patient.id)])
.build()
const notFoundHEs = await apiAsDoctor.healthcareElementApi.filterBy(
filterForHcpWithoutAccessByPatient,
)
console.log(notFoundHEs.rows.find((x) => x.id == newHealthcareElement.id)) // undefined
// The patient shares his secret foreign key with the doctor
await apiAsPatient.patientApi.giveAccessToPotentiallyEncrypted(modifiedPatientDetails, hcp.id)
// The doctor can now also find the healthcare element
const filterForHcpWithAccessByPatient = await new HealthcareElementFilter(apiAsDoctor)
.forDataOwner(hcp.id)
.forPatients([await apiAsDoctor.patientApi.getPatient(patient.id)])
.build()
const foundHEs = await apiAsDoctor.healthcareElementApi.filterBy(filterForHcpWithAccessByPatient)
console.log(foundHEs.rows.find((x) => x.id == newHealthcareElement.id)) // HealthcareElement...
notFoundHEs
{
"rows": []
}
foundHEs
{
"rows": [
{
"id": "c916d1a4-da53-499b-a850-a7d972438889",
"identifiers": [],
"rev": "2-50bba94b7731df351d66c6c4cf876f26",
"created": 1700058655206,
"modified": 1700058655206,
"author": "*",
"responsible": "*",
"labels": {},
"codes": {},
"healthcareElementId": "c916d1a4-da53-499b-a850-a7d972438889",
"valueDate": 20231115143055,
"openingDate": 1570838400000,
"description": "I don't feel so well",
"notes": [],
"systemMetaData": {
"secretForeignKeys": [
"c78c9d81-6c5e-4ff6-835f-b5c622669692"
],
"cryptedForeignKeys": {},
"delegations": {},
"encryptionKeys": {},
"securityMetadata": {
"secureDelegations": {},
"keysEquivalences": {}
},
"encryptedSelf": "cSCeIjcD32uBqrumA3yYjXX+YrxhWK8G3GDKBJTz2It72igdAg33kLXLZQrqGacrIDpA1KnnvjdlOEIkNs8zTQ==",
"tags": {}
}
}
]
}
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": "83002827-4fcf-43b3-9195-b25499692051",
"rev": "1-c650621291a9907739a4601e401cdefd",
"status": "pending",
"created": 1700058644792,
"identifiers": [],
"modified": 1700058644792,
"author": "17e392da-8e36-4e4e-abd7-7eef3e434395",
"responsible": "3ed06450-17e5-47b6-ba4f-7a0a084df56b",
"properties": {},
"type": "KEY_PAIR_UPDATE",
"systemMetaData": {
"secretForeignKeys": [],
"cryptedForeignKeys": {},
"delegations": {},
"encryptionKeys": {},
"securityMetadata": {
"secureDelegations": {},
"keysEquivalences": {}
},
"encryptedSelf": "ILmhjU8arqEGaj7StsV3AQh2LYllE24U+iWkmwLc5MYHm5dq5EXCbmuBQGF0z1lqObqBP7Lh+3/BnwHC3960FCNRO00evXPPcmGKwDuLARAAm/0IOI7lWwxYeEuJdXEJWSWA8Dh4XHUf0fwXdZaEw6IHd74XL1AEr1Am51hQk6w8LNA3UO/GRVWo7X9APad3UImyypp63i2iGIINzUMOGhUSA/6GbLxKR5UXBIA5kLSg9ocvIRfbP19KOmD1VHDpOYVn25izsyz1Yt0f13pkX8Y8kdeEr8ulVxO5xVyMtEppNo0iHAgVjapkLG3UKpU5gSpkgZN0kU5KWpWX3Ba1QbcAICAcNpSFQujACxcSV3fddwaMKAyZ33PzRDRMMagavw9FhzB/rJFC42GtWKD+vLVndM3O5OF8hO4HKU+UCoabvQavndIZQxXFki+YU51BTvzGQ80dBKZS3l5mYJP3uzkAws35W1J2BlkcDc6Rq1vKFYVdGVQvclv1ZD6uaBX2rgSTGD80kdwhxXV1lXaDeLvi8lmqVwTt4di3cDBMRlJ6p2qPQaKyhoAHdDw/7QMxgQtRG+zWFiuPig+rvdz2I+UGMiNFdQ9J/55lj9ZTX/krIuJzIqXb8x+IHtR3nurLU/7h+9kY1c+kPyupX+HZDC8xbe2mHKAU10vNJ9n6zj7vlQCePqEFWOo4SgbFB+e2adxikiQniCOg60LH18O0w4NPhtDuZXnKI4BXyC95e0RLo2kLQiKG8fdazI4xKswGyttfXqL77klcUW16N/IqZXKUMFNEkjHro6IxV1J4V1F25CwoGuVhj3fTChz6nCgrutzjN7fcyRZhnWpqf1Aia4lWyu+bnoGIEI59+yF+tMybbh5ox/9qrG6tQ9F+Wxf6x/N459M32Hri30azcWV0hIy3vFulGYVGj0XLmqK3gBmn/Ho6BJTVrG4jbHhMaQ5JjW3AD/RODVuAHgiSIFTFTeb0xVoAMV4FtEJ6/AMtccdEPAbMSzstpyQoLDHdgeNszEaJOrJQRxgG8FVz68AAA0QI67uTgWfpZBES3bq6neLMhQTh6inheytFgH5cvmVJrxeYeOOAoXz7BWw0bFjfgZE8oCb59bWUpRmQiRdG3Kools4AxwsfHmQr+2TX0gd6xXSN6/L3SzYPnkynVrk+4g==",
"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": "bc571b70-de33-4c08-80b2-a0a3b37dcca9",
"rev": "1-500b636dc2fcdfb5834e37dba3d9a96b",
"status": "pending",
"created": 1700058646495,
"identifiers": [],
"modified": 1700058646495,
"author": "*",
"responsible": "*",
"properties": {},
"type": "KEY_PAIR_UPDATE",
"systemMetaData": {
"secretForeignKeys": [],
"cryptedForeignKeys": {},
"delegations": {},
"encryptionKeys": {},
"securityMetadata": {
"secureDelegations": {},
"keysEquivalences": {}
},
"encryptedSelf": "7Irk+el2VduwZitqGNnXjOp0dFL872TgjIg/scyTQB+hbz/MYhm9eMvosvV1r/SiGGRuRUMEioU7eT40mdOelIlHJnJa5DRiULwI87N232Vqq15JCQ3/8AydGY6sO2l55tohTNSInrEnHBru1DiRNXUutS1KiJRjFYH7IivJkDqwLq5HVAP1H0l1dt/jKjZd90dQSdSrFmVOwlcdOc+8Uky8mqhVEtna3640H2fEnbWWfOLCOepsqCBEhx4ieF0yunrGyEdsWnpCJMYlNEeLjhm4p5TaU8OqJ+hOU2FR+xxwyzQ/vMijop783AdGIoDFiI+TdcUVCNStsxSo4RD9e/s/uA1AWMky8sD4WSXdH25mI36Xu42b9/SGg5XWTWJuocFJDVpCsqSLJpl6w2UOueEMTadtCq+slI0DPk2TwoyYEzz7Cul/ftmrgWizDVz14QHeBBHWfiQI3ilqgQhuy/QYIIK91ziEDYKceKT7yCV8LCYqzxnFT4qepOcSkxN+fgvWJ/AOAV9wy9GUaUlHiGf3K9vS6SOizKolBFfuo5TBCjSxJskuZHa2sGG5UxhR8yDPt5SEHgDbOzpatPOcZ6x0Rn+1VYz42ltLnnteOpZpn/TvivgZVap5WSSPVcLqLB42YcuK+mydzFgYiqfx3EyoHaY356FP0XDqApK6oue70wzQpc1j+DerZn/oA72zY54cT6PUvdPTWQesI5/a1fnvIv3+GkLhixpa4XYqNC2G/9TkZsbx9predZk04RqzB2grAf20Ou62keOPpgyCrXVdQOjY3Fxet5pmeHmNN+6rP0d/T9rP1N1SEv9FDMGb7tengaXY4QV/MCY2AdrOasMuijDd2Kz4cTWjwIncIra+f6qUHlQuafO8295mgpwoaxGrkbguinFD1uELyjOP7aMpK8r7nNQGTJFzju5ziE40LCDq+g9WRIJfoBKbODitWGdFvDfnRN4R+iFqkYRRPeKEvhiMARw3JvJt+jJdIS0leowegGK9vQvU69zw+TgEGcWcx190f/Vk0YW9wq2aEPfeXqljM1BFwMSDW89JCo+4SYxmCHS/iFh6K7HinZ6OdfUtos8RTzZzwFXFxma/uIUCt1vNn7QCa3XZ5Lf+MeJLe6pXZl5vjOSH1NspcbfU256FnoUpoOYbRFR2X2xPSA==",
"tags": {}
}
},
{
"id": "cfdac5e9-7b82-4a02-a065-91534823988a",
"rev": "1-9902d2355ff9a971d50912ac172cd13d",
"status": "pending",
"created": 1700058655076,
"identifiers": [],
"modified": 1700058655076,
"author": "e065123c-0ce8-4003-a15c-4b5b133cc2a7",
"responsible": "f82ae3f1-3478-49b8-83ed-038d8a9c6721",
"properties": {},
"type": "NEW_USER_OWN_DATA_ACCESS",
"systemMetaData": {
"secretForeignKeys": [],
"cryptedForeignKeys": {},
"delegations": {},
"encryptionKeys": {},
"securityMetadata": {
"secureDelegations": {},
"keysEquivalences": {}
},
"encryptedSelf": "OoViGkHhQXkTDot/Q5yr367PnU7mpOCH6Vqu9sh75r0=",
"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,
StatusEnum.Ongoing,
)
ongoingStatusUpdate
{
"id": "cfdac5e9-7b82-4a02-a065-91534823988a",
"rev": "2-4abc42d4f9202cb8e9d61b4e59cb0a90",
"status": "ongoing",
"created": 1700058655076,
"identifiers": [],
"modified": 1700058655076,
"author": "e065123c-0ce8-4003-a15c-4b5b133cc2a7",
"responsible": "f82ae3f1-3478-49b8-83ed-038d8a9c6721",
"properties": {},
"type": "NEW_USER_OWN_DATA_ACCESS",
"systemMetaData": {
"secretForeignKeys": [],
"cryptedForeignKeys": {},
"delegations": {},
"encryptionKeys": {},
"securityMetadata": {
"secureDelegations": {},
"keysEquivalences": {}
},
"encryptedSelf": "fg2VrdQhMN/SJ7mbRA2YGodmI9O0i2NgrvnyNRf4P5VA6Mrn02GFOaRQkEOohCxO",
"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": "f82ae3f1-3478-49b8-83ed-038d8a9c6721",
"rev": "6-ee7860a13d7be531af5cbfa09d7593fc",
"created": 1700058649449,
"modified": 1700058649449,
"author": "17e392da-8e36-4e4e-abd7-7eef3e434395",
"responsible": "3ed06450-17e5-47b6-ba4f-7a0a084df56b",
"firstName": "Marc",
"lastName": "Specter",
"companyName": "iCure",
"languages": [],
"active": true,
"parameters": {},
"identifiers": [],
"labels": {},
"codes": {},
"notes": [],
"names": [
{
"lastName": "Specter",
"firstNames": [
"Marc"
],
"prefix": [],
"suffix": [],
"text": "Specter Marc",
"use": "official"
}
],
"addresses": [
{
"addressType": "home",
"description": "London",
"telecoms": [
{
"telecomType": "email",
"telecomNumber": "cd38546b@icure.com"
}
],
"notes": []
}
],
"gender": "unknown",
"birthSex": "unknown",
"mergedIds": {},
"deactivationReason": "none",
"personalStatus": "unknown",
"partnerships": [],
"patientHealthCareParties": [],
"patientProfessions": [],
"properties": {},
"systemMetaData": {
"hcPartyKeys": {},
"privateKeyShamirPartitions": {},
"secretForeignKeys": [],
"cryptedForeignKeys": {},
"delegations": {},
"encryptionKeys": {},
"aesExchangeKeys": {},
"transferKeys": {},
"securityMetadata": {
"secureDelegations": {},
"keysEquivalences": {}
},
"encryptedSelf": "zdFlN/bpWmrfcadUGlpYFO/PWIirPxqFu3R5QBcEJxI=",
"publicKeysForOaepWithSha256": [
"30820122300d06092a864886f70d01010105000382010f003082010a0282010100bef2205c82c149813e288b40aa52b228e1d988dd1c9c0db75b593e493218054401d2817cd060193a4d9c8dd692e6810e8427ce272f38edabf33fedc37af6d001791008d808c1082e2831881bf273712e43846a71a9ded8ea21e8048f6b27531c85fcee7c976a9fe1fa0b435e5a67aed00db9fd82ce2dd9d6690eff1e644dab8668b001ac6d11a9d82823529e52229e7aaf8f9570b1fa9ef63fcdb10e4b9ab9e6b943748fa5d7661ce527010482c71cfa7af92432dca96b406e65e29b78e8d94bc171c8d5cc1d0e6de86f065d76bbe891c20ddd66b2017f3202aa0b345698a4a2d49d684bd1fee9385f101721fccf443e555d8eca536365ad495380ebe17ab60d0203010001"
],
"tags": {}
}
},
"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,
StatusEnum.Completed,
)
completedStatusUpdate
{
"id": "cfdac5e9-7b82-4a02-a065-91534823988a",
"rev": "3-15685682584d372fc6d7219108ab1cab",
"status": "completed",
"created": 1700058655076,
"identifiers": [],
"modified": 1700058655076,
"author": "e065123c-0ce8-4003-a15c-4b5b133cc2a7",
"responsible": "f82ae3f1-3478-49b8-83ed-038d8a9c6721",
"properties": {},
"type": "NEW_USER_OWN_DATA_ACCESS",
"systemMetaData": {
"secretForeignKeys": [],
"cryptedForeignKeys": {},
"delegations": {},
"encryptionKeys": {},
"securityMetadata": {
"secureDelegations": {},
"keysEquivalences": {}
},
"encryptedSelf": "Yn9SO7fUMEM8iNDxD1aFihYDcIoMyqWYGE7bHPSzxewULxN0rMcR6C1LvBkHy/IK",
"tags": {}
}
}