# client.webhook_endpoints

> The webhook_endpoints methods of the morevoice Python SDK: URLs that receive signed events.

URLs that receive signed events. These methods are on `client.webhook_endpoints`, where `client` is a `MoreVoice` client (see [the Python SDK](https://docs.morevoice.ai/sdk/python/#connect)). On `AsyncMoreVoice` the same methods are awaited. Each one returns the response object and raises an exception when the API answers with an error.

## `create()`

**Create a webhook endpoint.** Starts sending the selected events to `url`, signed with a new secret (`whsec_…`, Standard Webhooks). The response carries the secret: this is the only time it is shown (rotate it to get a new one). With `signing: ed25519` the events are signed with a new Ed25519 key pair instead (`v1a`): no secret is returned, and you verify with `public_key`. Live endpoints need https; private and local addresses are refused.

```python
# client.webhook_endpoints
def create(self, body: _m.WebhookEndpointCreateParamsInput | Mapping[str, Any], *, more_voice_version: str | Unset = UNSET, idempotency_key: str | Unset = UNSET) -> _m.WebhookEndpoint
```

`client.webhook_endpoints.create()` · `await async_client.webhook_endpoints.create()` · `POST /webhook_endpoints` · [API reference](https://docs.morevoice.ai/api/operations/webhook_endpoints_create/)

### Request body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `url` | `string` | yes | An https URL (http is accepted in test mode). Private and local addresses are refused. |
| `enabled_events` | `string[]` | yes | An event type (`call.ended`), a group (`call.*`) or `"*"` for every event. At least one. |
| `api_version` | `"2026-11-01"` | no | Render payloads in this API version (default: the current one). |
| `description` | `string` | no | — |
| `max_in_flight` | `integer` | no | Deliveries in flight at once (1–100, default 10). |
| `metadata` | `MetadataInput` | no | Up to 50 key/value pairs (keys up to 40 characters, values up to 500) you attach to an object. Returned as sent. |
| `signing` | `"hmac" \| "ed25519"` | no | How deliveries are signed (Standard Webhooks). `hmac` (default): `v1,<HMAC-SHA256>` with a shared secret (`whsec_…`) returned once. `ed25519`: `v1a,<Ed25519 signature>`; you verify with the endpoint's `public_key` (`whpk_…`) and no secret is ever shared. |

### Returns

`WebhookEndpoint`:

| Field | Type | Description |
| --- | --- | --- |
| `id` | `string` | A webhook endpoint ID (prefix `we_`). |
| `object` | `"webhook_endpoint"` | Always `webhook_endpoint`. |
| `url` | `string` | Where events are POSTed. |
| `description` | `string` | — |
| `enabled_events` | `string[]` | The event types sent to this endpoint; `["*"]` means every event. |
| `status` | `"enabled" \| "disabled" \| "auto_disabled"` | `auto_disabled`: every delivery failed for 72 hours, so sending stopped (and your admins were told). Set `status: enabled` to resume. |
| `disabled_reason` | `string \| null` | — |
| `disabled_at` | `string \| null` | — |
| `api_version` | `string` | The API version event payloads are rendered in. `legacy`: an endpoint moved from the old webhook settings, which keeps getting the previous body (`{id, event, createdAt, data}`). |
| `campaign_id` | `string \| null` | `cmp_…` ID. |
| `legacy_headers` | `boolean` | Also sends the previous `X-Webhook-*` headers (moved endpoints, for one deprecation cycle). |
| `max_in_flight` | `integer` | Deliveries in flight to this endpoint at once; more wait their turn. |
| `metadata` | `Metadata` | Up to 50 key/value pairs (keys up to 40 characters, values up to 500) you attach to an object. Returned as sent. |
| `previous_secret_expires_at` | `string \| null` | While a rotated secret still signs (alongside the new one): until when. |
| `signing` | `"hmac" \| "ed25519"` | How deliveries are signed (Standard Webhooks). `hmac` (default): `v1,<HMAC-SHA256>` with a shared secret (`whsec_…`) returned once. `ed25519`: `v1a,<Ed25519 signature>`; you verify with the endpoint's `public_key` (`whpk_…`) and no secret is ever shared. |
| `public_key` | `string \| null` | `ed25519` endpoints: the Ed25519 public key (`whpk_` + base64 of 32 bytes) that verifies `v1a` signatures; also as PEM at `GET /v1/webhook_endpoints/{id}/public_key`. Null for `hmac`. |
| `previous_public_key` | `string \| null` | After a rotation of an `ed25519` key: the previous public key, which still signs alongside until `previous_secret_expires_at`. |
| `livemode` | `boolean` | `true` in live mode, `false` in test mode. |
| `created` | `string` | An ISO-8601 timestamp in UTC. |
| `updated` | `string` | An ISO-8601 timestamp in UTC. |
| `secret` | `string` | The signing secret (`whsec_…`) of an `hmac` endpoint. Returned only when the endpoint is created and when its secret is rotated: store it then. `ed25519` endpoints never return one. |

### Example

```python
from morevoice import MoreVoice

client = MoreVoice()  # MOREVOICE_API_KEY from the environment

webhook_endpoint = client.webhook_endpoints.create({
    "description": "CRM sync",
    "enabled_events": [
        "call.ended",
        "call.analyzed",
    ],
    "url": "https://hooks.example.com/webhooks",
})
print(webhook_endpoint)
```

## `delete()`

**Delete a webhook endpoint.** Stops all deliveries to the endpoint at once; its queued deliveries are dropped.

```python
# client.webhook_endpoints
def delete(self, id: str, *, more_voice_version: str | Unset = UNSET, idempotency_key: str | Unset = UNSET) -> _m.DeletedWebhookEndpoint
```

`client.webhook_endpoints.delete()` · `await async_client.webhook_endpoints.delete()` · `DELETE /webhook_endpoints/{id}` · [API reference](https://docs.morevoice.ai/api/operations/webhook_endpoints_delete/)

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | `str` | yes | A webhook endpoint ID (`we_…`). |

### Returns

`DeletedWebhookEndpoint`:

| Field | Type | Description |
| --- | --- | --- |
| `id` | `string` | A webhook endpoint ID (prefix `we_`). |
| `object` | `"webhook_endpoint"` | Always `webhook_endpoint`. |
| `deleted` | `true` | — |

### Example

```python
from morevoice import MoreVoice

client = MoreVoice()  # MOREVOICE_API_KEY from the environment

deleted_webhook_endpoint = client.webhook_endpoints.delete("we_7Hk2Lm9Qp")
print(deleted_webhook_endpoint)
```

## `list()`

**List webhook endpoints.** Your webhook endpoints in this mode, newest first. Signing secrets are never listed.

```python
# client.webhook_endpoints
def list(self, *, limit: int | Unset = 20, starting_after: str | Unset = UNSET, ending_before: str | Unset = UNSET, more_voice_version: str | Unset = UNSET) -> AsyncPage[_m.WebhookEndpoint]
```

`client.webhook_endpoints.list()` · `await async_client.webhook_endpoints.list()` · `GET /webhook_endpoints` · [API reference](https://docs.morevoice.ai/api/operations/webhook_endpoints_list/)

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `limit` | `integer` | no | How many objects to return, 1–100 (default 20). |
| `starting_after` | `string` | no | A cursor (`next_cursor`) or object ID: return the objects after it (older). |
| `ending_before` | `string` | no | A cursor or object ID: return the objects before it (newer). |

### Returns

A page of results (`WebhookEndpointList`): `data`, `has_more` and `next_cursor`.

### Example

```python
from morevoice import MoreVoice

client = MoreVoice()  # MOREVOICE_API_KEY from the environment

for webhook_endpoint in client.webhook_endpoints.list():
    print(webhook_endpoint)
```

## `replay()`

**Replay events to an endpoint.** Queues a new delivery to this endpoint for every stored event since `since` that it subscribes to (optionally only `types`, or only the events whose deliveries to it never succeeded). At most 10 000 events; narrow the window otherwise. Events keep their ids, so receivers can deduplicate.

```python
# client.webhook_endpoints
def replay(self, id: str, body: _m.WebhookEndpointReplayParamsInput | Mapping[str, Any], *, more_voice_version: str | Unset = UNSET, idempotency_key: str | Unset = UNSET) -> _m.WebhookReplay
```

`client.webhook_endpoints.replay()` · `await async_client.webhook_endpoints.replay()` · `POST /webhook_endpoints/{id}/replay` · [API reference](https://docs.morevoice.ai/api/operations/webhook_endpoints_replay/)

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | `str` | yes | A webhook endpoint ID (`we_…`). |

### Request body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `since` | `string` | yes | Replay events created at or after this time (events are kept 30 days). |
| `only_failed` | `boolean` | no | Only events whose deliveries to this endpoint never succeeded. |
| `types` | `string[]` | no | Only these types (within the endpoint's own `enabled_events`). |
| `until` | `string` | no | …and up to this time (default: now). |

### Returns

`WebhookReplay`:

| Field | Type | Description |
| --- | --- | --- |
| `object` | `"webhook_replay"` | Always `webhook_replay`. |
| `endpoint_id` | `string` | A webhook endpoint ID (prefix `we_`). |
| `since` | `string` | An ISO-8601 timestamp in UTC. |
| `until` | `string` | An ISO-8601 timestamp in UTC. |
| `types` | `string[]` | — |
| `only_failed` | `boolean` | — |
| `events_queued` | `integer` | New deliveries queued (one per event; at most 10000). They keep their event ids, so receivers can deduplicate. |

### Example

```python
from morevoice import MoreVoice

client = MoreVoice()  # MOREVOICE_API_KEY from the environment

webhook_replay = client.webhook_endpoints.replay("we_7Hk2Lm9Qp", {
    "only_failed": True,
    "since": "2026-11-03T00:00:00Z",
})
print(webhook_replay)
```

## `retrieve()`

**Retrieve a webhook endpoint.** Returns a webhook endpoint: its URL, the events it receives, its status (and why it was disabled, if it was) and its API version. The signing secret is shown only when the endpoint is created and when the secret is rotated.

```python
# client.webhook_endpoints
def retrieve(self, id: str, *, more_voice_version: str | Unset = UNSET) -> _m.WebhookEndpoint
```

`client.webhook_endpoints.retrieve()` · `await async_client.webhook_endpoints.retrieve()` · `GET /webhook_endpoints/{id}` · [API reference](https://docs.morevoice.ai/api/operations/webhook_endpoints_retrieve/)

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | `str` | yes | A webhook endpoint ID (`we_…`). |

### Returns

`WebhookEndpoint`:

| Field | Type | Description |
| --- | --- | --- |
| `id` | `string` | A webhook endpoint ID (prefix `we_`). |
| `object` | `"webhook_endpoint"` | Always `webhook_endpoint`. |
| `url` | `string` | Where events are POSTed. |
| `description` | `string` | — |
| `enabled_events` | `string[]` | The event types sent to this endpoint; `["*"]` means every event. |
| `status` | `"enabled" \| "disabled" \| "auto_disabled"` | `auto_disabled`: every delivery failed for 72 hours, so sending stopped (and your admins were told). Set `status: enabled` to resume. |
| `disabled_reason` | `string \| null` | — |
| `disabled_at` | `string \| null` | — |
| `api_version` | `string` | The API version event payloads are rendered in. `legacy`: an endpoint moved from the old webhook settings, which keeps getting the previous body (`{id, event, createdAt, data}`). |
| `campaign_id` | `string \| null` | `cmp_…` ID. |
| `legacy_headers` | `boolean` | Also sends the previous `X-Webhook-*` headers (moved endpoints, for one deprecation cycle). |
| `max_in_flight` | `integer` | Deliveries in flight to this endpoint at once; more wait their turn. |
| `metadata` | `Metadata` | Up to 50 key/value pairs (keys up to 40 characters, values up to 500) you attach to an object. Returned as sent. |
| `previous_secret_expires_at` | `string \| null` | While a rotated secret still signs (alongside the new one): until when. |
| `signing` | `"hmac" \| "ed25519"` | How deliveries are signed (Standard Webhooks). `hmac` (default): `v1,<HMAC-SHA256>` with a shared secret (`whsec_…`) returned once. `ed25519`: `v1a,<Ed25519 signature>`; you verify with the endpoint's `public_key` (`whpk_…`) and no secret is ever shared. |
| `public_key` | `string \| null` | `ed25519` endpoints: the Ed25519 public key (`whpk_` + base64 of 32 bytes) that verifies `v1a` signatures; also as PEM at `GET /v1/webhook_endpoints/{id}/public_key`. Null for `hmac`. |
| `previous_public_key` | `string \| null` | After a rotation of an `ed25519` key: the previous public key, which still signs alongside until `previous_secret_expires_at`. |
| `livemode` | `boolean` | `true` in live mode, `false` in test mode. |
| `created` | `string` | An ISO-8601 timestamp in UTC. |
| `updated` | `string` | An ISO-8601 timestamp in UTC. |
| `secret` | `string` | The signing secret (`whsec_…`) of an `hmac` endpoint. Returned only when the endpoint is created and when its secret is rotated: store it then. `ed25519` endpoints never return one. |

### Example

```python
from morevoice import MoreVoice

client = MoreVoice()  # MOREVOICE_API_KEY from the environment

webhook_endpoint = client.webhook_endpoints.retrieve("we_7Hk2Lm9Qp")
print(webhook_endpoint)
```

## `retrieve_public_key()`

**Retrieve a webhook endpoint's public key.** The Ed25519 public key that verifies an `ed25519` endpoint's `v1a` signatures, as `whpk_…` (Standard Webhooks) and as SPKI PEM, with the previous key while a rotation overlaps. An `hmac` endpoint has none (409 `resource_conflict`).

```python
# client.webhook_endpoints
def retrieve_public_key(self, id: str, *, more_voice_version: str | Unset = UNSET) -> _m.WebhookEndpointPublicKey
```

`client.webhook_endpoints.retrieve_public_key()` · `await async_client.webhook_endpoints.retrieve_public_key()` · `GET /webhook_endpoints/{id}/public_key` · [API reference](https://docs.morevoice.ai/api/operations/webhook_endpoints_retrieve_public_key/)

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | `str` | yes | A webhook endpoint ID (`we_…`). |

### Returns

`WebhookEndpointPublicKey`:

| Field | Type | Description |
| --- | --- | --- |
| `object` | `"webhook_endpoint_public_key"` | Always `webhook_endpoint_public_key`. |
| `endpoint_id` | `string` | A webhook endpoint ID (prefix `we_`). |
| `algorithm` | `"ed25519"` | Always `ed25519`. |
| `signature_version` | `"v1a"` | The `webhook-signature` entries these keys verify (`v1a,<base64 signature>`). |
| `public_key` | `string \| null` | The current key: `whpk_` + base64 of the 32-byte Ed25519 public key (Standard Webhooks format). Null while an endpoint switched back to `hmac` still overlaps with its last Ed25519 key. |
| `public_key_pem` | `string \| null` | The same key as SPKI PEM, for libraries that load PEM. |
| `previous_public_key` | `string \| null` | After a rotation: the previous key, which still signs alongside the current one until `previous_expires_at`. |
| `previous_public_key_pem` | `string \| null` | — |
| `previous_expires_at` | `string \| null` | — |
| `livemode` | `boolean` | `true` in live mode, `false` in test mode. |

### Example

```python
from morevoice import MoreVoice

client = MoreVoice()  # MOREVOICE_API_KEY from the environment

webhook_endpoint_public_key = client.webhook_endpoints.retrieve_public_key("we_7Hk2Lm9Qp")
print(webhook_endpoint_public_key)
```

## `rotate_secret()`

**Rotate a webhook endpoint's secret.** Creates a new signing secret and returns it (this once). The previous secret keeps signing alongside it for `expire_previous_in_hours` (default 24), so `webhook-signature` carries both signatures until your receivers switch. An `ed25519` endpoint gets a new key pair instead: the response carries the new `public_key` and the previous one (no secret). `signing` switches the scheme with the same overlap.

```python
# client.webhook_endpoints
def rotate_secret(self, id: str, body: _m.WebhookEndpointRotateSecretParamsInput | Mapping[str, Any] | Unset = UNSET, *, more_voice_version: str | Unset = UNSET, idempotency_key: str | Unset = UNSET) -> _m.WebhookEndpoint
```

`client.webhook_endpoints.rotate_secret()` · `await async_client.webhook_endpoints.rotate_secret()` · `POST /webhook_endpoints/{id}/rotate_secret` · [API reference](https://docs.morevoice.ai/api/operations/webhook_endpoints_rotate_secret/)

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | `str` | yes | A webhook endpoint ID (`we_…`). |

### Request body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `expire_previous_in_hours` | `number` | no | How long the previous secret keeps signing alongside the new one (0–72 hours, default 24; 0 stops it at once). |
| `signing` | `"hmac" \| "ed25519"` | no | Switch the scheme with this rotation (default: keep the endpoint's). During the overlap `webhook-signature` carries both a `v1` and a `v1a` entry, so receivers can move over without missing an event. |

### Returns

`WebhookEndpoint`:

| Field | Type | Description |
| --- | --- | --- |
| `id` | `string` | A webhook endpoint ID (prefix `we_`). |
| `object` | `"webhook_endpoint"` | Always `webhook_endpoint`. |
| `url` | `string` | Where events are POSTed. |
| `description` | `string` | — |
| `enabled_events` | `string[]` | The event types sent to this endpoint; `["*"]` means every event. |
| `status` | `"enabled" \| "disabled" \| "auto_disabled"` | `auto_disabled`: every delivery failed for 72 hours, so sending stopped (and your admins were told). Set `status: enabled` to resume. |
| `disabled_reason` | `string \| null` | — |
| `disabled_at` | `string \| null` | — |
| `api_version` | `string` | The API version event payloads are rendered in. `legacy`: an endpoint moved from the old webhook settings, which keeps getting the previous body (`{id, event, createdAt, data}`). |
| `campaign_id` | `string \| null` | `cmp_…` ID. |
| `legacy_headers` | `boolean` | Also sends the previous `X-Webhook-*` headers (moved endpoints, for one deprecation cycle). |
| `max_in_flight` | `integer` | Deliveries in flight to this endpoint at once; more wait their turn. |
| `metadata` | `Metadata` | Up to 50 key/value pairs (keys up to 40 characters, values up to 500) you attach to an object. Returned as sent. |
| `previous_secret_expires_at` | `string \| null` | While a rotated secret still signs (alongside the new one): until when. |
| `signing` | `"hmac" \| "ed25519"` | How deliveries are signed (Standard Webhooks). `hmac` (default): `v1,<HMAC-SHA256>` with a shared secret (`whsec_…`) returned once. `ed25519`: `v1a,<Ed25519 signature>`; you verify with the endpoint's `public_key` (`whpk_…`) and no secret is ever shared. |
| `public_key` | `string \| null` | `ed25519` endpoints: the Ed25519 public key (`whpk_` + base64 of 32 bytes) that verifies `v1a` signatures; also as PEM at `GET /v1/webhook_endpoints/{id}/public_key`. Null for `hmac`. |
| `previous_public_key` | `string \| null` | After a rotation of an `ed25519` key: the previous public key, which still signs alongside until `previous_secret_expires_at`. |
| `livemode` | `boolean` | `true` in live mode, `false` in test mode. |
| `created` | `string` | An ISO-8601 timestamp in UTC. |
| `updated` | `string` | An ISO-8601 timestamp in UTC. |
| `secret` | `string` | The signing secret (`whsec_…`) of an `hmac` endpoint. Returned only when the endpoint is created and when its secret is rotated: store it then. `ed25519` endpoints never return one. |

### Example

```python
from morevoice import MoreVoice

client = MoreVoice()  # MOREVOICE_API_KEY from the environment

webhook_endpoint = client.webhook_endpoints.rotate_secret("we_7Hk2Lm9Qp", {
    "expire_previous_in_hours": 24,
})
print(webhook_endpoint)
```

## `test()`

**Send a test event.** Sends one sample event of `type` to the endpoint now, signed like a real delivery, and reports how it answered. Samples are not stored and never retried.

```python
# client.webhook_endpoints
def test(self, id: str, body: _m.WebhookEndpointTestParamsInput | Mapping[str, Any] | Unset = UNSET, *, more_voice_version: str | Unset = UNSET, idempotency_key: str | Unset = UNSET) -> _m.WebhookEndpointTest
```

`client.webhook_endpoints.test()` · `await async_client.webhook_endpoints.test()` · `POST /webhook_endpoints/{id}/test` · [API reference](https://docs.morevoice.ai/api/operations/webhook_endpoints_test/)

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | `str` | yes | A webhook endpoint ID (`we_…`). |

### Request body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `type` | `"call.created" \| "call.started" \| "call.ringing" \| "call.answered" \| "call.transferred" \| "call.ended" \| "call.analyzed" \| "call.cost_finalized" \| …` | no | The event type to send a sample of (default `test`). |

### Returns

`WebhookEndpointTest`:

| Field | Type | Description |
| --- | --- | --- |
| `object` | `"webhook_endpoint_test"` | Always `webhook_endpoint_test`. |
| `endpoint_id` | `string` | A webhook endpoint ID (prefix `we_`). |
| `event_id` | `string` | The sample's id (the `webhook-id` header). Samples are not stored. |
| `type` | `"call.created" \| "call.started" \| "call.ringing" \| "call.answered" \| "call.transferred" \| "call.ended" \| "call.analyzed" \| "call.cost_finalized" \| …` | — |
| `delivered` | `boolean` | The endpoint answered 2xx. |
| `response_status` | `integer \| null` | The HTTP status it answered (null: no answer, e.g. a timeout or a refused address). |
| `duration_ms` | `integer \| null` | — |
| `error` | `string \| null` | — |
| `response_excerpt` | `string \| null` | The first 2 KB of its answer. |

### Example

```python
from morevoice import MoreVoice

client = MoreVoice()  # MOREVOICE_API_KEY from the environment

webhook_endpoint_test = client.webhook_endpoints.test("we_7Hk2Lm9Qp", {
    "type": "call.ended",
})
print(webhook_endpoint_test)
```

## `update()`

**Update a webhook endpoint.** Change the URL, the events, the description, the API version or the status. `status: enabled` resumes an endpoint that was disabled (or auto-disabled after 72 hours of failures).

```python
# client.webhook_endpoints
def update(self, id: str, body: _m.WebhookEndpointUpdateParamsInput | Mapping[str, Any] | Unset = UNSET, *, more_voice_version: str | Unset = UNSET) -> _m.WebhookEndpoint
```

`client.webhook_endpoints.update()` · `await async_client.webhook_endpoints.update()` · `PATCH /webhook_endpoints/{id}` · [API reference](https://docs.morevoice.ai/api/operations/webhook_endpoints_update/)

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | `str` | yes | A webhook endpoint ID (`we_…`). |

### Request body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `api_version` | `"2026-11-01"` | no | Render payloads in this API version (default: the current one). |
| `description` | `string` | no | — |
| `enabled_events` | `string[]` | no | — |
| `max_in_flight` | `integer` | no | Deliveries in flight at once (1–100, default 10). |
| `metadata` | `MetadataInput` | no | Up to 50 key/value pairs (keys up to 40 characters, values up to 500) you attach to an object. Returned as sent. |
| `status` | `"enabled" \| "disabled"` | no | `disabled` stops deliveries; `enabled` resumes them (also after an auto-disable). |
| `url` | `string` | no | An https URL (http is accepted in test mode). Private and local addresses are refused. |

### Returns

`WebhookEndpoint`:

| Field | Type | Description |
| --- | --- | --- |
| `id` | `string` | A webhook endpoint ID (prefix `we_`). |
| `object` | `"webhook_endpoint"` | Always `webhook_endpoint`. |
| `url` | `string` | Where events are POSTed. |
| `description` | `string` | — |
| `enabled_events` | `string[]` | The event types sent to this endpoint; `["*"]` means every event. |
| `status` | `"enabled" \| "disabled" \| "auto_disabled"` | `auto_disabled`: every delivery failed for 72 hours, so sending stopped (and your admins were told). Set `status: enabled` to resume. |
| `disabled_reason` | `string \| null` | — |
| `disabled_at` | `string \| null` | — |
| `api_version` | `string` | The API version event payloads are rendered in. `legacy`: an endpoint moved from the old webhook settings, which keeps getting the previous body (`{id, event, createdAt, data}`). |
| `campaign_id` | `string \| null` | `cmp_…` ID. |
| `legacy_headers` | `boolean` | Also sends the previous `X-Webhook-*` headers (moved endpoints, for one deprecation cycle). |
| `max_in_flight` | `integer` | Deliveries in flight to this endpoint at once; more wait their turn. |
| `metadata` | `Metadata` | Up to 50 key/value pairs (keys up to 40 characters, values up to 500) you attach to an object. Returned as sent. |
| `previous_secret_expires_at` | `string \| null` | While a rotated secret still signs (alongside the new one): until when. |
| `signing` | `"hmac" \| "ed25519"` | How deliveries are signed (Standard Webhooks). `hmac` (default): `v1,<HMAC-SHA256>` with a shared secret (`whsec_…`) returned once. `ed25519`: `v1a,<Ed25519 signature>`; you verify with the endpoint's `public_key` (`whpk_…`) and no secret is ever shared. |
| `public_key` | `string \| null` | `ed25519` endpoints: the Ed25519 public key (`whpk_` + base64 of 32 bytes) that verifies `v1a` signatures; also as PEM at `GET /v1/webhook_endpoints/{id}/public_key`. Null for `hmac`. |
| `previous_public_key` | `string \| null` | After a rotation of an `ed25519` key: the previous public key, which still signs alongside until `previous_secret_expires_at`. |
| `livemode` | `boolean` | `true` in live mode, `false` in test mode. |
| `created` | `string` | An ISO-8601 timestamp in UTC. |
| `updated` | `string` | An ISO-8601 timestamp in UTC. |
| `secret` | `string` | The signing secret (`whsec_…`) of an `hmac` endpoint. Returned only when the endpoint is created and when its secret is rotated: store it then. `ed25519` endpoints never return one. |

### Example

```python
from morevoice import MoreVoice

client = MoreVoice()  # MOREVOICE_API_KEY from the environment

webhook_endpoint = client.webhook_endpoints.update("we_7Hk2Lm9Qp", {
    "enabled_events": [
        "call.*",
    ],
    "status": "enabled",
})
print(webhook_endpoint)
```
