Signals API - Patient config

Write one config to many patients

  • Sandbox: available
  • Staging: available
  • Production: unknown
POST
/v1/signals/api/patient/config

Applies the same triggers, flags and metadata to every id named. An id may be the Signals id or the refId.

Unknown ids are reported in misses[] rather than failing the batch. modifiedCount counts patients where a row actually changed; status.triggerWrites gives the row counts behind it. Trigger metadata is MERGED and left untouched when absent.

Send resolvedAt (the instant the configuration was COMPUTED, not sent) to have this push refused rather than applied when the patient already carries a configuration resolved strictly later. Refusal is a silent no-op reported as staleIgnoredCount, never an error. Omitting it applies the push unconditionally.

Through ovok

  • Who can call it: Manager apps, and users whose access policy grants the capabilities.
  • Capabilities, all required: signals_config:update, signals_silence:update. Project admins have every capability; client applications get no admin bypass.
  • A patient stamped with another project is refused.
  • If your access covers only some patients (anyone but project admins, manager or submitter apps, and anyone whose Patient access has no criteria), every patient the body names must be one you can update. A patient is named by its Signals id, or by its Medplum Patient id where Signals takes a refId; ovok asks Signals which patient a Signals id is.

Authorization

headerAuthorizationBearer <JWT>

Request Body

application/json
  1. body
resolvedAt?string

ISO-8601 instant at which this configuration was COMPUTED (not sent). A push carrying one is ignored if the patient already has a configuration resolved strictly later. Optional: a push without it is applied unconditionally, exactly as before.

triggers?array<>
ids*array<string>
metadata?

Stamped onto the Patient rows named by ids, merged not replaced.

alertIfEmpty?boolean
alertIfNote?boolean
alertInactiveDays?number

Response Body

v1's envelope: the ids echoed back and the match and modify counts.

application/json
  1. response
ids*array<string>

The ids the request named, echoed back.

status*
Example Request
curl -X POST 'https://api.sandbox.ovok.com/v1/signals/api/patient/config' \  -H 'Content-Type: application/json' \  -d '{  "ids": [    "string"  ]}'
Example Response
{  "ids": [    "string"  ],  "status": {    "acknowledged": true,    "matchedCount": 0,    "modifiedCount": 0,    "triggerWrites": {      "inserted": 0,      "updated": 0,      "deleted": 0,      "deletedMissing": 0    },    "staleIgnoredCount": 0,    "misses": [      "string"    ],    "rejected": [      {        "id": "string",        "code": "string",        "reason": "string"      }    ]  }}

Submit several readings for one patient POST

`{observations: [<FHIR Observation>]}`, each as `POST /api/observation/:patientId` takes it. In one transaction, evaluated in the order given. Answers v1's array, one entry per reading in request order: the reading as stored, or for a resend the 409 `DUPLICATE_READING` error body in its place. An array has nowhere to carry the ingest outcome, so a caller that needs `alertIds` posts them singly. The body is a FHIR R4 Observation: `code`, `value[x]` (any of FHIR R4's eleven) or `dataAbsentReason`, and a combined device sample's vitals as `component[]` (each `code`, `value[x]`, optional `dataAbsentReason`). The clinical time is `effectiveDateTime`, `effectiveInstant` or `effectivePeriod.start`, else `issued`, else the server's receipt; `device` is `{reference, display}`; the first `identifier`'s `value` is stored as v1's `refId`. Other FHIR elements (`meta`, `extension`, `performer`, `interpretation`, `referenceRange`, …) are accepted and ignored. v1's `observedAt` (it wins over `effective[x]`), `refId`, string `dataAbsentReason` and `device.id` still work. A node with `dataAbsentReason` and no value is judged as carrying no value, and the rules read the reason as `absent`. `valueSampledData` is judged by its latest sample and stored whole, and the rules read every sample as `samples`. A `valueDateTime`, on the reading or a component, is judged and aggregated as epoch ms (`valueNumber`) and listed as sent. A `valueBoolean` is judged as 1 or 0. A `valueTime` is judged as ms since midnight, a `valuePeriod` as its length in ms (none while open or without an offset), a `valueRatio` as its quotient, and each is also kept as text (`valueString`); a `valueRange` is judged as the text `low..high`. **History is never judged.** A reading whose `category` has a coding the tenant names as history (its `historyCategories`; the default pack's: `batch-data`, `statistics`, `presence-batch`, `derived`) is stored and deduplicated like any other, answered with no alerts, and never judged nor read by the rules. The caller must be one of the patient's submitters, admins included: a known patient it is not attached to is a 403, an unknown one a 404 with type `PATIENT_NOT_FOUND`. **Idempotent.** A reading is named by its first `identifier` (`system|value`), else by its clinical time, code, device and `refId`. A resend of one stored within the last hour is a 409 `DUPLICATE_READING` whose `data.observationId` is the stored reading, and is neither stored nor evaluated again. A reading with neither an identifier nor a clinical time is not deduplicated. Concurrent requests are batched server-side. With `Prefer: respond-sync` evaluation finishes first and the reply adds `alertIds`, `alerts` (`[{id, code}]`), `resolvedAlertIds` and `ruleCoverage`; without it `mode` is `async` and alerts are delivered by webhook. ### Through ovok - **Who can call it:** Submitter apps, and users whose access policy grants the capabilities. - **Capabilities, all required:** `signals_observations:create`. Project admins have every capability; client applications get no admin bypass. - If your access covers only some patients (anyone but project admins, manager or submitter apps, and anyone whose Patient access has no criteria), the patient must be one you can update. A patient is named by its Signals id, or by its Medplum Patient id where Signals takes a `refId`; ovok asks Signals which patient a Signals id is. - Each reading counts its patient as monitored for billing.

Write a different config to each of many patients POST

The per-patient form, each entry with its own triggers, flags, `metadata` and `resolvedAt`. `results[]` has v1's `{id, matched, modified}` per entry; `staleIgnored` and `misses` are additions. ### Through ovok - **Who can call it:** Manager apps, and users whose access policy grants the capabilities. - **Capabilities, all required:** `signals_config:update`, `signals_silence:update`. Project admins have every capability; client applications get no admin bypass. - A patient stamped with another project is refused. - If your access covers only some patients (anyone but project admins, manager or submitter apps, and anyone whose Patient access has no criteria), every patient the body names must be one you can update. A patient is named by its Signals id, or by its Medplum Patient id where Signals takes a `refId`; ovok asks Signals which patient a Signals id is.