# Carers

Canonical page: https://docs.ovok.com/authentication/carers

A **carer** is someone a patient trusts with some of their readings, such as a family member. In FHIR a carer is a `RelatedPerson` linked to the patient. A carer signs in to the same tenant as the patient, usually in the patient app, and reads only what patients have shared with them.

| Task | Route |
| --- | --- |
| Get an account | Accept a patient's invite: [`POST /v1/invites/record-share/accept`](/invitations/share-with-a-carer#step-2-accept) |
| Sign in | `POST /auth/tenant/RelatedPerson/login/start`, then `login/mfa` (only with MFA), then `login/token` |
| Reset a password | `POST /v2/auth/reset-password` with `"type": "RelatedPerson"` |
| Read shared data | The FHIR API with the carer's token, for example `GET /fhir/R4/Observation` |

## What the project needs [#what-the-project-needs]

| Setting | Used for | When unset |
| --- | --- | --- |
| [`RELATED_PERSON_INVITATION_ENABLED`](/settings-and-features/settings/related-person-invitation-enabled) | Inviting carers and accepting invites | On |
| [`RELATED_PERSON_LOGIN_ENABLED`](/settings-and-features/settings/related-person-login-enabled) | Carer sign-in and token refresh | On |
| Patient sharing, `enabled` on [`PUT /v1/patient-sharing/settings`](/settings-and-features/settings/patient-sharing-enabled) | Inviting carers and accepting invites | **Off** |
| [`PATIENT_APP_URL`](/settings-and-features/settings/patient-app-url) | The links in invite and reset emails | Falls back; invites answer `409` if nothing resolves |
| Email templates `RELATED_PERSON_INVITED`, `RELATED_PERSON_RECORD_SHARED` and `PATIENT_RESET_PASSWORD` | Invites and resets | See [the template catalogue](/email-templates/template-catalogue) |

The carer switches are separate from the patient ones: [`PATIENT_LOGIN_ENABLED`](/settings-and-features/settings/patient-login-enabled) does not affect carers, and turning carer sign-in off does not affect patients. Projects created with `POST /v1/slim/project/child` start with both carer switches set to `false`.

## Get an account [#get-an-account]

A carer account is created **only** when a carer accepts a patient's invite. There is no register route, because a FHIR `RelatedPerson` must point at a patient.

1. A patient shares readings with the carer's address: [Share readings with a carer](/invitations/share-with-a-carer).
2. The carer opens the link in the email, chooses a password in your app, and your app posts it with the link's values, without a token.
3. The carer signs in with the address and that password.

Later invites from other patients of the same project are added to the same account. A carer of two projects has two accounts, one per tenant.

## Sign in [#sign-in]

Carer sign-in is the same three-step PKCE flow as [patient sign-in](/authentication/patient-login), under `/auth/tenant/RelatedPerson`. None of the steps needs a bearer token.

| Step | Method | Path |
| --- | --- | --- |
| 1 | `POST` | `/auth/tenant/RelatedPerson/login/start` |
| 2, only with MFA | `POST` | `/auth/tenant/RelatedPerson/login/mfa` |
| 3 | `POST` | `/auth/tenant/RelatedPerson/login/token` |

