Get trends graphs
| Method | Path |
|---|---|
GET | /v1/slim/trends/graphs |
Authentication · Access policies
Returns a resident's heart rate and respiratory rate over a date range, grouped by graph type and device, with threshold and baseline values and summary statistics. Use it to draw the trend charts. For live readings from the last day, use GET /v1/slim/trends/realtime.
Auth: bearer token. The caller must be a practitioner, an admin or a System Owner, and needs Organization:read, Device:read, Patient:read and DocumentReference:read.
Scope: the caller's project (from the token). Observations are read with your token, so Medplum's access policy limits what you see.
Behaviour
graphTypeis a comma-separated list ofheartRateandrespiratoryRate. Repeated entries are ignored.startDatemust not be afterendDate.- Days are cut at the project's time zone (default
Europe/Berlin). The chart for a day starts at 09:00 local time, so a night stays in one day. - A range of two or more days is an over-day chart: one point per day, at 09:00 local time, starting the day after
startDate. Each point is the day's mean from the pre-computed statistics, with batch readings filling nights that have none. - A range of one day or less is an in-day chart: 30-second points across the 24 hours that start at 09:00 local time on
startDate.endDateonly decides which kind of chart you get. In-day points come from batch readings only. - Before a night's recording is picked, both charts drop the readings and sleep statistics that end outside the resident's use periods of their device, as
GET /v1/slim/sleep/statisticsdoes. So a previous resident's night that ended before the device was handed over, but reached Ovok after it and was stored under this resident, is not charted. A resident with no device use history keeps all of them. - Once a resident has any use period, this also drops the readings of a device with no period of theirs, and those from before a device's earliest period. A device assigned before use periods were recorded gets its first one on its next edit in Ovok, starting at the Device's last write before that edit, so its readings from before then are not charted. Each device whose readings in a request all drop is counted in the
sleep.device_use_device_droppedmetric and logged at most once a minute. - The use periods are read on every uncached call. When Medplum answers that read with an error, the route answers
502rather than chart a night it cannot check; when the request itself fails, the route fails with Medplum's status, or500. - When a night (the calendar day its recordings end on, in the project's time zone) has several sleep recordings left, both charts show only the readings of the one whose sleep statistics were written last (latest
meta.lastUpdated): its heart and respiratory rate statistics, matched by the recording's exact period, and its batch readings, matched by their start (below). That is the ruleGET /v1/slim/sleep/statisticsuses. A night with no sleep statistics goes by its heart and respiratory rate statistics' write times. A night with one recording left, a night with batch readings only, and a night none of whose readings belongs to the chosen recording are charted with all the readings left. Readings with no end time cannot be put on a night, so neither rule applies to them: they are always charted. - Sleep statistics are read over the chart's range, which ends at 09:00 local time on its last day, so a recording that starts later that day is not weighed.
- A batch block is matched by its start, because Sleepiz ends one up to about ten minutes before its recording: it belongs to the recording that starts at the same instant and ends at or after it, at most 15 minutes later, the earliest such end when two recordings share the start. A batch block that starts at any other time belongs to no recording, so on a night with several recordings it is dropped: a chosen recording whose only heart rate reading is a batch block that starts after the recording shows no heart rate for that night, while its respiratory rate still shows.
- Every bucket is returned. A bucket without a reading has
data: null.pointTimestampandpointTimestampRangeshow which readings filled it. maxThreshold,minThreshold,maxBaseLineandminBaseLineare the values that applied at that time. They are left out when none apply, and when the threshold lookup fails the chart still returns without them.- Devices are keyed
<manufacturer>-<device id>. A resident who changed device has one entry per device. - A graph type with no data in the range comes back as
{}. When nothing at all can be charted, bothheartRateandrespiratoryRateare{}. - Results are cached for up to 60 seconds. A write to the resident's data invalidates the cache sooner.
Example
curl -X GET 'https://api.sandbox.ovok.com/v1/slim/trends/graphs?patientId=3f1c2b7e-8d4a-4c1e-9b2f-6a7d5e4c3b21&startDate=2026-09-01T00:00:00Z&endDate=2026-09-30T00:00:00Z&graphType=heartRate,respiratoryRate' \
-H "Authorization: Bearer ${OVOK_TOKEN}"
The response is grouped by graph type and then by device key. Each device entry contains dataPoints and summary stats. Empty buckets have data: null; unavailable thresholds or baselines are omitted. Devices use the <manufacturer>-<device id> key.
Successful response
200 — Successfully retrieved trends data grouped by device and graph type
Errors
| Status | Meaning |
|---|---|
400 | Your session has no project, or your project cannot be read. |
401 | Bearer token is missing, invalid or expired. |
403 | You are not a practitioner, admin or System Owner, or you lack Organization:read, Device:read, Patient:read or DocumentReference:read. |
422 | A query parameter fails validation, or startDate is after endDate. |
502 | Medplum answered the read of the resident's device use periods with an error. |