---
title: "Update a recipient"
method: PATCH
path: "/api/v1/recipients/{recipientID}"
---

# Update a recipient

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

## Request

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

Only fields present in the body are changed. `type` is immutable:
including it at all (any value) returns 400. `name`, `default`,
`events`, and `config` each reject an explicit JSON `null` with
400 — omit the field entirely to leave it unchanged. `events`, if
present, must be a non-empty subset of
`["down","up","flapping"]`. Changing `config.address` on a
`type=email` recipient resets it to `status=pending` and sends a
new confirmation message BEST-EFFORT — a send failure here does
not fail the request (the response still reflects the committed
address change), so a caller that does not see the mail arrive
should call `POST /recipients/{recipientID}/confirm/resend` to
retry delivery rather than re-submitting the same PATCH, which
sends nothing on an unchanged address (see `RecipientPatch`). For
`type=slack` (issue #359), `config.url` and `config.channel` are
each independently optional on PATCH — at least one is required —
and `config.channel` set to `null` or `""` clears the override
back to the incoming webhook's own default. PATCHing back the
masked `config.url` from a GET/POST response is rejected with 400
(it fails the same Slack URL grammar check) and leaves the stored
URL unchanged. Any URL or channel change on a `type=slack`
recipient cancels its own still-queued, not-yet-claimed
(`pending`/`retrying`) deliveries, so a destination the caller
just replaced never receives one of those. A delivery the Worker
has already claimed for dispatch at the moment of the PATCH still
runs against the pre-edit destination: a successful attempt is
recorded as `delivered` (overwriting the cancellation), but a
failed or retryable attempt leaves the row `cancelled`, with no
attempt count, response code, or response body recorded for that
attempt. For `type=pagerduty` (issue #363), `config.integration_key`
is required when `config` is present (there is no optional
secondary field the way Slack has `channel`). PATCHing back the
masked `config.integration_key` from a GET/POST response is
rejected with 400 (it fails the same key-shape check) and leaves
the stored key unchanged. A key change on a `type=pagerduty`
recipient cancels its own still-queued, not-yet-claimed
deliveries the same way a Slack URL/channel change does — but
never resolves an incident the old key already opened in
PagerDuty; only the customer or PagerDuty's own auto-resolve can
do that. `config` applies the
same cross-type reserved-key rejection Create does (see that
operation's description).

## Request body

- RecipientPatch: Every field is optional; only fields present are changed. `type` must not be present at all. `name`/`default`/`events`/`config` each reject an explicit JSON `null`. Changing `config.address` on a `type=email` recipient resets it to `status:"pending"` (old confirm/unsubscribe links stop working, a new confirmation is sent) and records `recipient.updated` with `changes.address: {"changed": true}` — never the old or new address itself. For `type=slack` (issue #359), `config.url` and `config.channel` are each independently optional — at least one is required when `config` is present — and changing either records `changes.url: {"changed": true}` (never the URL text) and `changes.channel: {"old": ..., "new": ...}` respectively, and cancels the recipient's own still-queued deliveries. Setting `config.channel` to `null` or `""` clears the override. For `type=pagerduty` (issue #363), `config.integration_key` is required when `config` is present; changing it records `changes.integration_key: {"changed": true}` (never the key itself) and cancels the recipient's own still-queued deliveries — it does not resolve an incident the old key already opened in PagerDuty.
  - `name` string
  - `config` object
    - `url` string, uri
    - `address` string, email
    - `channel` string, nullable: `type=slack` only. `null` or `""` clears the channel override back to the incoming webhook's own default.
    - `integration_key` string: `type=pagerduty` only. Exactly 32 characters of `[A-Za-z0-9]`.
  - `default` boolean
  - `events` string[]

## Response `200`

Updated (never includes `secret`)

- Recipient: Account-level, named delivery target (issue #212) assignable to any number of monitors via `PUT /api/v1/monitors/{monitorID}/recipients`. `secret` is included ONLY on the `POST /api/v1/recipients` response for `type=webhook` — never on `GET`/`PATCH`, and never for `type=email`, `type=slack`, or `type=pagerduty`. The signed JSON delivery body for `webhook` is unchanged from the pre-#212 `Webhook` contract; see its description. `type=email` (issue #321) sends outage/recovery alert mail instead of a signed POST — see `status` below for its confirmation lifecycle. `type=slack` (issue #359) posts an unsigned Block Kit message to a Slack incoming webhook URL; `config.url` is always masked (the real token is never echoed). `type=pagerduty` (issue #363) posts to the PagerDuty Events API v2; `config.integration_key` is always masked (the real key is never echoed).
  - `id` integer
  - `name` string
  - `type` 'webhook' | 'email' | 'slack' | 'pagerduty'
  - `config` object: Channel-specific config: `url` for `type=webhook` or `type=slack`, `address` for `type=email`, `integration_key` for `type=pagerduty`, plus an optional `channel` for `type=slack`. For `type=slack`, `url` is always the masked form `https://hooks.slack.com/services/{team}/{bot}/********` (or the bare `https://hooks.slack.com/services/********` for a stored URL that doesn't match the expected shape) — the real token is never returned. For `type=pagerduty`, `integration_key` is always the masked form `"********"` plus the last 4 characters (or the bare `"********"` for a stored key that doesn't match the expected shape) — the real key is never returned.
    - `url` string, uri
    - `address` string, email: Normalized (lowercased) email address.
    - `channel` string: `type=slack` only. Optional channel/user override (e.g. `"#ops"` or `"@alice"`); omitted when unset.
    - `integration_key` string: `type=pagerduty` only. Masked — see this object's own description above.
  - `default` boolean: When true, this recipient is attached automatically to every new monitor created on this account, at create time, in the same transaction as the monitor row. A monitor born down does NOT notify a newly-attached default recipient at creation — the create response already shows the monitor's state directly, and the default's first notification is the monitor's next qualifying transition.
  - `status` 'active' | 'pending' | 'disabled': `pending`: a `type=email` recipient awaiting confirmation (no alerts sent yet). `disabled`: unsubscribed (via the mailed unsubscribe link) — alerts stopped; re-enabling requires deleting and recreating the recipient, which re-confirms from scratch. `active`: every `type=webhook`, `type=slack`, and `type=pagerduty` recipient, and a confirmed, non-disabled `type=email` recipient.
  - `events` string[]: Non-empty subset of event types this recipient is notified for.
  - `monitor_ids` integer[]: Monitors this recipient is currently attached to. `[]` for none.
  - `secret` string: HMAC-SHA256 signing secret, prefix `whs_live_`. Present ONLY on the `POST /api/v1/recipients` response for `type=webhook` (and the deprecated `GET /api/v1/monitors/{monitorID}/webhook` alias, for compatibility) — omitted everywhere else, and always omitted for `type=email`, `type=slack`, and `type=pagerduty` (neither a slack nor a pagerduty recipient has a secret at all — the incoming webhook URL or the routing key itself is the credential, and both live in `config`, masked).
  - `created_at` string, date-time
  - `updated_at` string, date-time

## Other responses

- `400`: Bad request
- `404`: Recipient not found
- `413`: Request body exceeds the 64 KB hard limit. Per docs/SPEC.md §Hard Limits the dispatcher rejects any payload larger than 65,536 bytes before it is decoded.
- `415`: The request `Content-Type` is not `application/json`. Write operations (POST, PUT, PATCH) require that header; a missing or non-JSON media type is rejected before the body is read. The `charset` parameter is accepted and ignored, so `application/json; charset=utf-8` is valid.
- `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)