Create a code verifier and challenge for every attempt, as in [step 0 of patient sign-in](/authentication/patient-login#step-0-create-a-code-verifier-and-challenge):

```bash
VERIFIER=$(openssl rand -base64 48 | tr '+/' '-_' | tr -d '=\n')
CHALLENGE=$(printf %s "$VERIFIER" | openssl dgst -sha256 -binary | openssl base64 -A | tr '+/' '-_' | tr -d '=')
```

### Step 1: Start [#step-1-start]

```bash
curl --request POST \
  --url 'https://api.sandbox.ovok.com/auth/tenant/RelatedPerson/login/start' \
  --header 'Content-Type: application/json' \
  --data "{
    \"email\": \"sam@example.com\",
    \"password\": \"${PASSWORD}\",
    \"tenantCode\": \"big-health-company\",
    \"codeChallenge\": \"${CHALLENGE}\"
  }"
```

| Body field | Description |
| --- | --- |
| `email` | The carer's email. Ovok lowercases it. |
| `password` | The password chosen when the invite was accepted. |
| `tenantCode` | The patient's tenant. |
| `codeChallenge` | The SHA-256 hash of your verifier, base64url-encoded. |

The answer is `201` with `nextStep`: `token` with a `sessionCode`, or `mfa` with a `loginId`.

| Status | Message | Cause |
| --- | --- | --- |
| `404` | `Tenant not found.` | The tenant code is wrong. Checked first. |
| `403` | `Login is not enabled.` | [`RELATED_PERSON_LOGIN_ENABLED`](/settings-and-features/settings/related-person-login-enabled) is `false`. |
| `401` | `Invalid email or password.` | A wrong email, an unknown account, a wrong password, or an account that is not a carer of this tenant. All give the same answer on purpose, with a `WWW-Authenticate` header. |
| `400` | `Username or password is incorrect.` | The sign-in could not be started after the password was accepted. Retry. |
| `422` | A list of `path` and `message` | A body field is missing or invalid. |
| `429` | | More than 10 calls a minute from the caller, or 5 failed sign-ins for this email within 15 minutes. Wait for the seconds in `Retry-After`. |

A patient or practitioner with the same email cannot sign in here, and a carer cannot sign in on the patient route.

### Step 2: Verify the one-time code (only with MFA) [#step-2-verify-the-one-time-code-only-with-mfa]

```bash
curl --request POST \
  --url 'https://api.sandbox.ovok.com/auth/tenant/RelatedPerson/login/mfa' \
  --header 'Content-Type: application/json' \
  --data '{"loginId":"8c1e5a52-6f0b-4d9e-9a41-0b2f7c3d9e10","mfaToken":"123456"}'
```

The answer is `201` with `{ "nextStep": "token", "sessionCode": "…" }`.

| Status | Message | Cause |
| --- | --- | --- |
| `400` | `Invalid MFA token.` | The code is wrong or expired, or the `loginId` is unknown. |
| `422` | A list of `path` and `message` | `loginId` is not a UUID, or `mfaToken` is shorter than 6 characters. |
| `429` | | More than 5 calls a minute. |

### Step 3: Exchange the session code for tokens [#step-3-exchange-the-session-code-for-tokens]

```bash
curl --request POST \
  --url 'https://api.sandbox.ovok.com/auth/tenant/RelatedPerson/login/token' \
  --header 'Content-Type: application/json' \
  --data "{\"sessionCode\":\"${SESSION}\",\"codeVerifier\":\"${VERIFIER}\"}"
```

The answer is `201` with `accessToken`, `refreshToken`, `expiresIn` (seconds), and the `project` and `profile` references. `profile.reference` is `RelatedPerson/<id>`.

| Status | Message | Cause |
| --- | --- | --- |
| `400` | `Code verification failed.` | The `sessionCode` or the `codeVerifier` is wrong. |
| `400` | `Invalid user profile.` | The session belongs to something other than a carer. |
| `400` | `Account not found.` | The carer account could not be found. |
| `422` | A list of `path` and `message` | A body field is missing. |
| `429` | | More than 30 calls a minute. |

If the carer had scheduled their account for deletion, signing in cancels it. Every error from these three steps takes at least a second.

## Reset a password [#reset-a-password]

A carer resets a password the way a patient does: the account-level reset route with `type` `RelatedPerson` and your patient app's `clientId`.

```bash
curl --request POST \
  --url 'https://api.sandbox.ovok.com/v2/auth/reset-password' \
  --header 'Content-Type: application/json' \
  --data '{
    "email": "sam@example.com",
    "type": "RelatedPerson",
    "clientId": "7e3a9d52-1c6b-4f08-a5d4-b2e8c0f19a37"
  }'
```

- The answer is always the same `201` `OperationOutcome`, so it does not reveal whether the address has a carer account.
- The carer receives `PATIENT_RESET_PASSWORD`, with a link on the [patient app URL](/settings-and-features/settings/patient-app-url): `<patient app URL>/setpassword/<id>/<secret>`.
- Your app finishes it with [`POST /v2/auth/reset-password/process`](/authentication/account/process-password-reset). The old password stops working.
- Without `clientId` the answer is `422`. See [Send a password reset email](/authentication/account/reset-password).

## What a carer can and cannot do [#what-a-carer-can-and-cannot-do]

A carer's token reads only what patients shared. Use the [FHIR API](/fhir-basics) with it.

| The carer can | How |
| --- | --- |
| Read and search a sharing patient's Observations with a shared code | `GET /fhir/R4/Observation?subject=Patient/<id>&code=8480-6` |
| Find the patients who share with them | `GET /fhir/R4/Patient`. Each patient's name is visible; `identifier`, `telecom`, `birthDate`, `address` and the other personal fields are hidden. |
| Read their own `RelatedPerson` | `GET /fhir/R4/RelatedPerson/<id>`, the id in `profile.reference` |
| Read who they are | `GET /auth/me` |
| Accept another patient's invite | [`POST /v1/invites/record-share/accept`](/invitations/share-with-a-carer#an-existing-carer-signed-in), signed in |

Anything else is refused:

| The carer cannot | What they get |
| --- | --- |
| Read another code, another patient, or another resource type | `403` or `404`, or an empty search |
| Create, update or delete anything, their own `RelatedPerson` included | `403` |
| Use the dashboard routes, `/v1/slim/...` | `403` |
| Use consents, `/v1/me/consent/...` | `403` |
| Use patient sharing, `/v1/patient-sharing/...`, including listing or leaving their shares | `403` |
| Use wearables, `/v1/wearables/...` | `403`: a wearable records its own wearer |
| Use the project member routes, `/v1/projects/me/members` | `403` |

A carer keeps access until the patient ends the share with `DELETE /v1/patient-sharing/shares/:consentId`. The change applies on the carer's next request.

## Sessions and the login switch [#sessions-and-the-login-switch]

- Turning [`RELATED_PERSON_LOGIN_ENABLED`](/settings-and-features/settings/related-person-login-enabled) off refuses new sign-ins and the next token refresh with `403`. A token already issued works until it expires, up to an hour.
- A session code from step 1 issued before the switch changed can still be exchanged at step 3.

## Gotchas [#gotchas]

- **No invite, no account.** Your app has no carer sign-up screen to build. Point carers to the invite email.
- **Carers use the patient app.** Invite and reset links are built on [`PATIENT_APP_URL`](/settings-and-features/settings/patient-app-url). Your patient app needs a carer mode: different sign-in route, and screens that read shared data rather than the user's own.
- **The patient record is redacted.** Do not expect `birthDate`, `gender` or contact details on a shared `Patient`.
- **A carer cannot list their shares yet.** Search `Patient` to find who shares with them, and Observations by the codes you expect.
- **Child projects start with carers off.** Projects created with `POST /v1/slim/project/child` set both carer switches to `false`.

## Related [#related]

- [Share readings with a carer](/invitations/share-with-a-carer)
- [Patient sign-in](/authentication/patient-login), the same flow for patients
- [RELATED_PERSON_LOGIN_ENABLED](/settings-and-features/settings/related-person-login-enabled)
- [RELATED_PERSON_INVITATION_ENABLED](/settings-and-features/settings/related-person-invitation-enabled)
