Skip to main content

Update patient threshold (v2)

MethodPath
PUT/v2/slim/threshold/patient/:patientId

Authentication · Access policies

Saves one resident's own thresholds in the v2 shape. mode is required on every settings field and decides which other values the field takes.

This route supersedes PUT /v1/slim/threshold/patient/{patientId}. Only it can set a baseline limit for heart or respiratory rate, the relative out-of-bed band and the optional vitals.

Auth: Bearer token. The caller must be a practitioner, an admin, a super admin, or hold the "System Owner" access policy. The access policy must grant Goal:update (admins and super admins skip this check). Scope: The resident must be in the caller's project (from the token). A resident in another project (a sub-project included) or an unknown id answers 404. A resident who carries no project answers 403.

Request​

Path parameters​

NameTypeRequiredDescription
patientIdstring (uuid)YesThe resident's Patient id.

Body​

A field in organization mode takes no other keys: strip the resolved values a GET returns for it before sending it back. Inside each metric object, an unlisted key answers 422, except the response-only keys marked "ignored". Keys not listed below are ignored at the top level and inside settings; a ranges key is ignored, because ranges belong to the home.

NameTypeRequiredDescription
settingsobjectYesThe resident's limits.
settings.heartRateobjectYesHeart-rate limit.
settings.heartRate.mode"organization" | "patient" | "baseline"Yesorganization follows the home: send no other key. patient is the resident's own absolute band. baseline is a band relative to the resident's own baseline.
settings.heartRate.enabledbooleanYes for patient and baselineWhether the limit alerts.
settings.heartRate.minnumberYes for patientLower bound in beats per minute, 20 to 300 and inside the home's heart-rate range (default 40 to 120). Must be below max when enabled is true.
settings.heartRate.maxnumberYes for patientUpper bound in beats per minute, 20 to 300 and inside the home's heart-rate range.
settings.heartRate.deviationPercentintegerYes for baselineThe symmetric deviation from the resident's baseline, 5 to 60 percent.
settings.heartRate.baselineAveragenumber | nullNobaseline only. Response-only; ignored.
settings.heartRate.effectiveBandobject | nullNobaseline only. Response-only ({ min, max }); ignored.
settings.respiratoryRateobjectYesRespiratory-rate limit. Same fields as settings.heartRate.
settings.respiratoryRate.min, settings.respiratoryRate.maxnumberYes for patientBounds in breaths per minute, 4 to 150 and inside the home's respiratory-rate range (default 4 to 60).
settings.outOfBedobjectYesOut-of-bed limit.
settings.outOfBed.mode"organization" | "patient"Yesorganization follows the home: send no other key. patient is the resident's own limit.
settings.outOfBed.enabledbooleanYes for patientWhether out-of-bed alerting is on.
settings.outOfBed.maxDurationSecondsintegerYes for patientThe longest exit allowed, in seconds. 0 to 86400, a multiple of 60. Must be above 0 when enabled is true.
settings.outOfBed.deviationPercentinteger | nullNopatient only. Alert sooner when an exit runs this many percent past the resident's usual one. 5 to 300. null switches it off. Left out, the stored value stays (the home's, when the resident had none).
settings.outOfBed.baselineAveragenumber | nullNopatient only. Response-only; ignored.
settings.outOfBed.effectiveMaxDurationSecondsnumber | nullNopatient only. Response-only; ignored.
settings.bedTimePeriodobjectYesThe window in which out-of-bed is checked.
settings.bedTimePeriod.mode"organization" | "patient"Yesorganization follows the home's window: send no other key. patient is the resident's own window.
settings.bedTimePeriod.startstring (HH:MM:SS)Yes for patientWindow start, wall-clock time in the home's time zone.
settings.bedTimePeriod.endstring (HH:MM:SS)Yes for patientWindow end. Equal to start means 24 hours; before start crosses midnight.
settings.bedTimePeriod.timezonestring | nullNopatient only. Response-only; ignored. The server resolves the zone.
settings.oxygenSaturationobject | nullNoSpO₂ limit. Left out or null keeps what is stored.
settings.oxygenSaturation.mode"organization" | "patient"Yes, if the object is sentorganization follows the home: send no other key. patient is the resident's own limit.
settings.oxygenSaturation.enabledbooleanYes for patientWhether the limit alerts.
settings.oxygenSaturation.min, settings.oxygenSaturation.maxnumber | nullYes for patientBounds in percent, 50 to 100 and inside the home's range (default 70 to 100). null for no bound on that side. An enabled limit needs at least one bound, and min must be below max when both are set.
settings.pulseRateobject | nullNoPulse-rate limit. Same fields as settings.oxygenSaturation; bounds 20 to 300 and inside the home's range (default 40 to 120).
settings.perfusionIndexobject | nullNoPerfusion-index limit. Same fields as settings.oxygenSaturation; bounds 0 to 20 and inside the home's range (default 0 to 20).
settings.bodyTemperatureobject | nullNoBody-temperature limit. Same fields as settings.oxygenSaturation; bounds 30 to 45 and inside the home's range (default 34 to 42), in °C.

