Share my record
- Sandbox: available
- Staging: available
- Production: unknown
The signed-in patient shares their record by email, with a practitioner or a related person (a carer):
"recipient": "practitioner"shares the whole record. A new address gets a practitioner account that already holds the share; a practitioner of the project gets an offer to accept."recipient": "related-person"shares only the Observationcodesnamed in the body, and optionally the patient's MedicationStatements and MedicationAdministrations whenincludeMedicationsistrue. The patient record's contact fields stay hidden. Either way the address gets an invite to accept; a new address makes its related-person account when it accepts.
Any other recipient is a 422 naming recipient.
Auth: bearer token of a Patient. Scope: the caller's project (from the token).
Behaviour
Practitioner
- Gates, checked before the address is looked up, in this order:
- practitioner sharing (
practitionerSharingEnabledonPUT /v1/patient-sharing/settings) is on; it is off by default; PRACTITIONER_INVITATION_ENABLEDis notfalse;- the project has a practitioner app URL;
- the project has a default practitioner access policy (
DEFAULT_PRACTITIONER_ACCESS_POLICY, elseCLINICIAN_INVITE_ACCESS_POLICY); - the patient has fewer than 20 pending offers.
- practitioner sharing (
- What happens, by address:
- No User has the address: a practitioner account of the project is created with the default practitioner access policy and the patient's share on its membership, in one atomic write. The address gets a
PRACTITIONER_INVITED_BY_PATIENTemail with a set-password link on the practitioner app. - The address belongs to exactly one active practitioner of the project: a pending offer is created and the address gets a
PRACTITIONER_PATIENT_SHAREDemail with an accept link,<practitioner app URL>/accept-invite?invite=<inviteId>&code=<code>, valid for 7 days. The share starts only when that practitioner, signed in, posts both values toPOST /v1/invites/record-share/accept. - The record is already shared with that practitioner: nothing is sent, unless their account has no password yet, in which case the set-password email goes out again.
- Anything else (a User that is not a practitioner of this project, or a practitioner with full project access): nothing is written or sent.
- No User has the address: a practitioner account of the project is created with the default practitioner access policy and the patient's share on its membership, in one atomic write. The address gets a
- Each email is sent as the last step of the write. Map the email templates named above in the project's email configuration first.
- Do not enable practitioner sharing in projects whose practitioners use the carehub dashboard: its route guard checks resource types only, so a practitioner who only holds shared patients could see project-wide data there.
- A 409 with
invitation_not_configuredmeans the project has no default practitioner access policy. It needs project configuration, not a retry. - A 429 means the patient already has 20 pending offers.
Related person
- Body:
email,firstName,lastName,codes(1 to 100 Observation codes, each letters, digits,.and-only), and optionalincludeMedications(defaults tofalse). - Gates, checked before the address is looked up, in this order:
- patient sharing (
enabledonPUT /v1/patient-sharing/settings) is on; it is off by default; RELATED_PERSON_INVITATION_ENABLEDis notfalse(403Invitation is not enabled for this project., as for a practitioner);- the project has a patient app URL;
- the patient has fewer than 20 pending related-person invites.
- patient sharing (
- What happens, by address:
- No User has the address: a pending share is created and the address gets a
RELATED_PERSON_INVITEDemail with an accept link,<patient app URL>/accept-invite?invite=<inviteId>&code=<code>, valid for 7 days. Nothing else is created yet: the account is made when the link is accepted (POST /v1/invites/record-share/acceptwith apassword). - The address belongs to exactly one active related person of the project: a pending share is created and the address gets a
RELATED_PERSON_RECORD_SHAREDemail with the same kind of link. The share starts when that related person, signed in, accepts it. - Anything else (a patient, a practitioner, or a User that is not a related person of this project): nothing is written or sent.
- No User has the address: a pending share is created and the address gets a
- The pending share is a
draftConsent holding only a hash of the one-time code. It grants nothing until it is accepted. RELATED_PERSON_INVITEDandRELATED_PERSON_RECORD_SHAREDcarryfirstName,lastName,sharerName(the patient's name),invitationUrl(the accept link) andbaseUrl. A project whose email configuration maps neither sendsPATIENT_INVITEandPATIENT_ASSIGNMENT_REQUESTinstead, withinviterNameset to the patient's name.- A 429 means the patient already has 20 pending related-person invites.
Both
- The answer is always
202 { "status": "sent" }, whichever case applied, so it does not reveal whether the address has an account. If the email cannot be sent, everything the invite wrote is undone, and the answer is still 202. - A 409 with
app_url_not_configuredmeans the project has no app URL for the recipient: the practitioner app, or the patient app for a related person.
Example
curl -X POST 'https://api.sandbox.ovok.com/v1/invites/record-share' \ -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3NjcyNjg4MDAsImV4cCI6MTc2NzI3MjQwMH0.Ql3B_ztDvctlyZ2z0vp4sl3ExXRQGkzmxdmTAqxNzZk' \ -H 'Content-Type: application/json' \ -d '{ "recipient": "practitioner", "email": "max.mustermann@example.com", "firstName": "Max", "lastName": "Mustermann" }'curl -X POST 'https://api.sandbox.ovok.com/v1/invites/record-share' \ -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3NjcyNjg4MDAsImV4cCI6MTc2NzI3MjQwMH0.Ql3B_ztDvctlyZ2z0vp4sl3ExXRQGkzmxdmTAqxNzZk' \ -H 'Content-Type: application/json' \ -d '{ "recipient": "related-person", "email": "max.mustermann@example.com", "firstName": "Max", "lastName": "Mustermann", "codes": ["8480-6", "8462-4"], "includeMedications": true }'bearerAuthorizationBearer <JWT>application/json- body
email*stringemaillength <= 254firstName*string1 <= length <= 200lastName*string1 <= length <= 200recipient*"practitioner""practitioner"Always { status: "sent" }, whichever case applied.
application/json- response
status*"sent""sent"curl -X POST 'https://api.sandbox.ovok.com/v1/invites/record-share' \ -H 'Content-Type: application/json' \ -d '{ "email": "user@example.com", "firstName": "string", "lastName": "string", "recipient": "practitioner"}'{ "status": "sent"}Check Signals health GET
Checks whether the backend can reach Signals. **Auth:** none. The route needs no bearer token. ## Behaviour - Calls Signals' own `GET /health`. `ok` is `true` only when Signals answers with `ok: true`, which it does while its database answers. - `ok` is `false` when Signals reports `ok: false`, cannot be reached, takes longer than 2 seconds, or no Signals URL is configured. - The status is `200` in every case. ## Example ```bash curl -X GET 'https://api.sandbox.ovok.com/healthcheck/signals' ``` ```json { "ok": true } ```
Accept a patient's record share POST
Accepts a patient's record share from its email: a practitioner's `PRACTITIONER_PATIENT_SHARED` offer, or a related person's `RELATED_PERSON_INVITED` or `RELATED_PERSON_RECORD_SHARED` invite. From then on the share is in place. The invite decides how it is accepted. **Auth:** bearer token of the Practitioner or RelatedPerson the invite names; none for an invite to a new address. **Scope:** the caller's project (from the token); for a new address, the invite's project. ## Behaviour - Send the `invite` and `code` values from the email's accept link as `inviteId` and `code`. `password` is only for a new related person; send `null` (or leave it out) otherwise. - Every invite is single use and expires 7 days after it was sent. A used one answers 409, an expired one 410, and an unknown one or a wrong `code` 404, whoever calls. - On success the invite's Consent becomes `active` and its grant is added to the recipient's membership, in one atomic write. The recipient's cached sessions are refreshed, so the new access applies on the next request. - The share lasts until the patient ends it (`DELETE /v1/patient-sharing/shares/{consentId}`). ### A practitioner's offer - Signed in as the practitioner the offer names; without a token the answer is 401. - Gates: practitioner sharing must be on for the project, and `PRACTITIONER_INVITATION_ENABLED` must not be `false`. - The share covers the patient's whole compartment. Observations, Communications, DocumentReferences, QuestionnaireResponses, ServiceRequests, Tasks and Appointments can be read, searched, created and updated; CommunicationRequests, Goals, CarePlans, Flags, EpisodeOfCare and DeviceUseStatements are read-only; the Patient can be read, searched and updated. Nothing can be deleted. - A 409 with `practitioner_without_access_policy` means the caller has full project access, which a share would narrow. - The answer is the practitioner share: `consentId`, `sharer`, `practitioner`, `startedAt`, `endsAt`. ### A related person's invite - Gate: patient sharing must be on for the invite's project. - The share covers the sharer's Observations with the invite's codes (read and search), and the sharer's Patient resource with contact details, identifiers and most other fields hidden. - A share also covers a multi-value reading's group Observation when it covers every value the group repeats (systolic and diastolic for blood pressure, the waveform for an ECG, any part of an SpO2 or urine-strip reading), so the whole reading can be read and nothing more. The share's `codes` stay as the sharer chose them. - **A new address** (`RELATED_PERSON_INVITED`): no token. Send a `password` that meets the password policy; without one the answer is a 422 with `password_required`. In one atomic write under the address's account lock, the account is made: a User scoped to the project, a RelatedPerson of the sharer, and a membership holding the built-in related-person base policy (read and search of its own RelatedPerson only, created the first time it is needed) and the share's grant. If the address has any account by now, nothing is written and the answer is a 409 with `address_has_account`. Sign in afterwards with the address and that password. - **A related person of the project** (`RELATED_PERSON_RECORD_SHARED`): signed in as the related person the invite names; without a token the answer is 401, as another related person or in another project 404. The grant is added to their membership, keeping its base policy. - The answer is the related-person share: `consentId`, `sharer`, `relatedPerson`, `codes`, `startedAt`, `endsAt`. - A 409 with `sharing_not_configured` means the project lost its patient-sharing template; turn patient sharing on again. ## Example ```bash curl -X POST 'https://api.sandbox.ovok.com/v1/invites/record-share/accept' \ -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3NjcyNjg4MDAsImV4cCI6MTc2NzI3MjQwMH0.Ql3B_ztDvctlyZ2z0vp4sl3ExXRQGkzmxdmTAqxNzZk' \ -H 'Content-Type: application/json' \ -d '{ "inviteId": "c3499c27-2973-0a7f-807e-fb8676a92dcb", "code": "<code from the accept link>", "password": null }' ``` A new related person, without a token: ```bash curl -X POST 'https://api.sandbox.ovok.com/v1/invites/record-share/accept' \ -H 'Content-Type: application/json' \ -d '{ "inviteId": "c3499c27-2973-0a7f-807e-fb8676a92dcb", "code": "<code from the accept link>", "password": "Ex4mple-Passw0rd" }' ```