---
title: "Replace a routing rule"
method: PUT
path: "/api/v1/observability/routing-rules/{routing_rule}"
tags: ["Alert routing rules"]
---

# Replace a routing rule

`PUT /api/v1/observability/routing-rules/{routing_rule}`

## 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/observability/routing-rules/{routing_rule}`
- Auth: API key in header `X-API-Key`

FULL REPLACEMENT, not a merge. Every field you do not send is RESET, not kept: `PUT {"name": "P1 → SRE"}` on a rule that used to catch `critical` + `firing` for the SRE group leaves it with no severities, no statuses, no label matchers and no recipient group. Read the rule first, change what you want, send the whole thing back.

That reset is not a cosmetic loss. An empty filter set means MATCHES EVERYTHING, so a rule stripped this way becomes a catch-all at its own position: every rule after it in the integration stops being reached, and the emptied rule falls back to its default action, which suppresses. Alerts keep arriving and nobody gets woken. The answer is still 200 and nothing appears in any log.

`position` is not settable here — use the reorder endpoint. There is no PATCH on this path: it would read as partial update and behave as replacement, so it answers 405.

## Path parameters

- `routing_rule` string, uuid, required

## Request body

- AlertRoutingRuleRequest: Every field is optional. A rule with no conditions matches every alert of its integration — a catch-all. Such a rule always belongs last, and creating one does not shadow later work: a rule created afterwards is inserted ABOVE the existing catch-all rather than under it. **The action fields decide the notification priority; the alert severity does not.** `threshold_action: escalate` sends at priority `critical` and starts the escalation chain; `message` sends at priority `normal` and starts no chain, so only the `initial` step of the escalation policy runs. Matching on `severities` chooses WHICH alerts the rule sees — it has no effect on how loudly they arrive. See the AccumulationRule schema for the full statement. On update this body is the WHOLE rule: fields you leave out are reset, not kept. That reset is the one way to still shoot yourself here — a PUT that drops every condition turns the rule into a catch-all IN PLACE, where it may sit above others and swallow them. See the PUT endpoint. **The five accumulation fields — `threshold_count`, `window_minutes` and the three actions — are read as one block, not one by one.** Leave all five out and the rule inherits the integration's accumulation rules entirely. Set even one and the block takes over: the other four stop inheriting and take the fallbacks named on each field below, which are not the integration's values. Sending nothing but `threshold_action: escalate` therefore also buys a 1-minute window, a threshold of 1, and `after_threshold_action: suppress` — a cool-down you did not ask for. If you override one, state all five.
  - `name` string, nullable: Label for humans.
  - `recipient_group_uuid` string, uuid, nullable: Group to notify when the rule matches. Must belong to the same company as the API key — a group UUID from anywhere else is refused with 422 rather than stored as "no group". Null mutes matching alerts on purpose.
  - `target_kind` 'group' | 'event': Who the rule pages: `group` (a recipient group you name) or `event` (the people the alert itself names — a GitLab merge request lists its reviewers). `event` is accepted only for sources that name people; every other source rejects it with 422. A rule with `event` takes no `recipient_group_uuid`.
  - `addressee_set` 'all' | 'added' | 're_requested' | 'reviewed', nullable: Which of the people named by the event to page. Required when `target_kind` is `event`, otherwise null. `all` — every reviewer; `added` — only those this event added; `re_requested` — only those asked to review again; `reviewed` — only those who already looked at the diff. Anyone the event names but whose login is not bound to a recipient on the integration page is recorded there for binding, and an event where nobody is bound lands in the triage inbox with `addressee_unresolved`.
  - `severities` string[], nullable: Empty or omitted means any severity.
  - `statuses` string[], nullable: Empty or omitted means any status.
  - `label_matchers` LabelMatcher[], nullable: All matchers must hold for the rule to match.
    - `key` string, required: Label name as it arrives in the alert payload.
    - `op` '=' | '!=' | '=~' | '!~', required: Comparison: `=` equals, `!=` differs, `=~` matches the regex, `!~` does not match it.
    - `value` string, required: Value, or regex pattern for the `=~` and `!~` operators.
  - `threshold_count` integer, nullable: Occurrences needed to reach threshold_action, overriding the integration for alerts this rule matches. Omit to inherit — but read the note above about what "omit" means once any sibling is set.
  - `window_minutes` integer, nullable: Length of the counting window, overriding the integration. Falls back to **1 minute** when the override block is engaged, which is short enough that a threshold above 1 is effectively never reached — set it deliberately.
  - `below_threshold_action` 'suppress' | 'message' | 'escalate', nullable: Applied to occurrences before the threshold. Falls back to the integration's default action.
  - `threshold_action` 'suppress' | 'message' | 'escalate', nullable: Applied to the occurrence that hits the threshold — this is the one that decides whether people are woken, and at which priority: `escalate` pages at `critical` and starts the escalation chain, `message` pages at `normal` and starts none, `suppress` records without notifying. Falls back to the integration's default action.
  - `after_threshold_action` 'suppress' | 'message' | 'escalate', nullable: Applied to further occurrences in the same window. Falls back to **`suppress`**, not to the integration default — one incident pages once by design. That is also why leaving this out while setting a sibling can quietly mute a repeating alert for the rest of the window.

