---
title: "Create or update the notification rule of one priority (PATCH alias)"
method: PATCH
path: "/api/v1/recipients/{recipient}/notification-settings/{priority}"
tags: ["Notification settings"]
---

# Create or update the notification rule of one priority (PATCH alias)

`PATCH /api/v1/recipients/{recipient}/notification-settings/{priority}`

## Request

- Base URL: `https://wardenpoint.com` (the document declares no server; this is the origin it was published from)
- URL: `https://wardenpoint.com/api/v1/recipients/{recipient}/notification-settings/{priority}`
- Auth: API key in header `X-API-Key`

Identical to PUT on the same path. Both verbs write only the fields present in the body — with one exception that no verb changes: `escalation_rules` is a list, and sending it replaces the whole ladder rather than appending to it.

## Path parameters

- `recipient` string, uuid, required
- `priority` 'low' | 'normal' | 'high' | 'critical', required

## Request body

- NotificationSettingRequest: Every field is optional and only the fields you send are written — anything omitted keeps its stored value. Send an explicit null to clear a nullable field. **Two different sets of defaults exist, and which one you get depends on whether a rule was ever stored.** Creating a rule fills the fields you omitted with this schema's stated defaults and they are now stored values, yours to change. A priority you never wrote a rule for has no stored values at all: reading it returns `is_customised: false` and the platform-wide defaults for that priority, which are not the same numbers — they are deliberately stricter on `low` and looser on `critical`. So "the rule says min_interval_minutes 0" and "nothing was configured and 5 applies" are different states that both come back as a number. **Every hour in this schema — the quiet window, the schedule grid, the day the daily cap counts — is read on the platform clock, `Europe/Kyiv`.** A recipient carries no timezone of their own, so a quiet window of 22–07 is 22–07 in Kyiv for everyone in the account, whatever local time that is for the person being paged. **Being over a limit does not postpone an alert indefinitely, and for two of the four it does not postpone it at all.** A quiet window and a `min_interval_minutes` gap name the moment they end, so the alert waits and is delivered then — provided that moment falls inside the hour a send is retried for. Beyond that hour, and for the daily cap (which ends at midnight, almost always further off than an hour), the alert ends `cancelled`: it carries `blocked_reason` and the time it would have become allowed, and nobody re-sends it. An hour outside `schedule_grid` names no moment to wait for at all, so that one does not wait even briefly — it ends `failed`. Read the two statuses as they are meant: `cancelled` is the platform declining to page on your instructions, `failed` is a delivery that did not happen. **Only the first page is subject to any of this.** Escalation steps, the owner fallback and test alerts bypass all four checks by design — an escalation that went quiet during quiet hours would be an escalation chain running with nobody at the end of it. So a quiet window mutes the opening page and nothing that follows it.
  - `escalation_rules` EscalationStep[], nullable: Inline escalation steps, replaced wholesale. An empty list does not disable escalation: a saved row with no steps still inherits the account-wide default steps, so a recipient can never end up silently unreachable. To hand the steps over to a policy, send escalation_policy_uuid instead.
    - `trigger` 'initial' | 'on_fail' | 'timeout': When the step runs. `initial` — the first dispatch. `on_fail` — the preceding dispatch could not be delivered at all (not a timeout: delivery failed). `timeout` — nobody acknowledged within delay_minutes; this is the step that escalates.
    - `action` 'send' | 'retry' | 'notify_manager': `send` — deliver over this step's channel/provider. `retry` — repeat the previous dispatch to the same target. `notify_manager` — deliver to somebody else; requires target_recipient_uuid.
    - `delay_minutes` union: How long the step waits before it runs, counted from the moment the chain reached it. On a timeout step this is therefore the acknowledgement window of the previous step. 0 means immediately, and is honoured literally — a timeout step at 0 runs as soon as the preceding dispatch is out, which is how you reach everybody at once. The literal string "adaptive" derives the delay from the alert priority and from how fast this company usually acknowledges; the `bounds_minutes` and `priority_defaults_minutes` the vocabulary reports under `adaptive` constrain that derived value only and never the number you send here.
      - integer
      - 'adaptive'
    - `channel` 'voice_call' | 'text_message' | 'voice_message', nullable: `text_message` — a written message. `voice_message` — a pre-rendered voice note inside a messenger. `voice_call` — a call that rings the person. Omit and the step delivers as a `text_message`, with the provider picked from the contacts that recipient can actually receive on — the same thing `provider: auto` does. Omitting it does not mean "no delivery": a step always names a channel by the time it runs.
    - `provider` 'telegram' | 'telegram_bot' | 'platform_asterisk' | 'custom_asterisk' | 'whatsapp' | 'viber' | 'email' | 'sms' | 'slack' | 'msteams' | 'auto', nullable: Who delivers it: telegram, telegram_bot, platform_asterisk, custom_asterisk, email, sms, whatsapp, viber, slack, msteams — or "auto" to pick the first provider the recipient actually has a contact for. A slug outside this list is refused, and so is a provider that cannot carry the step's channel — email cannot place a voice_call. Which providers this company may use depends on its plan; call GET /escalation-policies/vocabulary for the live list.
    - `ack_on` 'button' | 'read' | 'delivered', nullable: What counts as acknowledgement and therefore stops the chain. `button` — the person pressed acknowledge (the only mode that proves a human saw it). `read` — the provider reported a read receipt. `delivered` — the provider reported delivery; for voice_call it means the call was answered. Not every provider supports every mode — see ack_capabilities in the vocabulary. A mode the provider cannot report never acknowledges.
    - `target_recipient_uuid` string, uuid, nullable: Deliver this step to a specific person instead of the alert recipient. Required when action is notify_manager.
    - `schedule_targets` object[], nullable: Priority-ordered on-call targets. The first entry that resolves to somebody wins.
      - `schedule_uuid` string, uuid: An active schedule of this company.
      - `role` 'primary' | 'secondary' | 'manager': Which slot of the rotation to page. These are parallel duty tiers, not an order of escalation: `secondary` is the person covering alongside `primary` right now, NOT the person who takes over from them. For "the next one" use `occupant`.
      - `occupant` 'current' | 'next', nullable: Which person of that rotation this entry means. `current` — the default, and what every step written before this field existed does: whoever is on duty in that role right now. `next` — whoever takes that same role at the next handoff of that same rotation. This is how you say "if nobody answers, wake the next one" without inventing a second rotation shifted by one person: two rotations agree only until somebody regenerates one of them with a different line-up, and nothing in this API would report the drift. `next` is counted from the moment the alert was raised, not from the moment the step runs — a chain can cross a handoff, and an alert at 08:55 whose second rung fires at 09:05 must reach the person who came on at 09:00, not the one after them. An override on the next shift wins, so `next` names whoever will actually answer. It can legitimately resolve to nobody: when the next person is the one already on duty (a single-member rotation, or an override handing the shift back to them) it does not page them twice — escalating to the same person is what `retry` is for — and when the rotation has no shift after this one it has nobody to name. Either way the step moves on to its next candidate and then to fallback_recipient_uuid, and the outcome is reported to the operator, not through this API: a warning naming the reason (`same_as_current`, `no_upcoming_shift`) and the `wardenpoint_next_on_call_unmet_total` counter.
    - `fallback_recipient_uuid` string, uuid, nullable: Used when no schedule target resolved to anybody.
    - `target_priority` 'schedule_first', nullable: Send "schedule_first" to consult the rotations before target_recipient_uuid. Omit for the default order: specific person, then rotations, then fallback.
    - `audio_source` 'tts' | 'file', nullable: For voice channels: `tts` synthesises the alert text, `file` plays a stored recording.
    - `audio_file_uuid` string, uuid, nullable: The recording to play. Required when audio_source is file.
    - `tts_engine` string, nullable: Speech engine override. Premium engines need a plan that includes them.
    - `tts_text_override` string, nullable: Say this instead of the alert text.
    - `ai_processing` boolean, nullable: Let the AI rewrite the alert into speakable prose before synthesis. Requires a plan with custom TTS.
  - `escalation_policy_uuid` string, uuid, nullable: Point this rule at a reusable policy. Send null to detach and fall back to the inline steps.
  - `schedule_grid` object, nullable: Allowed delivery hours as {weekday "0".."6": [hours 0..23]}. A complete 7x24 grid is stored as "round the clock".
  - `quiet_hours_start` integer, nullable: Hour the quiet window opens, on the platform clock. Null (the default) means no quiet hours and nothing on this priority is ever held back for the time of day.
  - `quiet_hours_end` integer, nullable: Hour the quiet window closes. Required whenever quiet_hours_start is sent. May be smaller than the start — the window then crosses midnight, which is the usual shape (22 to 7).
  - `bypass_quiet_hours_critical` boolean: Let `critical` alerts through the quiet window. Only `critical` — the other three priorities wait until the window closes however this is set. Default true, so setting a quiet window does not by itself silence the page you would want to be woken for.
  - `min_interval_minutes` integer: Smallest gap between two alerts to this person, in minutes. Default 0, meaning no gap. The gap is measured against their **last completed delivery on any priority**, not just this one: the number is read from the rule of the priority being sent, but the clock it compares against is shared by the whole person. A `low` rule with a 30-minute gap therefore also swallows a `low` alert that arrives ten minutes after a `critical` one.
  - `max_daily_notifications` integer, nullable: Cap on deliveries to this person per calendar day. 0 (the default) or null means no cap. Counted the same way the gap is measured — every **completed** delivery to this person that day, on any priority, test alerts excluded. Attempts that failed or were held back do not consume the quota; the day rolls over at midnight on the platform clock.
  - `audio_source` 'tts' | 'file', nullable: What a voice channel says. `tts` or null (the default) synthesises the alert text. `file` plays a recording you uploaded, named by audio_file_uuid. Ignored by text channels. `file` is a preference, not a guarantee: if the recording is missing or still converting, delivery falls through to the audio of the matching escalation step, and then to synthesised text. A voice alert is never dropped for want of a file.
  - `audio_file_uuid` string, uuid, nullable: The recording to play. Required when audio_source is file, ignored otherwise. Must be a file of this company that finished converting.

