---
title: "Create an escalation policy"
method: POST
path: "/api/v1/escalation-policies"
tags: ["Escalation policies"]
---

# Create an escalation policy

`POST /api/v1/escalation-policies`

## 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/escalation-policies`
- Auth: API key in header `X-API-Key`

Creates a named chain that notification settings can point at. Steps run in the order given. Names are not unique and creating is not idempotent — a repeated call produces a second policy, so check the listing first if you are retrying. Call GET /escalation-policies/vocabulary to learn which providers this company may use before composing the steps.

## Request body

- EscalationPolicyRequest
  - `name` string, required: Display name. Nothing stops two policies of a company sharing one, and this is the name that identifies the policy when a deletion is refused because it holds a recipient or a schedule — a second policy called "Night shift" makes that answer useless. Choose it as the label somebody will read under pressure.
  - `description` string, nullable: Free text for people, never used to route anything. Omitting the field on an update leaves the stored text alone; sending an explicit null erases it. On create the two are indistinguishable.
  - `rules` EscalationStep[], required: The full chain, in execution order. Replaced wholesale on update — a list of steps is an order, and "append to an order" means nothing. At least one step, at most 50.
    - `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.

## Response `201`

The created policy.

- object
  - `data` EscalationPolicy: A named, reusable chain of escalation steps. Notification settings point at a policy instead of carrying their own steps, so one edit changes every rule that uses it.
    - `uuid` string, uuid
    - `name` string
    - `description` string, nullable
    - `is_template` boolean: True for platform-provided starting points. Policies created through this API are never templates.
    - `steps` EscalationStep[]: The chain, in execution order.
      - `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.
    - `used_by` object: How many distinct recipients and groups point at this policy. Four priority rules of one person count as one recipient.
      - `recipients` integer
      - `groups` integer
    - `created_at` string, date-time
    - `updated_at` string, date-time

## Other responses

- `401`: API key missing, invalid, revoked or expired.
- `403`: API key lacks the config.write ability.
- `422`: Validation failed — no steps at all, more than 50 steps, more than 10 schedule targets on one step, an unknown trigger/action/channel/ack mode, a notify_manager step without a target, or a recipient, audio file or schedule UUID that belongs to another company.

---

[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)
