Projects

Update a project text setting

  • Sandbox: available
  • Staging: available
  • Production: unknown
PUT
/v1/project/settings/values/{key}

Sets one text setting of your current project, such as an app URL or a default access policy, or removes it with null.

Auth: bearer token of a project admin. The caller must also be a Practitioner, an admin or hold the System Owner access policy. Scope: the caller's project (from the token).

Behaviour

  • :key must be one of the text keys returned in values by GET /v1/project/settings. It may be URL-encoded.
  • value is trimmed and checked for the key's kind:
    • App URL keys (PATIENT_APP_URL, PRACTITIONER_APP_URL, PATIENT_SIGNUP_URL, CLINICIAN_DASHBOARD_URL): see App URLs below. The canonical form is stored.
    • Access policy keys (DEFAULT_PATIENT_ACCESS_POLICY, DEFAULT_PRACTITIONER_ACCESS_POLICY, CLINICIAN_INVITE_ACCESS_POLICY): AccessPolicy/<id> of an AccessPolicy in this project.
  • null removes the entry. Every other entry of Project.setting[] is kept as it is.
  • DEFAULT_PATIENT_ACCESS_POLICY is written to Project.defaultPatientAccessPolicy, not to Project.setting[]. Removing it turns patient registration and patient invitations off: see effectiveSettings in GET /v1/project/settings.
  • The write only succeeds if the project did not change since the server read it; otherwise it answers 409.
  • The response is the full settings envelope after the write, as GET /v1/project/settings returns it.

App URLs

PATIENT_APP_URL and PRACTITIONER_APP_URL are the app roots that links in emails are built on: https://app.example.com gives https://app.example.com/setpassword/<id>/<secret>.

  • Resolution order, per audience: the project's setting, then the parent project's, then the legacy setting (PATIENT_SIGNUP_URL or CLINICIAN_DASHBOARD_URL, on the project then its parent), then the request Origin for flows that allow it, then the deployment default.
  • For set-password and invite links, only the origin of PATIENT_SIGNUP_URL is used: https://app.example.com/register gives https://app.example.com, and vitalwise://register gives vitalwise://. Set PATIENT_APP_URL if the patient app lives under a path.
  • The continue-as-patient link (a business-address sign-up turned into a clinician invite) uses PATIENT_SIGNUP_URL in full whenever it is set, ahead of PATIENT_APP_URL.
  • An app URL is an https URL on any host (http only on localhost), or an app deep link scheme://[host][/path]: vitalwise:// gives vitalwise:///setpassword/<id>/<secret>. A web URL on this API's own host is refused, and a stored one is skipped when an email is sent.
  • Only the canonical form is stored and used, without a trailing slash. Userinfo, a query, a fragment, an opaque scheme:path, and web, script or local schemes used as a deep link (javascript:, data:, file: and others) are refused on write and skipped on send.
  • An invite with no app URL for its audience answers 409 app_url_not_configured and creates nothing.
  • An unknown :key returns 400 UNKNOWN_SETTING with the accepted keys. A web app URL on this API's own host returns 400 APP_URL_NOT_ALLOWED. A value that is not valid for the key returns 422 INVALID_SETTING_VALUE.

Example

curl -X PUT 'https://api.sandbox.ovok.com/v1/project/settings/values/PRACTITIONER_APP_URL' \  -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3NjcyNjg4MDAsImV4cCI6MTc2NzI3MjQwMH0.Ql3B_ztDvctlyZ2z0vp4sl3ExXRQGkzmxdmTAqxNzZk' \  -H 'Content-Type: application/json' \  -d '{ "value": "https://app.example.com" }'

Authorization

bearer
headerAuthorizationBearer <JWT>

Path Parameters

key*string

The setting key.

Request Body

application/json
  1. body
value*string
Lengthlength <= 2048

Response Body

The full settings envelope after the write.

application/json
  1. response
projectId*string
settings*
overrides*

The saved value of each switch, or null when the project has none saved.

effectiveSettings*

What ovok applies: the saved value, else the default, and off while a needed access policy is missing, or, for SIGNALS_SEND_AUTO and SIGNALS_ANNOTATE_OBSERVATIONS, while the project has no Signals tenant of its own. SIGNALS_ANNOTATE_OBSERVATIONS with no saved value follows SIGNALS_SEND_AUTO.

