---
title: "Update webhook"
method: PUT
path: "/v1/webhooks/settings"
tags: ["Webhooks"]
---

# Update webhook

`PUT /v1/webhooks/settings`

Update an existing webhook configuration. All fields except `webhookId` are optional; only provided fields will be updated. `webhookId` is the same name the other /v1/webhooks operations use (logs, redeliver, test); the deprecated `_id` is still accepted in its place.

When provided, `name` must be 1-50 characters, `url` must be a valid URL, and `events` must contain at least one event. Whitespace is trimmed from `url` before validation.

Webhooks are auto-disabled only once the endpoint has had no successful delivery for 3 days AND has either reached 20 consecutive terminal failures (each one an event that exhausted the full retry ladder) or been failing continuously for 3 days. The owner is emailed; re-enable it with `isActive: true`.

A restricted (zrk_) API key can only set `events` to events whose resource
group the key holds; an event outside the key's groups is rejected with 403.
It also cannot widen an existing subscription past its own groups.

`disabledResourceGroups` replaces the subscription's own denylist, which
applies to delivery regardless of which key or session created it. Send an
empty array to clear it. A restricted key's own disabled groups are unioned
into the stored value on every update, so repointing a legacy unrestricted
subscription with a restricted key also narrows it.

Timing: the new denylist applies to every event emitted after the update.
Events already queued for delivery when the update landed were filtered
against the previous denylist and can still arrive at your endpoint for up
to five minutes after they were enqueued, because the delivery worker
trusts a five-minute enqueue-time snapshot before re-checking the
subscription. Retries beyond that window, dead-letter replays, test fires,
and redeliveries are all checked against the current denylist.

`profileIds` and `accountIds` replace the subscription's allowlists; an
empty array clears one. Ids outside your team are rejected with 404
(`profile_not_found`, `account_not_found`).

## Request body

- object
  - `webhookId` string — Webhook ID to update. Required unless the deprecated `_id` is sent instead.
  - `_id` string — Alias of webhookId, kept for existing callers
  - `name` string — Webhook name (1-50 characters). Must be non-empty if provided.
  - `url` string, uri — Webhook endpoint URL (must be a valid URL, whitespace trimmed). Must be a valid URL if provided.
  - `secret` string — Secret key for HMAC-SHA256 signature verification
  - `events` string[] — Events to subscribe to. Must contain at least one event if provided.
  - `isActive` boolean — Enable or disable webhook delivery
  - `customHeaders` object — Custom headers to include in webhook requests
  - `disabledResourceGroups` string[] — Replaces the subscription's denylist. Send an empty array to clear it and receive every event in `events` again. Omitting the field leaves the current denylist untouched. Applies to events emitted after the update; already-queued events can still deliver for up to five minutes after they were enqueued. When the caller is a restricted (zrk_) key, that key's own disabled groups are unioned back in either way, so a restricted key can neither clear nor widen a subscription past its own groups.
  - `profileIds` string[] — Replaces the subscription's profile allowlist. Send an empty array to receive every profile again. Omitting the field leaves the current list untouched. Every id must be a profile in your team, otherwise the request fails with 404 `profile_not_found` and nothing changes. Applies to events emitted after the update. Sending the stored list back unchanged is accepted without re-validation, so an endpoint stays editable after a listed profile is deleted.
  - `accountIds` string[] — Replaces the subscription's account allowlist. Send an empty array to receive every account again. Omitting the field leaves the current list untouched. Every id must be an account in your team, otherwise the request fails with 404 `account_not_found` and nothing changes. Sending the stored list back unchanged is accepted without re-validation.

## Response `200`

Webhook updated successfully