## Response `200`

The stored rule.

- object
  - `data` NotificationSetting: The rule for one recipient on one priority. There is no identifier: the row is addressed by recipient and priority, and all four priorities are always present in a listing — an unconfigured one shows what would apply by default. **Where the priority of an observability alert comes from.** Not from the alert's severity. A routing rule whose action is `escalate` produces a `critical` notification; every other notifying action produces a `normal` one. So a rule keyed on `critical` here is reached by alerts an `escalate` routing rule matched, whatever their severity was, and a `warning` alert routed with `message` lands on the `normal` rule. Configuring `critical` and expecting Grafana's `severity: critical` to arrive there is the mistake this note exists to prevent — see the AccumulationRule schema.
    - `priority` 'low' | 'normal' | 'high' | 'critical'
    - `is_customised` boolean: False means nothing was ever saved for this priority: the values below are the defaults that would apply, and the steps come from the account-wide floor.
    - `escalation_policy_uuid` string, uuid, nullable: When set, the policy supplies the steps and the inline steps below are ignored — unless the policy has no steps of its own, in which case the inline ones run after all. Either way this response does not inline the policy's steps: read the policy by this UUID, or call the preview endpoint, which answers "what would actually run" in one call.
    - `steps` EscalationStep[], nullable: The steps stored **on this rule** — not necessarily the ones that would run. With escalation_policy_uuid set they are the ignored ones, kept for the moment you detach the policy, and a policy-backed rule therefore commonly reads back as an empty list. Do not conclude from an empty `steps` that nobody would be paged: on an unconfigured priority it is null and the account-wide default ladder applies, and empty likewise falls back to that ladder rather than to silence.
      - `trigger` 'initial' | 'on_fail' | 'timeout': When the step runs. `initial` — the first dispatch. `on_fail` — the preceding dispatch could not be delivered at all (not a timeout: delivery failed). `timeout` — nobody acknowledged within delay_minutes; this is the step that escalates.
      - `action` 'send' | 'retry' | 'notify_manager': `send` — deliver over this step's channel/provider. `retry` — repeat the previous dispatch to the same target. `notify_manager` — deliver to somebody else; requires target_recipient_uuid.
      - `delay_minutes` union: How long the step waits before it runs, counted from the moment the chain reached it. On a timeout step this is therefore the acknowledgement window of the previous step. 0 means immediately, and is honoured literally — a timeout step at 0 runs as soon as the preceding dispatch is out, which is how you reach everybody at once. The literal string "adaptive" derives the delay from the alert priority and from how fast this company usually acknowledges; the `bounds_minutes` and `priority_defaults_minutes` the vocabulary reports under `adaptive` constrain that derived value only and never the number you send here.
        - integer
        - 'adaptive'
      - `channel` 'voice_call' | 'text_message' | 'voice_message', nullable: `text_message` — a written message. `voice_message` — a pre-rendered voice note inside a messenger. `voice_call` — a call that rings the person. Omit and the step delivers as a `text_message`, with the provider picked from the contacts that recipient can actually receive on — the same thing `provider: auto` does. Omitting it does not mean "no delivery": a step always names a channel by the time it runs.
      - `provider` 'telegram' | 'telegram_bot' | 'platform_asterisk' | 'custom_asterisk' | 'whatsapp' | 'viber' | 'email' | 'sms' | 'slack' | 'msteams' | 'auto', nullable: Who delivers it: telegram, telegram_bot, platform_asterisk, custom_asterisk, email, sms, whatsapp, viber, slack, msteams — or "auto" to pick the first provider the recipient actually has a contact for. A slug outside this list is refused, and so is a provider that cannot carry the step's channel — email cannot place a voice_call. Which providers this company may use depends on its plan; call GET /escalation-policies/vocabulary for the live list.
      - `ack_on` 'button' | 'read' | 'delivered', nullable: What counts as acknowledgement and therefore stops the chain. `button` — the person pressed acknowledge (the only mode that proves a human saw it). `read` — the provider reported a read receipt. `delivered` — the provider reported delivery; for voice_call it means the call was answered. Not every provider supports every mode — see ack_capabilities in the vocabulary. A mode the provider cannot report never acknowledges.
      - `target_recipient_uuid` string, uuid, nullable: Deliver this step to a specific person instead of the alert recipient. Required when action is notify_manager.
      - `schedule_targets` object[], nullable: Priority-ordered on-call targets. The first entry that resolves to somebody wins.
        - `schedule_uuid` string, uuid: An active schedule of this company.
        - `role` 'primary' | 'secondary' | 'manager': Which slot of the rotation to page. These are parallel duty tiers, not an order of escalation: `secondary` is the person covering alongside `primary` right now, NOT the person who takes over from them. For "the next one" use `occupant`.
        - `occupant` 'current' | 'next', nullable: Which person of that rotation this entry means. `current` — the default, and what every step written before this field existed does: whoever is on duty in that role right now. `next` — whoever takes that same role at the next handoff of that same rotation. This is how you say "if nobody answers, wake the next one" without inventing a second rotation shifted by one person: two rotations agree only until somebody regenerates one of them with a different line-up, and nothing in this API would report the drift. `next` is counted from the moment the alert was raised, not from the moment the step runs — a chain can cross a handoff, and an alert at 08:55 whose second rung fires at 09:05 must reach the person who came on at 09:00, not the one after them. An override on the next shift wins, so `next` names whoever will actually answer. It can legitimately resolve to nobody: when the next person is the one already on duty (a single-member rotation, or an override handing the shift back to them) it does not page them twice — escalating to the same person is what `retry` is for — and when the rotation has no shift after this one it has nobody to name. Either way the step moves on to its next candidate and then to fallback_recipient_uuid, and the outcome is reported to the operator, not through this API: a warning naming the reason (`same_as_current`, `no_upcoming_shift`) and the `wardenpoint_next_on_call_unmet_total` counter.
      - `fallback_recipient_uuid` string, uuid, nullable: Used when no schedule target resolved to anybody.
      - `target_priority` 'schedule_first', nullable: Send "schedule_first" to consult the rotations before target_recipient_uuid. Omit for the default order: specific person, then rotations, then fallback.
      - `audio_source` 'tts' | 'file', nullable: For voice channels: `tts` synthesises the alert text, `file` plays a stored recording.
      - `audio_file_uuid` string, uuid, nullable: The recording to play. Required when audio_source is file.
      - `tts_engine` string, nullable: Speech engine override. Premium engines need a plan that includes them.
      - `tts_text_override` string, nullable: Say this instead of the alert text.
      - `ai_processing` boolean, nullable: Let the AI rewrite the alert into speakable prose before synthesis. Requires a plan with custom TTS.
    - `schedule_grid` object, nullable: Allowed delivery hours as {weekday "0".."6": [hours 0..23]}. Null means round the clock.
    - `quiet_hours_start` integer, nullable: Hour of day the quiet window opens. Null means no quiet hours.
    - `quiet_hours_end` integer, nullable: Hour of day the quiet window closes. May be smaller than the start — the window then crosses midnight.
    - `bypass_quiet_hours_critical` boolean: Critical alerts ignore the quiet window. Only critical — the other three priorities always wait.
    - `min_interval_minutes` integer: Minimum gap between two alerts to this person, in minutes; 0 means no gap. The number belongs to this priority's rule, but the gap it enforces is measured against the person's last completed delivery on **any** priority.
    - `max_daily_notifications` integer, nullable: Daily cap on deliveries to this person. 0 or null means no cap. Counts completed, non-test deliveries on any priority, resetting at midnight on the platform clock.
    - `audio_source` 'tts' | 'file', nullable: What a voice channel says on this priority: `file` plays the recording named by audio_file_uuid, `tts` or null synthesises the alert text. A recording that is missing or still converting falls back to synthesis rather than failing the call.
    - `audio_file_uuid` string, uuid, nullable: The recording audio_source refers to. Null when none is chosen, and meaningless unless audio_source is `file`.
    - `updated_at` string, date-time, nullable

## Other responses

- `401`: API key missing, invalid, revoked or expired.
- `403`: API key lacks the config.write ability, or the recipient belongs to another company.
- `404`: No recipient with this UUID, or the priority is not one of the four.
- `422`: Validation failed.

---

[API](https://skmtc.dev/wardenpoint/apis/wardenpoint-api.md) · [All operations](https://skmtc.dev/wardenpoint/apis/wardenpoint-api/llms.txt) · [OpenAPI document](https://skmtc.dev/wardenpoint/apis/wardenpoint-api/revisions/259ee445728e?raw)
