---
title: "Delete a recipient"
method: DELETE
path: "/api/v1/recipients/{recipientID}"
---

# Delete a recipient

`DELETE /api/v1/recipients/{recipientID}`

## Request

- Base URL: `https://api.uptimemonitoring.com`
- URL: `https://api.uptimemonitoring.com/api/v1/recipients/{recipientID}`
- Auth: HTTP bearer

Detaches the recipient from every monitor it was attached to
(cascade) and deletes it. Deletes the recipient's own
`pending`/`retrying` webhook_deliveries rows so nothing keeps
retrying to a removed destination.

**Guarded delete (issue #396).** An optional `?replaced_by=` runs
a stricter path instead: the recipient is deleted only if a
replacement recipient already covers every one of its live
(`pending`/`retrying`) deliveries. Omitting `replaced_by` keeps
the plain, unguarded behavior described above exactly.

Four checks, all evaluated in one transaction, each failing with
409 and its own named reason:

- `replacement_not_ready`: the replacement must exist in the same
  account, differ from the recipient being deleted, be active
  (not disabled; not an unconfirmed email recipient), have
  `events` that are a superset of the recipient being deleted's,
  and — when the recipient being deleted is an account default —
  itself be a default too.
- `recipient_not_active`: the recipient being deleted must itself
  be active. An inactive recipient already lost its live
  deliveries when it went inactive, so a guarded delete would
  prove nothing; use the plain `DELETE` instead.
- `attachment_missing`: every monitor the recipient being deleted
  is attached to must have the replacement attached too, with the
  same `down_delay_s`.
- `delivery_not_covered`: every live delivery of the recipient
  being deleted must have a non-null `enqueued_at` strictly after
  the replacement's `attached_at` on that monitor (see `GET
  /api/v1/monitors/{id}/recipients`), AND a same-transition
  sibling delivery for the replacement (same monitor, event type,
  and occurrence) whose status is `pending`, `retrying`, or
  `delivered`.

**What a 204 guarantees, and what it doesn't.** The checks hold
at the instant the delete commits — every removed delivery had a
replacement copy that was delivered or still live at that
instant. It does NOT mean the deleted recipient's destination
goes quiet immediately: an attempt the delivery worker already
claimed before the commit can still reach it once more, signed
with its credentials, with no delivery-log row recording it — we
recommend keeping the old destination (and, for a secret
rotation, the old signing secret) accepting requests for at least
five minutes after the 204. After the commit, the replacement is
an ordinary recipient — a later edit to it (unsubscribe, delete,
narrowed `events`, changed Slack/PagerDuty configuration) affects
its own deliveries the same as for any recipient.

**Drain recipe.** Before calling this with `replaced_by`: create
and attach the replacement everywhere the original recipient is
attached (same `events`, same `down_delay_s`, same default
status), then scan
`GET /api/v1/webhook-deliveries?recipient_id={id}&status=pending&status=retrying&order=id`
until `next_cursor` is `null` and every returned row's
`enqueued_at` is non-null and greater than the replacement's
`attached_at` on its monitor. Until the guarded delete returns
204, avoid detaching, unsubscribing, deleting (without
`replaced_by`), or otherwise editing the original recipient in
any way that removes or cancels its live deliveries — the guard
can only see rows that still exist, and any of those actions
empties the scan and lets a later guarded delete succeed with the
alert already silently lost.

## Query parameters

- `replaced_by` integer

## Response `204`

Deleted

## Other responses

- `400`: The query string was malformed, or `replaced_by` was present but empty, not an integer, or equal to the path `recipientID`.
- `404`: Recipient not found
- `409`: Guarded delete only (`replaced_by` present) — one of the four checks failed. `error` is the failed check's name: `replacement_not_ready`, `recipient_not_active`, `attachment_missing`, or `delivery_not_covered`.
- `429`: Per-account dispatcher rate limit exceeded (200 requests/minute). The `Retry-After` header gives the integer seconds until at least one token has refilled. The `X-RateLimit-*` trio reflects the bucket whose rejection produced this response — usually the global 200/min cap; on `POST /api/v1/monitors` a 429 may instead reflect the per-route 20/min creation limit.

---

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