- object
  - `success` boolean
  - `webhook` Webhook — Individual webhook configuration for receiving real-time notifications
    - `_id` string — Unique webhook identifier
    - `name` string — Webhook name (for identification)
    - `url` string, uri — Webhook endpoint URL
    - `secret` string — Secret key for HMAC-SHA256 signature verification.
    - `events` string[] — Events subscribed to
    - `isActive` boolean — Whether webhook delivery is enabled
    - `lastFiredAt` string, date-time — Timestamp of last successful webhook delivery
    - `failureCount` integer — Consecutive terminal delivery failures (resets to 0 on any successful delivery). Auto-disable only triggers when the endpoint has had no successful delivery within a 3-day window AND either reaches 20 consecutive terminal failures or has been failing continuously for 3 days; any success within that window keeps the endpoint enabled regardless of the count.
    - `customHeaders` object — Custom headers included in webhook requests
    - `disabledResourceGroups` string[] — Resource groups this subscription does not receive (opt-out denylist, same vocabulary and same semantics as the field on API keys). Absent or empty means the subscription receives every event listed in `events`, which is how every subscription created before this field existed behaves. An event whose group is listed here is dropped before delivery even when it is still present in `events`, and the same check runs on every replay path (test fire, redelivery, dead-letter requeue). Editing the denylist applies to every event emitted afterwards; events already queued when the edit landed can still be delivered for up to five minutes after they were enqueued.
    - `profileIds` string[] — Profiles this subscription receives events for (allowlist). Absent or empty means every profile, which is how every subscription created before this field existed behaves. A scoped subscription is only sent events attributable to a listed profile. An aggregate `post.*` event is attributed to the profile of every account the post targets, so a post spanning two scoped endpoints' profiles reaches both. Events with no profile behind them (`verification.*`, `phone_number.*`, a legacy post whose accounts were deleted) are not delivered to it. Applied when the event is emitted: a redelivery replays a delivery already made to this endpoint, and a test fire ignores the list.
    - `accountIds` string[] — Connected accounts this subscription receives events for (allowlist). Absent or empty means every account. Same semantics as `profileIds`, keyed on the account: an aggregate `post.*` event is attributed to every account the post targets. Events that name no connected account (`verification.*`, `phone_number.*`, and `whatsapp.number.*`, which carry the phone number) are not delivered to an account-scoped subscription. A subscription with both lists must be satisfied on both. Applied when the event is emitted; a redelivery replays a delivery already made to this endpoint and a test fire ignores the list.

## Other responses

- `400` — Validation error or missing webhook ID
- `401` — Unauthorized
- `403` — The API key is a restricted key (zrk_ prefix) and may not perform this operation. Three cases. (1) The operation's resource group (see the operation's x-resource-group) is disabled on the key: fix it by creating a key with the group enabled in the dashboard API keys tab and revoking the old one. (2) The operation is admin-plane (x-resource-group admin-plane: API keys, invites, connected apps, member identity), which is never grantable to restricted keys; the error reads "Restricted API keys cannot manage API keys, invites, or member identity." and the fix is a full-access key or the dashboard, never a new restricted key. (3) On webhook subscription writes, delivery-log reads and replays, a named event maps to a resource group the key does not hold, so a restricted key can never create or edit a subscription broader than itself (a no-messages key cannot subscribe to, test-fire, redeliver or read logs for message.* events).
- `404` — Webhook not found, or a profile in `profileIds` (`profile_not_found`) / an account in `accountIds` (`account_not_found`) is not in your team

## Changes

- **2026-09-25** `2c04683ce694` — 2 warning, 2 info
  - added the new `sms.registration.action_required` enum value to the `webhook/events/items/` response property for the response status `200`
  - added the new `sms.registration.status_updated` enum value to the `webhook/events/items/` response property for the response status `200`
  - added the new `sms.registration.action_required` enum value to the request property `events/items/`
  - added the new `sms.registration.status_updated` enum value to the request property `events/items/`
- **2026-09-25** `a0d8f21b5abe` — 4 info
  - added the new optional request property `accountIds`
  - added the new optional request property `profileIds`
  - added the optional property `webhook/accountIds` to the response with the `200` status
  - added the optional property `webhook/profileIds` to the response with the `200` status
- **2026-09-23** `dd3865482f9f` — 3 info
  - added the new optional request property `webhookId`
  - the request property `_id` became optional
  - request property `_id` deprecated
- **2026-09-09** `41eff0cffb2d` — 1 warning, 1 info
  - added the new `conversation.control_changed` enum value to the `webhook/events/items/` response property for the response status `200`
  - added the new `conversation.control_changed` enum value to the request property `events/items/`
- …earlier changes not shown

[Full history](https://skmtc.dev/zernio/apis/zernio-api/changes/v1/webhooks/settings/put.md)

---

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