## Response `200`

Updated rule.

- object
  - `data` AlertRoutingRule: One rule of an integration. Rules are evaluated in `position` order and the first match decides where the alert goes.
    - `uuid` string, uuid
    - `name` string, nullable: Label for humans. Optional.
    - `position` integer: Priority: 0 is evaluated first. Assigned automatically on create — appended last, except that a rule with no conditions (a catch-all) is never allowed to sit above a rule added after it: the new rule takes the catch-all's slot and the catch-all moves down. Change the order with the reorder endpoint.
    - `recipient_group_uuid` string, uuid, nullable: Group notified when the rule matches. Null means the rule matches but pages nobody — useful as a deliberate mute.
    - `target_kind` 'group' | 'event': Who the rule pages: `group` (a recipient group you name) or `event` (the people the alert itself names — a GitLab merge request lists its reviewers). `event` is accepted only for sources that name people; every other source rejects it with 422. A rule with `event` takes no `recipient_group_uuid`.
    - `addressee_set` 'all' | 'added' | 're_requested' | 'reviewed', nullable: Which of the people named by the event to page. Required when `target_kind` is `event`, otherwise null. `all` — every reviewer; `added` — only those this event added; `re_requested` — only those asked to review again; `reviewed` — only those who already looked at the diff. Anyone the event names but whose login is not bound to a recipient on the integration page is recorded there for binding, and an event where nobody is bound lands in the triage inbox with `addressee_unresolved`.
    - `recipient_group_name` string, nullable
    - `severities` string[]: Severities the rule matches. Empty means any.
    - `statuses` string[]: Statuses the rule matches. Empty means any.
    - `label_matchers` LabelMatcher[]: Label conditions, all of which must hold (AND).
      - `key` string, required: Label name as it arrives in the alert payload.
      - `op` '=' | '!=' | '=~' | '!~', required: Comparison: `=` equals, `!=` differs, `=~` matches the regex, `!~` does not match it.
      - `value` string, required: Value, or regex pattern for the `=~` and `!~` operators.
    - `threshold_count` integer, nullable: Per-rule override of the integration thresholds. Null falls back to the integration — but only while **all five** override fields are null; see below.
    - `window_minutes` integer, nullable: Length of the counting window. Null here does not always mean "inherits": the five override fields are read as one block, so if any sibling is set, this behaves as 1 minute instead of as the integration's window.
    - `below_threshold_action` 'suppress' | 'message' | 'escalate', nullable: What happens to occurrences before the threshold. Null behaves as the integration's default action once the override block is engaged.
    - `threshold_action` 'suppress' | 'message' | 'escalate', nullable: What happens at the threshold — the field that decides whether people are woken and how loudly: `escalate` pages at `critical` and starts the escalation chain, `message` pages at `normal` and starts none, `suppress` records silently. Null behaves as the integration's default action once the override block is engaged.
    - `after_threshold_action` 'suppress' | 'message' | 'escalate', nullable: What happens to further occurrences in the same window. Null behaves as `suppress` once the override block is engaged — not as the integration default — so a rule showing four nulls and one set field is quieter after its first page than it looks.
    - `created_at` string, date-time
    - `updated_at` string, date-time

## Other responses

- `401`: API key missing, invalid, revoked or expired.
- `403`: API key lacks config.write, or the rule belongs to another company.
- `404`: No routing rule with this UUID.
- `405`: PATCH was used. This endpoint replaces the whole rule, so only PUT is accepted.
- `422`: Validation failed — unknown severity or status, a recipient group that does not exist in this company, a matcher missing `key`/`op`/`value` or using an operator outside `=`, `!=`, `=~`, `!~`, or a regex pattern that does not compile.

---

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