Behaviour​

  • mode: "organization" follows the home; send no other values. mode: "patient" is an individual limit: heart rate and respiratory rate take enabled, min and max; out of bed takes enabled and maxDurationSeconds, plus an optional deviationPercent that alerts sooner when an exit runs that far past the resident's usual one (it only ever brings the limit forward: maxDurationSeconds stays the limit when there is no baseline yet). mode: "baseline" (heart rate and respiratory rate only) alerts relative to the resident's own baseline, with enabled and deviationPercent.
  • settings.bedTimePeriod: mode: "organization" (follow the home's window) or mode: "patient" with start and end.
  • Arming a baseline limit needs a baseline average on record for the resident. Without one the save answers 424. A baseline limit sent back unchanged is not re-checked, so an unrelated save still works after the average has aged out.
  • A resident's own enabled limits, the four vitals included, must sit inside the home's ranges (see Get current org threshold (v2)). One outside answers 422 naming the field, and nothing is saved. A limit sent back unchanged is not re-checked, so a home that narrows its range does not block a resident's other edits. Set ranges with Update org threshold (v2).
  • settings.oxygenSaturation, pulseRate, perfusionIndex, bodyTemperature (each optional): { "mode": "organization" } follows the home; { "mode": "patient", ... } is the resident's own limit, either bound null for a one-sided one. Leaving one out, or sending null, keeps what is stored. Following a home that does not watch the vital removes the resident's rule in Signals.
  • enabled: false with the values still sent stores the limit switched off and keeps its values.
  • Heart-rate, respiratory-rate and vital limits are evaluated by Signals. A save that changes one waits for Signals to confirm it. If Signals does not confirm, the route answers 502 with signalsSync: { "status": "pending", "reason": ... }. The new limits are already stored by then; send the save again to retry, even if nothing differs from what is stored. An out-of-bed or bed-time save does not wait for Signals and still succeeds while Signals is unreachable.
  • While a save of the home's limits (Update org threshold (v2)) is running, this save answers 409 and nothing is changed: try again once it is done.
  • If the resident's stored out-of-bed band cannot be read while settings.outOfBed is patient with no deviationPercent, the save answers 502 and nothing is saved: a failed read is not "no band".
  • timezone never travels up on a PUT body.
  • Change-history entries are written for each changed metric.
  • The response is the resident's resolved thresholds after the save, in the shape of Get patient effective threshold (v2). signalsSync is { "status": "ok" } when Signals confirmed a changed medical limit, { "status": "pending", "reason": "metric-disarmed:..." } when Signals confirmed it but a metric shown as armed was sent as removed, and null when the save claimed nothing Signals evaluates.

Example​

curl -X PUT 'https://api.sandbox.ovok.com/v2/slim/threshold/patient/3f1c2b7e-8d4a-4c1e-9b2f-6a7d5e4c3b21' \
-H "Authorization: Bearer ${OVOK_TOKEN}" \
-H 'Content-Type: application/json' \
-d '{
"settings": {
"heartRate": { "mode": "patient", "enabled": true, "min": 45, "max": 110 },
"respiratoryRate": { "mode": "organization" },
"outOfBed": { "mode": "patient", "enabled": true, "maxDurationSeconds": 900, "deviationPercent": 50 },
"bedTimePeriod": { "mode": "organization" }
}
}'

Successful response​

200 — Patient v2 threshold updated successfully.