values*
Example Request
curl -X PUT 'https://api.sandbox.ovok.com/v1/project/settings/values/string' \  -H 'Content-Type: application/json' \  -d '{  "value": "string"}'
Example Response
{  "projectId": "string",  "settings": {    "CONTENT_ENABLED": true,    "MAILING_ENABLED": true,    "PATIENT_INVITATION_ENABLED": true,    "PATIENT_REGISTRATION_ENABLED": true,    "PATIENT_LOGIN_ENABLED": true,    "PRACTITIONER_INVITATION_ENABLED": true,    "PRACTITIONER_REGISTRATION_ENABLED": true,    "PRACTITIONER_LOGIN_ENABLED": true,    "RELATED_PERSON_INVITATION_ENABLED": true,    "RELATED_PERSON_LOGIN_ENABLED": true,    "CLINICIAN_INVITE_ON_BUSINESS_EMAIL": true,    "SIGNALS_SEND_AUTO": true,    "SIGNALS_ANNOTATE_OBSERVATIONS": true  },  "overrides": {    "CONTENT_ENABLED": true,    "MAILING_ENABLED": true,    "PATIENT_INVITATION_ENABLED": true,    "PATIENT_REGISTRATION_ENABLED": true,    "PATIENT_LOGIN_ENABLED": true,    "PRACTITIONER_INVITATION_ENABLED": true,    "PRACTITIONER_REGISTRATION_ENABLED": true,    "PRACTITIONER_LOGIN_ENABLED": true,    "RELATED_PERSON_INVITATION_ENABLED": true,    "RELATED_PERSON_LOGIN_ENABLED": true,    "CLINICIAN_INVITE_ON_BUSINESS_EMAIL": true,    "SIGNALS_SEND_AUTO": true,    "SIGNALS_ANNOTATE_OBSERVATIONS": true  },  "effectiveSettings": {    "CONTENT_ENABLED": true,    "MAILING_ENABLED": true,    "PATIENT_INVITATION_ENABLED": true,    "PATIENT_REGISTRATION_ENABLED": true,    "PATIENT_LOGIN_ENABLED": true,    "PRACTITIONER_INVITATION_ENABLED": true,    "PRACTITIONER_REGISTRATION_ENABLED": true,    "PRACTITIONER_LOGIN_ENABLED": true,    "RELATED_PERSON_INVITATION_ENABLED": true,    "RELATED_PERSON_LOGIN_ENABLED": true,    "CLINICIAN_INVITE_ON_BUSINESS_EMAIL": true,    "SIGNALS_SEND_AUTO": true,    "SIGNALS_ANNOTATE_OBSERVATIONS": true  },  "values": {    "PATIENT_APP_URL": "string",    "PRACTITIONER_APP_URL": "string",    "PATIENT_SIGNUP_URL": "string",    "CLINICIAN_DASHBOARD_URL": "string",    "DEFAULT_PATIENT_ACCESS_POLICY": "string",    "DEFAULT_PRACTITIONER_ACCESS_POLICY": "string",    "CLINICIAN_INVITE_ACCESS_POLICY": "string"  }}

Update a project setting PUT

Turns one on/off setting of your current project on or off, for example `PRACTITIONER_INVITATION_ENABLED`. **Auth:** bearer token of a project admin. The caller must also be a Practitioner, an admin or hold the System Owner access policy. **Scope:** the caller's project (from the token). ## Behaviour - `:key` must be one of the on/off keys returned in `settings` by `GET /v1/project/settings`. It may be URL-encoded. - Writes the entry into `Project.setting[]`, adding it when missing. Every other entry is kept as it is. - The write only succeeds if the project did not change since the server read it; otherwise it answers 409. - The response is the full settings envelope after the write, as `GET /v1/project/settings` returns it. - An unknown `:key` returns 400 `UNKNOWN_SETTING` with the accepted keys. - `SIGNALS_SEND_AUTO` and `SIGNALS_ANNOTATE_OBSERVATIONS` also switch their wiring before the setting is saved: Medplum's `Subscription` for send-auto, Signals' `ovok-annotations` subscriber for annotations. Nothing is saved if that fails (502 `SETTING_WIRING_FAILED`). Turning either on for a project without its own Signals tenant answers 409 `SETTING_NOT_AVAILABLE`; a concurrent change of the same switch answers 409 too. While `SIGNALS_ANNOTATE_OBSERVATIONS` has no saved value it follows `SIGNALS_SEND_AUTO`, so switching send-auto also switches annotations' wiring. - Read the project again and retry on 409. ## Example ```bash curl -X PUT 'https://api.sandbox.ovok.com/v1/project/settings/PRACTITIONER_INVITATION_ENABLED' \ -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3NjcyNjg4MDAsImV4cCI6MTc2NzI3MjQwMH0.Ql3B_ztDvctlyZ2z0vp4sl3ExXRQGkzmxdmTAqxNzZk' \ -H 'Content-Type: application/json' \ -d '{ "enabled": true }' ```

List administered projects GET

Lists every project you administer, with the tenant code you need to switch to it. Every other `/v1/projects` route answers about the single project of your token; use this one to build a project picker. **Auth:** bearer token. The caller must be a Practitioner, a project admin, or hold the System Owner access policy. You do not need to be an admin of your current project. **Scope:** your own user's Practitioner memberships across all projects, kept only where the membership is an admin one. ## Behaviour - One row per project, in membership order, even when you hold two memberships on one project. - `page` (from 0, default 0) and `count` (1 to 100, default 20) page over the projects. `total` counts the projects you administer, not your memberships. - `tenantCode` is `null` for a project that has none. `name` is `null` when the project can no longer be read; the row is kept. - `isMainProject` is `true` for a project with no parent, `false` for a child, and `null` when the hierarchy could not be read. - `parentProjectId` is set only when the parent is also a project you administer; otherwise it is `null`. - To switch, pass `tenantCode` to `POST /v1/auth/tenant/Practitioner/login/token`. ## Example ```bash curl -X GET 'https://api.sandbox.ovok.com/v1/projects?page=0&count=20' \ -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3NjcyNjg4MDAsImV4cCI6MTc2NzI3MjQwMH0.Ql3B_ztDvctlyZ2z0vp4sl3ExXRQGkzmxdmTAqxNzZk' ```