List a patient's readings
- Sandbox: available
- Staging: available
- Production: unknown
Newest first by clinical time, in v1's envelope {items, total, page, pageSize, totalPages} with nest's nextCursor. from, to and code (comma-separated) filter as in v1; category (comma-separated) keeps the readings carrying any of those FHIR category codes. With from, a reading whose clinical time is more than an hour ahead of when it was stored is not found: the read skips the storage older than that, so it never waits on it. pageSize defaults to 10; above 200 it needs both from and to, and then reaches 10000. A page may start at most 10000 rows in; walk further with cursor.
Each item is a flat timeline entry (code, valueNumber, observedAt, category, valueSampledData, components, effectiveStart, effectiveEnd, deviceId, …) with v1's _id, id, patient, valueQuantity and createdAt beside it. fields=code,valueNumber returns exactly those keys instead.
Through ovok
- Who can call it: Manager apps, and users whose access policy grants the capabilities.
- Capabilities, all required:
signals_observations:read. 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 read. 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.
AuthorizationBearer <JWT>patientId*stringThe Signals patient id.
page?number1-based, as v1 paged. A page may start at most 10000 rows in; walk further with cursor.
1 <= valuepageSize?numberv1's page size; defaults to 10. Wins over limit when both are sent.
1 <= valuefrom?stringInclusive lower bound on clinical time; a reading timed over an hour after it was stored is missed.
to?stringInclusive upper bound on the reading's clinical time.
code?stringOne LOINC code, or several separated by commas.
length <= 1000category?stringOne FHIR category code, or several separated by commas; a reading carrying any of them.
length <= 1000limit?number1 <= valuecursor?stringlength <= 512fields?stringOnly these keys of each item, separated by commas (code,valueNumber,observedAt).
Every key when absent. A chart reading a day of readings needs a few of them.
length <= 200One page of readings.
application/json- response
items*array<>Exactly the fields keys instead, when fields is sent.
total*numberpage*number1-based.
pageSize*numbertotalPages*numbernextCursor*stringOpaque keyset cursor.
curl -X GET 'https://api.sandbox.ovok.com/v1/signals/api/observation/string'{ "items": [ { "observationId": "string", "valueNumber": 0, "valueString": "string", "valueUnit": "string", "note": "string", "observedAt": "2019-08-24T14:15:22Z", "recordedAt": "2019-08-24T14:15:22Z", "code": "string", "system": "string", "display": "string", "status": "string", "components": [ { "code": "string", "system": "string", "display": "string", "valueNumber": 0, "valueUnit": "string", "valueString": "string", "valueDateTime": "2019-08-24T14:15:22Z", "valueSampledData": {}, "dataAbsentReason": "string", "sequence": 0 } ], "category": [ "string" ], "valueSampledData": {}, "valueDateTime": "2019-08-24T14:15:22Z", "effectiveStart": "2019-08-24T14:15:22Z", "effectiveEnd": "2019-08-24T14:15:22Z", "deviceId": "string", "_id": "string", "id": "string", "patient": "string", "valueQuantity": {}, "createdAt": "2019-08-24T14:15:22Z" } ], "total": 0, "page": 0, "pageSize": 0, "totalPages": 0, "nextCursor": "string"}Submit a reading by patient refId POST
The patient is `patientRefId` in the body, else `subject.reference` (`Patient/<refId>`). 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), 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. - Each reading counts its patient as monitored for billing.
Submit a reading for a patient id POST
`subject` is ignored: the path names the patient. 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.