{
"id": null,
"patientId": "3f1c2b7e-8d4a-4c1e-9b2f-6a7d5e4c3b21",
"projectId": "3f1c2b7e-8d4a-4c1e-9b2f-6a7d5e4c3b11",
"updatedAt": "2026-10-09T08:30:00.000Z",
"settings": {
"heartRate": { "mode": "patient", "enabled": true, "min": 45, "max": 110 },
"respiratoryRate": { "mode": "organization", "enabled": true, "min": 8, "max": 25 },
"outOfBed": {
"mode": "patient",
"enabled": true,
"maxDurationSeconds": 900,
"deviationPercent": 50,
"baselineAverage": 312,
"effectiveMaxDurationSeconds": 420
},
"bedTimePeriod": { "mode": "organization", "start": "19:00:00", "end": "09:00:00", "timezone": "Europe/Berlin" },
"oxygenSaturation": { "mode": "organization", "enabled": true, "min": 92, "max": null },
"pulseRate": null,
"perfusionIndex": null,
"bodyTemperature": null
},
"ranges": {
"heartRate": { "min": 40, "max": 120 },
"respiratoryRate": { "min": 4, "max": 60 },
"oxygenSaturation": { "min": 70, "max": 100 },
"pulseRate": { "min": 40, "max": 120 },
"perfusionIndex": { "min": 0, "max": 20 },
"bodyTemperature": { "min": 34, "max": 42 }
},
"signalsSync": { "status": "ok" }
}
FieldTypeDescription
idstring | nullAlways null today.
patientIdstring (uuid)The resident.
projectIdstringThe caller's project.
updatedAtstring (ISO 8601 date-time) | nullWhen the home's thresholds were last saved. It does not move when only the resident's own limits are saved.
settingsobjectThe limits in effect for the resident after the save.
settings.heartRateobjectHeart-rate limit. Either the absolute shape or the baseline shape below.
settings.heartRate.mode"organization" | "patient" | "baseline"organization follows the home. patient is the resident's own absolute band. baseline is the resident's own baseline band.
settings.heartRate.enabledbooleanWhether the limit alerts.
settings.heartRate.minnumberLower bound in beats per minute. Absolute shape only.
settings.heartRate.maxnumberUpper bound in beats per minute. Absolute shape only.
settings.heartRate.deviationPercentnumberBaseline shape only: the symmetric deviation from the resident's baseline, in percent.
settings.heartRate.baselineAveragenumber | nullBaseline shape only: the resident's baseline average, or null when none is on record.
settings.heartRate.effectiveBandobject | nullBaseline shape only: the band in effect, or null without an average.
settings.heartRate.effectiveBand.minnumberLower bound of the band in effect.
settings.heartRate.effectiveBand.maxnumberUpper bound of the band in effect.
settings.respiratoryRateobjectRespiratory-rate limit. Same fields as settings.heartRate, in breaths per minute.
settings.outOfBedobjectOut-of-bed limit.
settings.outOfBed.mode"organization" | "patient"Whether the limit follows the home or is the resident's own.
settings.outOfBed.enabledbooleanWhether out-of-bed alerting is on.
settings.outOfBed.maxDurationSecondsintegerThe longest exit allowed, in seconds.
settings.outOfBed.deviationPercentinteger | nullThe relative band, in percent. null when off.
settings.outOfBed.baselineAveragenumber | nullThe resident's usual exit length in seconds. null with the band off, on too little data, or when it could not be read.
settings.outOfBed.effectiveMaxDurationSecondsnumber | nullThe limit the evaluator applies to this resident, in seconds. null when out-of-bed alerting is off.
settings.bedTimePeriodobjectThe window in which out-of-bed is checked.
settings.bedTimePeriod.mode"organization" | "patient"Whether the window is the home's or the resident's own.
settings.bedTimePeriod.startstring (HH:MM:SS)Window start, wall-clock time in timezone.
settings.bedTimePeriod.endstring (HH:MM:SS)Window end.
settings.bedTimePeriod.timezonestring | nullIANA time zone the window is read in, resolved by the server.
settings.oxygenSaturationobject | nullSpO₂ limit. null when neither the home nor the resident watches the vital.
settings.oxygenSaturation.mode"organization" | "patient"Whether the limit follows the home or is the resident's own.
settings.oxygenSaturation.enabledbooleanWhether the limit alerts.
settings.oxygenSaturation.minnumber | nullLower bound, or null for none.
settings.oxygenSaturation.maxnumber | nullUpper bound, or null for none.
settings.pulseRateobject | nullPulse-rate limit. Same fields as settings.oxygenSaturation.
settings.perfusionIndexobject | nullPerfusion-index limit. Same fields as settings.oxygenSaturation.
settings.bodyTemperatureobject | nullBody-temperature limit. Same fields as settings.oxygenSaturation.
rangesobjectThe home's range for each vital's limits. Always present.
ranges.heartRateobject{ min, max }, both number. Same shape for ranges.respiratoryRate, ranges.oxygenSaturation, ranges.pulseRate, ranges.perfusionIndex and ranges.bodyTemperature.
signalsSyncobject | nullnull when the save claimed nothing Signals evaluates.
signalsSync.status"ok" | "pending"ok: Signals confirmed the changed limit. pending: Signals confirmed it, but a metric shown as armed was sent to Signals as removed.
signalsSync.reasonstringPresent when status is pending: metric-disarmed: followed by the affected codes.

Errors​

StatusMeaning
400Your session has no project.
401Bearer token is missing, invalid or expired.
403You are not a practitioner, admin or System Owner, you lack Goal:update, or the patient carries no project.
404No patient with this id exists, or the patient is in another project.
409A save of the home's limits is running. Nothing was changed; try again once it is done.
422The body fails validation, or a limit is outside the home's ranges. The body lists each problem as a path and message.
424The body arms a baseline band for heart or respiratory rate with no stored average for this resident. The body names them in missing.
502The home's thresholds or the resident's stored out-of-bed band could not be read (nothing was saved), or the save changes a heart-rate, respiratory-rate or vital limit and Signals did not confirm it (the new limits are stored; the body has signalsSync: { "status": "pending", "reason": ... }; send the save again).
503The access policy could not be read right now. Retry.