Skip to main content

Update org threshold (v2)

MethodPath
PUT/v2/slim/threshold

Authentication · Access policies

Saves the home's threshold configuration in the v2 shape: the alert limits every resident follows unless they have limits of their own. mode is required on every settings field; patient is not a valid mode here.

This route supersedes PUT /v1/slim/threshold. Only it can set the home's limit ranges, the optional vitals, the relative out-of-bed band and the bed-time window.

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 PlanDefinition:update (admins and super admins skip this check). Scope: The caller's project (from the token).

Request​

Body​

Keys not listed below are ignored at the top level and inside settings. Inside each metric object, an unlisted key answers 422, except the response-only keys marked "ignored".

NameTypeRequiredDescription
settingsobjectYesThe home's limits.
settings.heartRateobjectYesHeart-rate limit.
settings.heartRate.mode"organization"YesThe only accepted value. "baseline" answers 422: a home's band is absolute, and baseline limits are set per resident.
settings.heartRate.enabledbooleanYesWhether the limit alerts.
settings.heartRate.minnumberYesLower 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.maxnumberYesUpper bound in beats per minute, 20 to 300 and inside the home's heart-rate range.
settings.respiratoryRateobjectYesRespiratory-rate limit. Same fields as settings.heartRate.
settings.respiratoryRate.min, settings.respiratoryRate.maxnumberYesBounds in breaths per minute, 4 to 150 and inside the home's respiratory-rate range (default 4 to 60). min must be below max when enabled.
settings.outOfBedobjectYesOut-of-bed limit.
settings.outOfBed.mode"organization"YesThe only accepted value.
settings.outOfBed.enabledbooleanYesWhether out-of-bed alerting is on.
settings.outOfBed.maxDurationSecondsintegerYesThe longest exit allowed, in seconds. 0 to 86400, a multiple of 60. Must be above 0 when enabled is true.
settings.outOfBed.deviationPercentinteger | nullNoAlert 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.
settings.outOfBed.baselineAveragenumber | nullNoResponse-only; ignored.
settings.outOfBed.effectiveMaxDurationSecondsnumber | nullNoResponse-only; ignored.
settings.bedTimePeriodobjectYesThe window in which out-of-bed is checked.
settings.bedTimePeriod.mode"organization"YesThe only accepted value.
settings.bedTimePeriod.startstring (HH:MM:SS)YesWindow start, wall-clock time in the home's time zone.
settings.bedTimePeriod.endstring (HH:MM:SS)YesWindow end. Equal to start means 24 hours; before start crosses midnight.
settings.bedTimePeriod.timezonestring | nullNoResponse-only; ignored. The server resolves the zone.
settings.oxygenSaturationobject | nullNoSpO₂ limit. Left out or null keeps what is stored.
settings.oxygenSaturation.mode"organization"Yes, if the object is sentThe only accepted value.
settings.oxygenSaturation.enabledbooleanYes, if the object is sentWhether the limit alerts. Disabling removes the vital's rules in Signals.
settings.oxygenSaturation.min, settings.oxygenSaturation.maxnumber | nullYes, if the object is sentBounds 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.
rangesobjectNoThe range each vital's limits may take, for the home and every resident. A vital left out keeps its stored range, or the default.
ranges.heartRateobjectNo{ min, max }, both number, 20 to 300, min below max.
ranges.respiratoryRateobjectNo{ min, max }, 4 to 150, min below max.
ranges.oxygenSaturationobjectNo{ min, max }, 50 to 100, min below max. The home must have an oxygenSaturation setting, stored or in this body.
ranges.pulseRateobjectNo{ min, max }, 20 to 300, min below max. Needs a pulseRate setting.
ranges.perfusionIndexobjectNo{ min, max }, 0 to 20, min below max. Needs a perfusionIndex setting.
ranges.bodyTemperatureobjectNo{ min, max }, 30 to 45, min below max. Needs a bodyTemperature setting.

