---
title: "Create webhook"
method: POST
path: "/v1/webhooks/settings"
tags: ["Webhooks"]
---

# Create webhook

`POST /v1/webhooks/settings`

Create a new webhook configuration. Maximum 50 webhooks per user.

`name`, `url` and `events` are required. `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 subscribe to events whose resource group
the key holds; an event outside the key's groups is rejected with 403, so a
restricted key can never create a subscription broader than itself.

`disabledResourceGroups` restricts the subscription itself, independently of
which key or session later reads it. Events in a disabled group are dropped
before delivery to this endpoint, on live delivery and on every replay path
(test fire, redelivery, dead-letter requeue), even if they are listed in
`events`. Omit it to receive everything in `events`, which is how existing
subscriptions behave. A restricted key's own disabled groups are always
unioned in.

`profileIds` pins the subscription to a set of profiles and `accountIds`
to a set of connected accounts: only events attributable to one of the
listed ids are delivered, and a subscription with both lists must match
on both. Use them to send test accounts to a staging endpoint. Ids
outside your team are rejected with 404 (`profile_not_found`,
`account_not_found`).

## Request body

- object
  - `name` string, required — Webhook name (1-50 characters)
  - `url` string, uri, required — Webhook endpoint URL (must be a valid URL, whitespace trimmed)
  - `secret` string — Secret key for HMAC-SHA256 signature verification
  - `events` string[], required — Events to subscribe to (at least one required)
  - `isActive` boolean — Enable or disable webhook delivery. Defaults to `true` when omitted.
  - `customHeaders` object — Custom headers to include in webhook requests
  - `disabledResourceGroups` string[] — Resource groups this subscription does not receive (opt-out denylist). Omit or send an empty array to receive every event in `events`. Listing a group here drops its events before delivery and on every replay path. Set at creation it applies to everything this subscription ever receives; changed later via PUT it applies to events emitted after the change, with a five-minute tail for events already queued (see that operation). When the caller is a restricted (zrk_) key, that key's own disabled groups are unioned into whatever you send here, so a restricted key can never create a subscription wider than itself.
  - `profileIds` string[] — Profiles this subscription receives events for. Omit or send an empty array to receive every profile. Every id must be a profile in your team, otherwise the request fails with 404 `profile_not_found` and nothing is created. Typical use is routing the profile that holds test accounts to a staging endpoint.
  - `accountIds` string[] — Connected accounts this subscription receives events for. Omit or send an empty array to receive every account. Every id must be an account in your team, otherwise the request fails with 404 `account_not_found` and nothing is created. Combine with `profileIds` to narrow further; both must match.

## Response `200`

Webhook created 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 maximum webhooks reached
- `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` — A profile in `profileIds` (`profile_not_found`) or an account in `accountIds` (`account_not_found`) is not in your team

## Changes

- **2026-09-25** `a0d8f21b5abe` — 5 info
  - added the new optional request property `accountIds`
  - added the new optional request property `profileIds`
  - added the non-success response with the status `404`
  - added the optional property `webhook/accountIds` to the response with the `200` status
  - …1 more
- **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/`
- **2026-09-02** `3f632ccead88` — 1 warning, 1 info
  - added the new `analytics.synced` enum value to the `webhook/events/items/` response property for the response status `200`
  - added the new `analytics.synced` enum value to the request property `events/items/`
- **2026-08-29** `e6f7a453bfb1` — 1 warning, 1 info
  - added the new `phone_number.stock_available` enum value to the `webhook/events/items/` response property for the response status `200`
  - added the new `phone_number.stock_available` enum value to the request property `events/items/`
- **2026-08-25** `44b6b1ca2a8b` — 1 warning, 1 info
  - added the new `whatsapp.account.name_status_updated` enum value to the `webhook/events/items/` response property for the response status `200`
  - added the new `whatsapp.account.name_status_updated` enum value to the request property `events/items/`

[Full history](https://skmtc.dev/zernio/apis/zernio-api/changes/v1/webhooks/settings/post.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/a0d8f21b5abe?raw)
