> ## Documentation Index
> Fetch the complete documentation index at: https://help.cloudtalk.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Manage webhooks with the API

> Create, update and delete webhook endpoints, read and rotate signing secrets, send test events and inspect deliveries from the CloudTalk API instead of the Dashboard.

Everything you can do under **Account → Webhooks** in the Dashboard, you can also do from the API at `https://api.cloudtalk.io/v1/webhooks`. Endpoints created in the API and in the Dashboard are the same endpoints: each place sees and manages the other's.

Use it to provision webhooks from your own onboarding flow, keep endpoint configuration in code, or let an integration register its own endpoint.

<Note>
  Webhooks are available on the **Essential** plan and higher. Requests authenticate with an API key, like the rest of the [CloudTalk API](/guides/authentication). API keys can only be created by Admins, the same role that manages webhooks in the Dashboard.
</Note>

## Quick start

<Steps>
  <Step title="Pick the event types">
    List what you can subscribe to:

    ```bash theme={"system"}
    curl -u ACCESS_KEY_ID:ACCESS_KEY_SECRET \
      https://api.cloudtalk.io/v1/webhooks/event-types
    ```

    Add `?include=schema` to get the JSON Schema of every event's body.
  </Step>

  <Step title="Create the endpoint and store its secret">
    ```bash theme={"system"}
    curl -u ACCESS_KEY_ID:ACCESS_KEY_SECRET \
      -X POST https://api.cloudtalk.io/v1/webhooks/endpoints \
      -H "Content-Type: application/json" \
      -H "Idempotency-Key: crm-sync-setup-1" \
      -d '{
        "url": "https://hooks.example.com/cloudtalk",
        "description": "CRM sync",
        "eventTypes": ["call.ended", "contact.updated"]
      }'
    ```

    ```json theme={"system"}
    {
      "id": "ep_2nJ4qKZr8C1tY0dXwV3uFh7bM9s",
      "url": "https://hooks.example.com/cloudtalk",
      "description": "CRM sync",
      "eventTypes": ["call.ended", "contact.updated"],
      "status": "enabled",
      "createdAt": "2026-10-13T09:12:44Z",
      "updatedAt": "2026-10-13T09:12:44Z",
      "secret": "whsec_EXAMPLE_SIGNING_SECRET"
    }
    ```

    Store `secret` in your handler's configuration and [verify signatures](/guides/webhooks/verify-signatures) on every delivery. You can read it again later with [Get the signing secret](/api-reference/webhooks/get-the-signing-secret).
  </Step>

  <Step title="Send a test event">
    ```bash theme={"system"}
    curl -u ACCESS_KEY_ID:ACCESS_KEY_SECRET \
      -X POST https://api.cloudtalk.io/v1/webhooks/endpoints/ep_2nJ4qKZr8C1tY0dXwV3uFh7bM9s/test \
      -H "Content-Type: application/json" \
      -d '{"eventType": "call.ended"}'
    ```

    The answer is `202` with a `deliveryId`. The test payload has the real structure of the event with placeholder values.
  </Step>

  <Step title="Check that it arrived">
    ```bash theme={"system"}
    curl -u ACCESS_KEY_ID:ACCESS_KEY_SECRET \
      https://api.cloudtalk.io/v1/webhooks/endpoints/ep_2nJ4qKZr8C1tY0dXwV3uFh7bM9s/deliveries/msg_2nJ5bQw1pX7cT4aZsY8vHk0dLr3
    ```

    You get the payload that was sent and every attempt, with the HTTP status your server answered and the first 4 KB of its response. When no HTTP response came back (connection refused, TLS failure, timeout), the attempt shows status code `0` and a fixed message instead of a body. A delivery shows up from its first attempt, usually within seconds of the test.
  </Step>
</Steps>

## Operations

| Operation | Request |
| - | - |
| [List webhook event types](/api-reference/webhooks/list-webhook-event-types) | `GET /webhooks/event-types` |
| [List webhook endpoints](/api-reference/webhooks/list-webhook-endpoints) | `GET /webhooks/endpoints` |
| [Create a webhook endpoint](/api-reference/webhooks/create-a-webhook-endpoint) | `POST /webhooks/endpoints` |
| [Get a webhook endpoint](/api-reference/webhooks/get-a-webhook-endpoint) | `GET /webhooks/endpoints/{endpointId}` |
| [Update a webhook endpoint](/api-reference/webhooks/update-a-webhook-endpoint) | `PATCH /webhooks/endpoints/{endpointId}` |
| [Delete a webhook endpoint](/api-reference/webhooks/delete-a-webhook-endpoint) | `DELETE /webhooks/endpoints/{endpointId}` |
| [Get the signing secret](/api-reference/webhooks/get-the-signing-secret) | `GET /webhooks/endpoints/{endpointId}/secret` |
| [Rotate the signing secret](/api-reference/webhooks/rotate-the-signing-secret) | `POST /webhooks/endpoints/{endpointId}/secret/rotate` |
| [Send a test event](/api-reference/webhooks/send-a-test-event) | `POST /webhooks/endpoints/{endpointId}/test` |
| [List deliveries](/api-reference/webhooks/list-deliveries) | `GET /webhooks/endpoints/{endpointId}/deliveries` |
| [Get a delivery](/api-reference/webhooks/get-a-delivery) | `GET /webhooks/endpoints/{endpointId}/deliveries/{deliveryId}` |
| [Retry a delivery](/api-reference/webhooks/retry-a-delivery) | `POST /webhooks/endpoints/{endpointId}/deliveries/{deliveryId}/retry` |