Behaviour​

  • settings.heartRate and settings.respiratoryRate: mode: "organization" with enabled, min and max. The home's band is absolute; mode: "baseline" answers 422, because a resident without a baseline of their own would get no alarm from it. Set baseline limits per resident with Update patient threshold (v2).
  • settings.outOfBed: mode: "organization" with enabled and maxDurationSeconds, plus an optional deviationPercent. With it, every resident on the home's out-of-bed setting alerts once an exit runs that far past their own usual exit, if that is sooner than maxDurationSeconds. Omit it to keep the stored value.
  • settings.bedTimePeriod: start and end as HH:MM:SS. end equal to start means 24 hours; end before start crosses midnight.
  • settings.oxygenSaturation, pulseRate, perfusionIndex, bodyTemperature (each optional): the home's default for every resident. Either bound may be null for a one-sided limit. Omitting one, or sending null, keeps what is stored. Disabling one removes its rules in Signals.
  • ranges (optional): the range each vital's limits may take. A range for SpO₂, pulse rate, perfusion index or body temperature is kept with that vital's setting, so the home must have set the vital (it may be disabled); otherwise the PUT answers 422 on ranges.<vital>.
  • Every enabled limit must sit inside its range, counting ranges in the same request. One outside answers 422 naming the field (for example settings.heartRate.max) and nothing is saved. A disabled band is not checked.
  • Narrowing a range does not rewrite residents whose limits fall outside it. A resident's next change to such a limit must fit; their other edits still save.
  • Medical limits (heart rate, respiratory rate and the optional vitals) are evaluated by Signals. A save that changes one is all or nothing: it writes every limit it changes to each resident following the home on it, and answers only once Signals holds them for all of them. A resident with limits of their own for a vital keeps them, and a limit switched off is removed from the followers' rules only. Residents Signals does not hold yet are enrolled first. While it runs, a resident's own limits save answers 409.
  • Homes with many residents take longer: the save gives up after about 25 seconds and puts back what it wrote. Putting back can take longer than the gateway allows, so the caller may get a 504 while it finishes. Read the limits (Get current org threshold (v2)) before sending the save again: until the undo is done, another save answers 409.
  • If any of that fails, everything the save changed is put back and it answers an error with saved: false and unconfirmedResidents (the resident ids it flagged "limits not confirmed"). Nothing is stored; send the save again:
    • 409: another save of this home's limits is running, the home is moving to another Signals tenant, or a resident's or the home's own limits were saved while this one ran.
    • 422: more than 1,000 residents follow the limits the save changes (the most one save writes).
    • 502: the save ran out of time, or Signals did not take the limits for every resident. Residents Signals holds no patient for are named in unconfirmedResidents: re-push them with POST /signals/config/unconfirmed/repush (the same access as this save) before saving again.
    • 500: putting the previous limits back failed too. The residents left behind are named in unconfirmedResidents and flagged, and POST /signals/config/unconfirmed/repush puts them right.
  • Any other save (out of bed, the bed-time window) is stored and passed on to residents in the background.
  • If the stored thresholds cannot be read, the PUT answers 502 and nothing is saved.
  • A GET response can be sent back as a PUT body, with two exceptions: a heart-rate or respiratory-rate field still stored on baseline (see above), and a range the GET reports for an optional vital the home has never set.
  • A change-history entry is written for a save that stands. The response is the saved configuration, with signalsSync: { "status": "ok" } when the save changed medical limits and null otherwise.

Example​

curl -X PUT 'https://api.sandbox.ovok.com/v2/slim/threshold' \
-H "Authorization: Bearer ${OVOK_TOKEN}" \
-H 'Content-Type: application/json' \
-d '{
"settings": {
"heartRate": { "mode": "organization", "enabled": true, "min": 40, "max": 120 },
"respiratoryRate": { "mode": "organization", "enabled": true, "min": 8, "max": 25 },
"outOfBed": { "mode": "organization", "enabled": true, "maxDurationSeconds": 900, "deviationPercent": 50 },
"bedTimePeriod": { "mode": "organization", "start": "22:00:00", "end": "06:00:00" },
"oxygenSaturation": { "mode": "organization", "enabled": true, "min": 92, "max": null }
},
"ranges": { "heartRate": { "min": 40, "max": 140 } }
}'

