# Webhook endpoints tools

> MCP tools for Webhook endpoints: webhook_endpoints_create, webhook_endpoints_delete, webhook_endpoints_get, webhook_endpoints_list, webhook_endpoints_rotate_secret, webhook_endpoints_test, webhook_endpoints_update.

- `webhook_endpoints_create`: Create a webhook endpoint
- `webhook_endpoints_delete`: Delete a webhook endpoint
- `webhook_endpoints_get`: Get a webhook endpoint
- `webhook_endpoints_list`: List webhook endpoints
- `webhook_endpoints_rotate_secret`: Rotate a webhook endpoint's signing secret
- `webhook_endpoints_test`: Send a test event to a webhook endpoint
- `webhook_endpoints_update`: Update a webhook endpoint

## `webhook_endpoints_create`

**Create a webhook endpoint.** Operation [`webhook_endpoints.create`](https://ownrecord.io/docs/reference/webhook-endpoints-create.md) · Brand level · permission `webhooks:admin` · risk tier `security` · since M4

Creates an endpoint that receives the brand's events in the request's mode: Ownrecord POSTs each event it
subscribes to as JSON, signed with the `Ownrecord-Signature` header (see the webhooks concept page).

- `events` lists event types from the catalogue that are delivered to webhooks, or `["*"]` for all of them,
  including types added later.
- `url` must be a public `https` URL. In development and test (dev gate open), `http://localhost` works too.
- The response contains the signing secret in `secret`. **It is shown only once**: it is stored encrypted and
  can't be read again. Rotate it with `webhook_endpoints.rotate_secret` if you lose it.

A brand can have at most 16 endpoints per mode. Send a test event with `webhook_endpoints.test`.

Annotations: `readOnlyHint: false` · `destructiveHint: false` · `idempotentHint: false` · `openWorldHint: false`

### Arguments

| Argument          | Type            | Required | Description                                                                                                                                                                                              |
| ----------------- | --------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `url`             | string          | yes      | The https URL Ownrecord POSTs events to, e.g. <https://example.com/webhooks/ownrecord>. (1–2048 characters)                                                                                              |
| `events`          | array of string | yes      | Event types to receive (from the event catalogue), or \["\*"] for every event of the brand. (1–100 items)                                                                                                |
| `description`     | string          | no       | (at most 500 characters)                                                                                                                                                                                 |
| `enabled`         | boolean         | no       | Start receiving events at once (default) or create it disabled. (default `true`)                                                                                                                         |
| `metadata`        | object          | no       | Your own key-value pairs (at most 50 keys). On updates the whole set is replaced.                                                                                                                        |
| `entity_id`       | ID (`ent_…`)    | no       | Optional: the brand's legal entity (ent\_…); `brand_id` alone is enough.                                                                                                                                 |
| `brand_id`        | ID (`brd_…`)    | no       | The brand to act on (brd\_…), inside your API key's reach. Needed with an organization or entity key; a brand key acts on its own brand.                                                                 |
| `idempotency_key` | string          | no       | A unique string for this change. Retrying with the same key and arguments never acts twice. When left out, the server generates one and returns it in the result's `idempotency_key`. (1–255 characters) |
| `dry_run`         | boolean         | no       | true: check permissions and rules and compute the result, then roll everything back. Nothing is saved. (default `false`)                                                                                 |

- **Brand:** pass `brand_id` with an organization or entity key; a brand key acts on its own brand.
- **Idempotency:** the result returns the `idempotency_key` used, yours or one the server generated. Retry with it: a call with the same key and arguments never acts twice (keys are kept for 24 hours).
- **Dry run:** supported. `dry_run: true` checks permissions and rules and computes the result, then rolls everything back.

### Errors

- [`validation_failed`](https://ownrecord.io/docs/reference/errors.md#validation_failed): A field is missing or malformed.
- [`webhook_url_not_allowed`](https://ownrecord.io/docs/reference/errors.md#webhook_url_not_allowed): The URL isn't https, or points at localhost, a private network or another refused address.
- [`webhook_event_type_invalid`](https://ownrecord.io/docs/reference/errors.md#webhook_event_type_invalid): An event type isn't in the catalogue or isn't delivered to brand webhook endpoints.
- [`webhook_endpoint_limit_reached`](https://ownrecord.io/docs/reference/errors.md#webhook_endpoint_limit_reached): The brand already has 16 endpoints in this mode.
- [`permission_denied`](https://ownrecord.io/docs/reference/errors.md#permission_denied): You can't manage this brand's webhooks.
- [`livemode_not_allowed`](https://ownrecord.io/docs/reference/errors.md#livemode_not_allowed): You have no live-mode access, so you can't manage live webhooks.

### Examples

**An endpoint for order fulfilment**

Arguments:

```json
{
  "url": "https://example.com/webhooks/ownrecord",
  "events": ["customer.created", "checkout.session.completed"],
  "description": "Order fulfilment",
  "brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz",
  "idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
```

Result `output`:

```json
{
  "id": "we_01m4tx5nj8emwbyf7y9vcf47z0",
  "object": "webhook_endpoint",
  "brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
  "livemode": false,
  "url": "https://example.com/webhooks/ownrecord",
  "description": "Order fulfilment",
  "events": ["customer.created", "checkout.session.completed"],
  "enabled": true,
  "disabledReason": null,
  "disabledAt": null,
  "failingSince": null,
  "secretRotatedAt": null,
  "previousSecretExpiresAt": null,
  "metadata": {},
  "createdAt": "2026-10-13T12:00:00.000Z",
  "updatedAt": "2026-10-13T12:00:00.000Z",
  "secret": "whsec_ExampleOnlyNotARealSigningSecret0000000"
}
```

**Every event of the brand**

Arguments:

```json
{
  "url": "https://example.com/webhooks/all",
  "events": ["*"],
  "brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz",
  "idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
```

Result `output`:

```json
{
  "id": "we_01m4tx5nj8emwbyf7y9vcf47z0",
  "object": "webhook_endpoint",
  "brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
  "livemode": false,
  "url": "https://example.com/webhooks/all",
  "description": null,
  "events": ["*"],
  "enabled": true,
  "disabledReason": null,
  "disabledAt": null,
  "failingSince": null,
  "secretRotatedAt": null,
  "previousSecretExpiresAt": null,
  "metadata": {},
  "createdAt": "2026-10-13T12:00:00.000Z",
  "updatedAt": "2026-10-13T12:00:00.000Z",
  "secret": "whsec_ExampleOnlyNotARealSigningSecret0000000"
}
```

## `webhook_endpoints_delete`

**Delete a webhook endpoint.** Operation [`webhook_endpoints.delete`](https://ownrecord.io/docs/reference/webhook-endpoints-delete.md) · Brand level · permission `webhooks:admin` · risk tier `security` · since M4

Deletes an endpoint and its signing secret for good. Nothing more is sent to it: deliveries waiting for a
retry are canceled instead of sent. Its delivery log stays readable with `webhook_deliveries.list`. To pause
an endpoint instead, set `enabled: false` with `webhook_endpoints.update`.

Annotations: `readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: true` · `openWorldHint: false`

### Arguments

| Argument          | Type         | Required | Description                                                                                                                                                                                              |
| ----------------- | ------------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`              | ID (`we_…`)  | yes      | –                                                                                                                                                                                                        |
| `entity_id`       | ID (`ent_…`) | no       | Optional: the brand's legal entity (ent\_…); `brand_id` alone is enough.                                                                                                                                 |
| `brand_id`        | ID (`brd_…`) | no       | The brand to act on (brd\_…), inside your API key's reach. Needed with an organization or entity key; a brand key acts on its own brand.                                                                 |
| `idempotency_key` | string       | no       | A unique string for this change. Retrying with the same key and arguments never acts twice. When left out, the server generates one and returns it in the result's `idempotency_key`. (1–255 characters) |
| `dry_run`         | boolean      | no       | true: check permissions and rules and compute the result, then roll everything back. Nothing is saved. (default `false`)                                                                                 |

- **Brand:** pass `brand_id` with an organization or entity key; a brand key acts on its own brand.
- **Idempotency:** the result returns the `idempotency_key` used, yours or one the server generated. Retry with it: a call with the same key and arguments never acts twice (keys are kept for 24 hours).
- **Dry run:** supported. `dry_run: true` checks permissions and rules and computes the result, then rolls everything back.

### Errors

- [`resource_not_found`](https://ownrecord.io/docs/reference/errors.md#resource_not_found): No endpoint with this ID is visible to the request.
- [`version_conflict`](https://ownrecord.io/docs/reference/errors.md#version_conflict): The endpoint changed at the same time; retry.
- [`permission_denied`](https://ownrecord.io/docs/reference/errors.md#permission_denied): You can't manage this brand's webhooks.

### Example

**Delete an endpoint**

Arguments:

```json
{
  "id": "we_01m4tx5nj8emwbyf7y9vcf47z0",
  "brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz",
  "idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
```

Result `output`:

```json
{
  "id": "we_01m4tx5nj8emwbyf7y9vcf47z0",
  "object": "webhook_endpoint",
  "deleted": true
}
```

## `webhook_endpoints_get`

**Get a webhook endpoint.** Operation [`webhook_endpoints.get`](https://ownrecord.io/docs/reference/webhook-endpoints-get.md) · Brand level · permission `webhooks:read` · risk tier `read` · since M4

Returns one webhook endpoint of the brand in the request's mode: its URL, event types, whether it is enabled (and why not), and since when its deliveries have been failing. Never its signing secret.

Annotations: `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Arguments

| Argument    | Type         | Required | Description                                                                                                                              |
| ----------- | ------------ | -------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `id`        | ID (`we_…`)  | yes      | –                                                                                                                                        |
| `entity_id` | ID (`ent_…`) | no       | Optional: the brand's legal entity (ent\_…); `brand_id` alone is enough.                                                                 |
| `brand_id`  | ID (`brd_…`) | no       | The brand to act on (brd\_…), inside your API key's reach. Needed with an organization or entity key; a brand key acts on its own brand. |

- **Brand:** pass `brand_id` with an organization or entity key; a brand key acts on its own brand.
- **Read-only:** changes nothing; `idempotency_key` is ignored.

### Errors

- [`resource_not_found`](https://ownrecord.io/docs/reference/errors.md#resource_not_found): No endpoint with this ID is visible to the request.
- [`permission_denied`](https://ownrecord.io/docs/reference/errors.md#permission_denied): You can't see this brand's webhooks.

### Example

**An endpoint**

Arguments:

```json
{
  "id": "we_01m4tx5nj8emwbyf7y9vcf47z0",
  "brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz"
}
```

Result `output`:

```json
{
  "id": "we_01m4tx5nj8emwbyf7y9vcf47z0",
  "object": "webhook_endpoint",
  "brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
  "livemode": false,
  "url": "https://example.com/webhooks/ownrecord",
  "description": "Order fulfilment",
  "events": ["customer.created", "checkout.session.completed"],
  "enabled": true,
  "disabledReason": null,
  "disabledAt": null,
  "failingSince": null,
  "secretRotatedAt": null,
  "previousSecretExpiresAt": null,
  "metadata": {},
  "createdAt": "2026-10-13T12:00:00.000Z",
  "updatedAt": "2026-10-13T12:00:00.000Z"
}
```

## `webhook_endpoints_list`

**List webhook endpoints.** Operation [`webhook_endpoints.list`](https://ownrecord.io/docs/reference/webhook-endpoints-list.md) · Brand level · permission `webhooks:read` · risk tier `read` · since M4

Lists the brand's webhook endpoints in the request's mode, newest first. Filter with `enabled` (`true` or
`false`) to find endpoints that were disabled, by you or after failing for 3 days.

Paginate with `limit` (1–100, default 10) and `starting_after` (the `next_cursor` of the previous page).

Annotations: `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Arguments

| Argument         | Type         | Required | Description                                                                                                                              |
| ---------------- | ------------ | -------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `enabled`        | string       | no       | Only enabled (`true`) or disabled (`false`) endpoints.                                                                                   |
| `limit`          | integer      | no       | (1–100)                                                                                                                                  |
| `starting_after` | ID (`we_…`)  | no       | –                                                                                                                                        |
| `entity_id`      | ID (`ent_…`) | no       | Optional: the brand's legal entity (ent\_…); `brand_id` alone is enough.                                                                 |
| `brand_id`       | ID (`brd_…`) | no       | The brand to act on (brd\_…), inside your API key's reach. Needed with an organization or entity key; a brand key acts on its own brand. |

- **Brand:** pass `brand_id` with an organization or entity key; a brand key acts on its own brand.
- **Read-only:** changes nothing; `idempotency_key` is ignored.

### Errors

- [`validation_failed`](https://ownrecord.io/docs/reference/errors.md#validation_failed): A filter or pagination value is malformed.
- [`permission_denied`](https://ownrecord.io/docs/reference/errors.md#permission_denied): You can't see this brand's webhooks.

### Examples

**The brand's endpoints**

Arguments:

```json
{
  "brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz"
}
```

Result `output`:

```json
{
  "object": "list",
  "data": [
    {
      "id": "we_01m4tx5nj8emwbyf7y9vcf47z0",
      "object": "webhook_endpoint",
      "brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
      "livemode": false,
      "url": "https://example.com/webhooks/ownrecord",
      "description": "Order fulfilment",
      "events": ["customer.created", "checkout.session.completed"],
      "enabled": true,
      "disabledReason": null,
      "disabledAt": null,
      "failingSince": null,
      "secretRotatedAt": null,
      "previousSecretExpiresAt": null,
      "metadata": {},
      "createdAt": "2026-10-13T12:00:00.000Z",
      "updatedAt": "2026-10-13T12:00:00.000Z"
    }
  ],
  "has_more": false,
  "next_cursor": null
}
```

**Endpoints that were disabled**

Arguments:

```json
{
  "enabled": "false",
  "brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz"
}
```

Result `output`:

```json
{
  "object": "list",
  "data": [
    {
      "id": "we_01m4tx5nj8emwbyf7y9vcf47z1",
      "object": "webhook_endpoint",
      "brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
      "livemode": false,
      "url": "https://example.com/webhooks/ownrecord",
      "description": "Order fulfilment",
      "events": ["customer.created", "checkout.session.completed"],
      "enabled": false,
      "disabledReason": "failing",
      "disabledAt": "2026-10-14T09:30:00.000Z",
      "failingSince": "2026-10-11T09:30:00.000Z",
      "secretRotatedAt": null,
      "previousSecretExpiresAt": null,
      "metadata": {},
      "createdAt": "2026-10-13T12:00:00.000Z",
      "updatedAt": "2026-10-14T09:30:00.000Z"
    }
  ],
  "has_more": false,
  "next_cursor": null
}
```

## `webhook_endpoints_rotate_secret`

**Rotate a webhook signing secret.** Operation [`webhook_endpoints.rotate_secret`](https://ownrecord.io/docs/reference/webhook-endpoints-rotate-secret.md) · Brand level · permission `webhooks:admin` · risk tier `security` · since M4

Replaces the endpoint's signing secret and returns the new one in `secret`. **It is shown only once.**

- Without `overlapSeconds` (or with 0) the old secret stops at once: deliveries are signed with the new one
  only, and receivers still checking the old one fail until they switch (failed deliveries are retried).
- With `overlapSeconds` (up to 86400) the old secret keeps signing next to the new one until
  `previousSecretExpiresAt`: each delivery carries two `v1=` signatures, and a receiver accepts it when either
  matches. Deploy the new secret, then let the overlap run out.

Rotating again ends an earlier overlap at once.

Annotations: `readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: false` · `openWorldHint: false`

### Arguments

| Argument          | Type         | Required | Description                                                                                                                                                                                              |
| ----------------- | ------------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`              | ID (`we_…`)  | yes      | –                                                                                                                                                                                                        |
| `overlapSeconds`  | integer      | no       | Seconds the old secret keeps signing next to the new one (0–86400, default 0: it stops at once). During the overlap each delivery carries two v1 signatures. (0–86400) (default `0`)                     |
| `entity_id`       | ID (`ent_…`) | no       | Optional: the brand's legal entity (ent\_…); `brand_id` alone is enough.                                                                                                                                 |
| `brand_id`        | ID (`brd_…`) | no       | The brand to act on (brd\_…), inside your API key's reach. Needed with an organization or entity key; a brand key acts on its own brand.                                                                 |
| `idempotency_key` | string       | no       | A unique string for this change. Retrying with the same key and arguments never acts twice. When left out, the server generates one and returns it in the result's `idempotency_key`. (1–255 characters) |
| `dry_run`         | boolean      | no       | true: check permissions and rules and compute the result, then roll everything back. Nothing is saved. (default `false`)                                                                                 |

- **Brand:** pass `brand_id` with an organization or entity key; a brand key acts on its own brand.
- **Idempotency:** the result returns the `idempotency_key` used, yours or one the server generated. Retry with it: a call with the same key and arguments never acts twice (keys are kept for 24 hours).
- **Dry run:** supported. `dry_run: true` checks permissions and rules and computes the result, then rolls everything back.

### Errors

- [`validation_failed`](https://ownrecord.io/docs/reference/errors.md#validation_failed): overlapSeconds isn't a whole number from 0 to 86400.
- [`resource_not_found`](https://ownrecord.io/docs/reference/errors.md#resource_not_found): No endpoint with this ID is visible to the request.
- [`version_conflict`](https://ownrecord.io/docs/reference/errors.md#version_conflict): The endpoint changed at the same time; retry.
- [`permission_denied`](https://ownrecord.io/docs/reference/errors.md#permission_denied): You can't manage this brand's webhooks.

### Example

**Rotate with a one-hour overlap**

Arguments:

```json
{
  "id": "we_01m4tx5nj8emwbyf7y9vcf47z0",
  "overlapSeconds": 3600,
  "brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz",
  "idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
```

Result `output`:

```json
{
  "id": "we_01m4tx5nj8emwbyf7y9vcf47z0",
  "object": "webhook_endpoint",
  "brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
  "livemode": false,
  "url": "https://example.com/webhooks/ownrecord",
  "description": "Order fulfilment",
  "events": ["customer.created", "checkout.session.completed"],
  "enabled": true,
  "disabledReason": null,
  "disabledAt": null,
  "failingSince": null,
  "secretRotatedAt": "2026-10-14T09:30:00.000Z",
  "previousSecretExpiresAt": "2026-10-14T10:30:00.000Z",
  "metadata": {},
  "createdAt": "2026-10-13T12:00:00.000Z",
  "updatedAt": "2026-10-14T09:30:00.000Z",
  "secret": "whsec_ExampleOnlyNotARealSigningSecret0000000"
}
```

## `webhook_endpoints_test`

**Send a test webhook.** Operation [`webhook_endpoints.test`](https://ownrecord.io/docs/reference/webhook-endpoints-test.md) · Brand level · permission `webhooks:write` · risk tier `write` · since M4

Sends a `webhook_endpoint.test` event to this endpoint only, signed like every delivery, so you can check that
your receiver verifies `Ownrecord-Signature` and answers 2xx. It works for enabled and disabled endpoints.

The call returns the queued delivery at once; it is attempted right after (one attempt, no retries). Read the
outcome with `webhook_deliveries.get`: `status`, `responseCode` and the start of your response body. The test
event isn't part of the event feed, and endpoints can't subscribe to it.

Annotations: `readOnlyHint: false` · `destructiveHint: false` · `idempotentHint: false` · `openWorldHint: true`

### Arguments

| Argument          | Type         | Required | Description                                                                                                                                                                                              |
| ----------------- | ------------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`              | ID (`we_…`)  | yes      | –                                                                                                                                                                                                        |
| `entity_id`       | ID (`ent_…`) | no       | Optional: the brand's legal entity (ent\_…); `brand_id` alone is enough.                                                                                                                                 |
| `brand_id`        | ID (`brd_…`) | no       | The brand to act on (brd\_…), inside your API key's reach. Needed with an organization or entity key; a brand key acts on its own brand.                                                                 |
| `idempotency_key` | string       | no       | A unique string for this change. Retrying with the same key and arguments never acts twice. When left out, the server generates one and returns it in the result's `idempotency_key`. (1–255 characters) |
| `dry_run`         | boolean      | no       | true: check permissions and rules and compute the result, then roll everything back. Nothing is saved. (default `false`)                                                                                 |

- **Brand:** pass `brand_id` with an organization or entity key; a brand key acts on its own brand.
- **Idempotency:** the result returns the `idempotency_key` used, yours or one the server generated. Retry with it: a call with the same key and arguments never acts twice (keys are kept for 24 hours).
- **Dry run:** supported. `dry_run: true` checks permissions and rules and computes the result, then rolls everything back.

### Errors

- [`resource_not_found`](https://ownrecord.io/docs/reference/errors.md#resource_not_found): No endpoint with this ID is visible to the request.
- [`permission_denied`](https://ownrecord.io/docs/reference/errors.md#permission_denied): You can't send test webhooks for this brand.

### Example

**Send a test event**

Arguments:

```json
{
  "id": "we_01m4tx5nj8emwbyf7y9vcf47z0",
  "brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz",
  "idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
```

Result `output`:

```json
{
  "id": "whd_01m4tx5nj8emwbyf7y9vcf47z3",
  "object": "webhook_delivery",
  "endpointId": "we_01m4tx5nj8emwbyf7y9vcf47z0",
  "brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
  "livemode": false,
  "eventId": "evt_01m4tx5nj8emwbyf7y9vcf47z5",
  "eventType": "webhook_endpoint.test",
  "trigger": "test",
  "redeliveryOf": null,
  "status": "pending",
  "attempt": 0,
  "maxAttempts": 1,
  "nextAttemptAt": "2026-10-14T10:30:00.000Z",
  "responseCode": null,
  "canceledReason": null,
  "attempts": [],
  "createdAt": "2026-10-14T10:30:00.000Z",
  "updatedAt": "2026-10-14T10:30:00.000Z"
}
```

## `webhook_endpoints_update`

**Update a webhook endpoint.** Operation [`webhook_endpoints.update`](https://ownrecord.io/docs/reference/webhook-endpoints-update.md) · Brand level · permission `webhooks:admin` · risk tier `security` · since M4

Changes an endpoint's URL, event types, description or metadata, or disables or enables it. Send only the
fields to change; `metadata` replaces the whole set.

- `enabled: false` stops deliveries: event deliveries waiting for a retry are canceled instead of sent.
- `enabled: true` resumes deliveries of new events, also after Ownrecord disabled the endpoint for failing for
  3 days. Events that happened while it was disabled aren't sent again on their own: redeliver them with
  `webhook_deliveries.redeliver`.

The signing secret doesn't change (use `webhook_endpoints.rotate_secret`).

Annotations: `readOnlyHint: false` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Arguments

| Argument          | Type            | Required | Description                                                                                                                                                                                              |
| ----------------- | --------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`              | ID (`we_…`)     | yes      | –                                                                                                                                                                                                        |
| `url`             | string          | no       | The https URL Ownrecord POSTs events to, e.g. <https://example.com/webhooks/ownrecord>. (1–2048 characters)                                                                                              |
| `events`          | array of string | no       | Event types to receive (from the event catalogue), or \["\*"] for every event of the brand. (1–100 items)                                                                                                |
| `description`     | string or null  | no       | A new description, or null to remove it.                                                                                                                                                                 |
| `enabled`         | boolean         | no       | false stops deliveries (waiting retries are canceled); true resumes them.                                                                                                                                |
| `metadata`        | object          | no       | Your own key-value pairs (at most 50 keys). On updates the whole set is replaced.                                                                                                                        |
| `entity_id`       | ID (`ent_…`)    | no       | Optional: the brand's legal entity (ent\_…); `brand_id` alone is enough.                                                                                                                                 |
| `brand_id`        | ID (`brd_…`)    | no       | The brand to act on (brd\_…), inside your API key's reach. Needed with an organization or entity key; a brand key acts on its own brand.                                                                 |
| `idempotency_key` | string          | no       | A unique string for this change. Retrying with the same key and arguments never acts twice. When left out, the server generates one and returns it in the result's `idempotency_key`. (1–255 characters) |
| `dry_run`         | boolean         | no       | true: check permissions and rules and compute the result, then roll everything back. Nothing is saved. (default `false`)                                                                                 |

- **Brand:** pass `brand_id` with an organization or entity key; a brand key acts on its own brand.
- **Idempotency:** the result returns the `idempotency_key` used, yours or one the server generated. Retry with it: a call with the same key and arguments never acts twice (keys are kept for 24 hours).
- **Dry run:** supported. `dry_run: true` checks permissions and rules and computes the result, then rolls everything back.

### Errors

- [`validation_failed`](https://ownrecord.io/docs/reference/errors.md#validation_failed): No field to change was sent, or a field is malformed.
- [`webhook_url_not_allowed`](https://ownrecord.io/docs/reference/errors.md#webhook_url_not_allowed): The new URL isn't allowed.
- [`webhook_event_type_invalid`](https://ownrecord.io/docs/reference/errors.md#webhook_event_type_invalid): An event type can't be delivered to brand endpoints.
- [`resource_not_found`](https://ownrecord.io/docs/reference/errors.md#resource_not_found): No endpoint with this ID is visible to the request.
- [`version_conflict`](https://ownrecord.io/docs/reference/errors.md#version_conflict): The endpoint changed at the same time; retry.
- [`permission_denied`](https://ownrecord.io/docs/reference/errors.md#permission_denied): You can't manage this brand's webhooks.

### Examples

**Subscribe to more event types**

Arguments:

```json
{
  "id": "we_01m4tx5nj8emwbyf7y9vcf47z0",
  "events": ["customer.created", "checkout.session.completed", "invoice.paid"],
  "brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz",
  "idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
```

Result `output`:

```json
{
  "id": "we_01m4tx5nj8emwbyf7y9vcf47z0",
  "object": "webhook_endpoint",
  "brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
  "livemode": false,
  "url": "https://example.com/webhooks/ownrecord",
  "description": "Order fulfilment",
  "events": ["checkout.session.completed", "customer.created", "invoice.paid"],
  "enabled": true,
  "disabledReason": null,
  "disabledAt": null,
  "failingSince": null,
  "secretRotatedAt": null,
  "previousSecretExpiresAt": null,
  "metadata": {},
  "createdAt": "2026-10-13T12:00:00.000Z",
  "updatedAt": "2026-10-14T09:30:00.000Z"
}
```

**Enable an endpoint again after it was disabled for failing**

Arguments:

```json
{
  "id": "we_01m4tx5nj8emwbyf7y9vcf47z0",
  "enabled": true,
  "brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz",
  "idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
```

Result `output`:

```json
{
  "id": "we_01m4tx5nj8emwbyf7y9vcf47z0",
  "object": "webhook_endpoint",
  "brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
  "livemode": false,
  "url": "https://example.com/webhooks/ownrecord",
  "description": "Order fulfilment",
  "events": ["customer.created", "checkout.session.completed"],
  "enabled": true,
  "disabledReason": null,
  "disabledAt": null,
  "failingSince": null,
  "secretRotatedAt": null,
  "previousSecretExpiresAt": null,
  "metadata": {},
  "createdAt": "2026-10-13T12:00:00.000Z",
  "updatedAt": "2026-10-14T09:30:00.000Z"
}
```