List operations return `{ "data": [...], "pagination": { "limit": 20, "next": "..." }, "requestId": "..." }`. Set `?limit=` (default 20, at most 100) and pass `pagination.next` as `?cursor=` to get the next page; `next` is `null` on the last page.

## Endpoints

**URL.** It must be `https://` and point at a public host whose name ends in a public top-level domain (`.com`, `.io`, `.co.uk` and so on). Private, loopback and other non-public addresses, internal host names and non-ASCII host names are rejected with `422 WEBHOOKS.INVALID_URL`; send an internationalized domain in its punycode (`xn--`) form. The URL comes back as stored, which may differ slightly from what you sent: the host lowercased, a bare host with a trailing `/`.

**Event types.** `eventTypes` lists the types the endpoint receives. Omit it, or send `null`, to receive every event type, including ones added later. An empty list is rejected. Responses list the types sorted.

**Status.** Every endpoint reports one of:

| `status` | Meaning |
| - | - |
| `enabled` | Deliveries are sent. |
| `disabled` | You turned it off (Dashboard, or `enabled: false`). CloudTalk never turns it back on by itself. |
| `failing` | CloudTalk turned it off after about 5 days of failed deliveries. It is re-enabled automatically if it recovers within 12 hours; you can also turn it back on with `enabled: true`. See [delivery and retries](/guides/webhooks/delivery). |

**Updating.** `PATCH` changes only the fields you send: `url`, `description`, `eventTypes`, `enabled`. `null` is accepted only for `eventTypes` (every event type).

**Deleting** is permanent: the endpoint, its signing secret and its delivery history are removed, and delivery stops immediately.

**Limit.** A company can have up to **50** endpoints, Dashboard-created ones included. Creating one more answers `409 WEBHOOKS.ENDPOINT_LIMIT_REACHED`.

## Safe retries with Idempotency-Key

A network error on `POST /webhooks/endpoints` leaves you not knowing whether the endpoint was created. Send an `Idempotency-Key` header (any text up to 255 characters) and retry with the same key and the same body:

* the endpoint already exists: you get it back with `201` and its secret, even if the company has meanwhile reached the 50-endpoint limit;
* the same key with a different body: `422 IDEMPOTENCY.KEY_REUSED` (a key names one create);
* after the endpoint is deleted, the same key creates a new one.

## Signing secrets

[Rotate the signing secret](/api-reference/webhooks/rotate-the-signing-secret) returns the new secret and `previousSecretExpiresAt`. Until that time, 24 hours after the rotation, every delivery is signed with both the old and the new secret, so you can roll the new one out to your handlers without dropping events. After it, only the new secret signs. The `create` response is the only time the secret is part of the endpoint object.

## Test events and the delivery log

* A test event must be a type the endpoint receives (`422 WEBHOOKS.EVENT_TYPE_NOT_SUBSCRIBED` otherwise), and the endpoint must be enabled (`409 WEBHOOKS.ENDPOINT_DISABLED`).
* `deliveryId` is the same value as the `svix-id` header on the request your endpoint received, so you can look up a delivery straight from your own logs.
* Delivery `status` is `succeeded`, `retrying` (an attempt failed and another is scheduled at `nextAttemptAt`), `failed` (no retry left), `pending` or `canceled`. Filter the list with `?status=succeeded` or `?status=failed`, `?eventType=` (comma-separated) and `?since=` / `?until=` (RFC 3339).
* Payloads are kept for 30 days. Older deliveries still appear, with `payload: null`.
* [Retry a delivery](/api-reference/webhooks/retry-a-delivery) works only for a delivery the endpoint actually received.

## Limits and errors

* The webhooks API allows **60 requests per minute** per company. This is its own budget: it does not consume the core API's [60 requests per minute](/guides/rate-limiting), and core API calls do not consume it. Over it you get `429` from the gateway with `X-CloudTalkAPI-Limit`, `X-CloudTalkAPI-Remaining` and `X-CloudTalkAPI-ResetTime`, and no error body.
* Test events and retries together allow **10 per minute** per company. Their responses carry `X-CloudTalkAPI-Send-Limit`, `X-CloudTalkAPI-Send-Remaining` and `X-CloudTalkAPI-Send-ResetTime` (a Unix timestamp, like the API-wide `X-CloudTalkAPI-ResetTime`); over the limit you get `429 RATE_LIMIT.EXCEEDED` with `Retry-After` (seconds).
* Request bodies must be sent as `Content-Type: application/json`, otherwise `415 UNSUPPORTED_MEDIA_TYPE`.
* Paths are case-sensitive: `/webhooks/endpoints`, not `/Webhooks/Endpoints`.
* A request that takes longer than 10 seconds answers `504 WEBHOOKS.UPSTREAM_TIMEOUT`; it is safe to retry (with the same `Idempotency-Key` for a create).

Errors share one shape:

```json theme={"system"}
{
  "error": {
    "code": "WEBHOOKS.INVALID_URL",
    "message": "url must use https://. Plain http is not accepted.",
    "target": "url"
  },
  "requestId": "c9856b94-db55-402b-a6ba-adb130351158"
}
```

Branch on `code`; `message` is for people and can change. Quote `requestId` when you contact support.


## Related topics

- [CloudTalk Webhooks](/guides/webhooks/overview.md)
- [CloudTalk API Rate Limits, Headers, and Throttling](/guides/rate-limiting.md)
- [Delete a webhook endpoint](/api-reference/webhooks/delete-a-webhook-endpoint.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.