Successful response​

200 — v2 threshold updated successfully. The body is the saved configuration, in the same shape as Get current org threshold (v2).

{
"id": "3f1c2b7e-8d4a-4c1e-9b2f-6a7d5e4c3b31",
"projectId": "3f1c2b7e-8d4a-4c1e-9b2f-6a7d5e4c3b11",
"updatedAt": "2026-10-09T09:12:44.000Z",
"settings": {
"heartRate": { "mode": "organization", "enabled": true, "min": 40, "max": 120 },
"respiratoryRate": { "mode": "organization", "enabled": true, "min": 8, "max": 25 },
"outOfBed": {
"mode": "organization",
"enabled": true,
"maxDurationSeconds": 900,
"deviationPercent": 50,
"baselineAverage": null,
"effectiveMaxDurationSeconds": null
},
"bedTimePeriod": { "mode": "organization", "start": "22:00:00", "end": "06: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": 140 },
"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 | nullThe id of the stored threshold document.
projectIdstringThe caller's project.
updatedAtstring (ISO 8601 date-time) | nullWhen this save was stored.
settingsobjectThe home's limits as saved.
settings.heartRateobjectHeart-rate limit. Either the absolute shape or, for a legacy home, the baseline shape below.
settings.heartRate.mode"organization" | "baseline"organization for an absolute band. baseline only for a legacy home stored on a 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.baselineAveragenullBaseline shape only. Always null: the baseline is per resident.
settings.heartRate.effectiveBandnullBaseline shape only. Always null: the band is per resident.
settings.respiratoryRateobjectRespiratory-rate limit. Same fields as settings.heartRate, in breaths per minute.
settings.outOfBedobjectOut-of-bed limit.
settings.outOfBed.mode"organization"Always organization on this route.
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.baselineAveragenullAlways null here: the usual exit length is per resident.
settings.outOfBed.effectiveMaxDurationSecondsnullAlways null here: the limit in effect is per resident.
settings.bedTimePeriodobjectThe window in which out-of-bed is checked.
settings.bedTimePeriod.mode"organization"Always organization on this route.
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 the home does not watch the vital.
settings.oxygenSaturation.mode"organization"Always organization on this route.
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 range each vital's limits may take in this home, after this save.
ranges.heartRateobject{ min, max }, both number. Same shape for ranges.respiratoryRate, ranges.oxygenSaturation, ranges.pulseRate, ranges.perfusionIndex and ranges.bodyTemperature.
signalsSyncobject | null{ "status": "ok" } when the save changed a medical limit and Signals confirmed it for every resident following the home. null when the save changed only out-of-bed or the bed-time window.
signalsSync.status"ok"Present when signalsSync is not null.

Errors​

StatusMeaning
400Your session has no project, or the FHIR server refused the write.
401Bearer token is missing, invalid or expired.
403You are not a practitioner, admin or System Owner, or you lack PlanDefinition:update.
409Another save of this home's limits is running (a save the gateway timed out may still be undoing), the home is moving to another Signals tenant, or a resident's or the home's own limits were saved while this one ran. Nothing was changed; read the limits, then send the save again.
422The body fails validation (including mode: "baseline" on heart or respiratory rate), a limit is outside the home's ranges, ranges names an optional vital the home has no setting for, or more than 1,000 residents follow the limits it changes. The body lists each problem as a path and message.
500The save failed and putting the previous limits back failed too. The body has saved: false and the residents left behind in unconfirmedResidents.
502The home's stored thresholds could not be read, the save ran out of time, or Signals did not take the changed heart-rate, respiratory-rate or vital limits for every resident following the home. Nothing was changed; send the save again.
503The access policy could not be read right now. Retry.
504The gateway's 29 s ran out while a failed save was still being put back (a home with many residents). Read the limits before sending the save again: until that is done, another save answers 409.