# Webhook endpoints

Webhook endpoints of your organization, their state and delivery log. Needs the `webhooks:read` permission and a key for all workspaces and campaigns.

Base URL: https://api.melioralab.io/v1. Pass the key in the `Authorization: Bearer mel_live_...` header.

Endpoints:

- `GET /v1/webhook-endpoints`: List webhook endpoints
- `GET /v1/webhook-endpoints/{id}`: Get a webhook endpoint
- `GET /v1/webhook-endpoints/{id}/deliveries`: Delivery log

## The WebhookEndpoint object

A webhook endpoint of your organization and its state. The full URL is not returned because its path can be a secret: only the host. Additional header values are not returned either, only their names.

| Field | Type | Description |
|---|---|---|
| `object` | string | Object type, always `webhook_endpoint`. |
| `id` | string (uuid) | Endpoint id. |
| `status` | string | `active` receives events, `paused` is paused, `disabled` was turned off: by itself or by a 410 from the receiver. Values: `active`, `paused`, `disabled`. |
| `disabled_reason` | string or null | Why the endpoint is off or paused: `gone` (the receiver answered 410), `failing_72h` (errors for more than 3 days), `plan` (the plan has no webhooks), `other`. `null` for a working endpoint. Values: `gone`, `failing_72h`, `plan`, `other`. |
| `host` | string | Host of the receiver. |
| `description` | string or null | Description from the app, or `null`. |
| `event_types` | array of string | Event types the endpoint receives. |
| `workspace_ids` | array of string or null | Workspaces whose events the endpoint receives. `null`: all workspaces. |
| `campaign_ids` | array of string or null | Campaigns whose events the endpoint receives. `null`: all campaigns. |
| `include_message_text` | boolean | Whether the lead's reply text is sent. |
| `header_names` | array of string | Names of additional headers, without values. |
| `failing_since` | string (date-time) or null | Since when the endpoint does not accept events, or `null` if the last attempt succeeded. |
| `consecutive_failures` | integer | Failed attempts in a row. |
| `last_success_at` | string (date-time) or null | Time of the last successful delivery, or `null`. |
| `version` | integer | Version of the endpoint settings. Grows with every change. |
| `created_at` | string (date-time) | When the endpoint was added. |
| `updated_at` | string (date-time) | When the endpoint settings last changed. |
| `deliveries` | object | Delivery summary. |
| `deliveries.pending` | integer | How many events wait to be sent. |
| `deliveries.succeeded_24h` | integer | Delivered in the last 24 hours. |
| `deliveries.failed_24h` | integer | Not delivered in the last 24 hours. |
| `deliveries.last_finished_at` | string (date-time) or null | When the last delivery finished, or `null`. |
| `deliveries.last_status_code` | integer or null | Receiver's response code for the last delivery, or `null`. |
| `deliveries.last_error_class` | string or null | Class of the last error: `invalid_url`, `dns`, `ssrf_blocked`, `connect`, `tls`, `timeout`, `http`, `payload_too_large`, `unknown` or `null`. Values: `invalid_url`, `dns`, `ssrf_blocked`, `connect`, `tls`, `timeout`, `http`, `payload_too_large`, `unknown`. |

Example:

```json
{
  "object": "webhook_endpoint",
  "id": "6b2d9f40-3c1e-4a7b-8f5d-2e9c0a1b7d64",
  "status": "active",
  "disabled_reason": null,
  "host": "hooks.example.com",
  "description": "CRM",
  "event_types": [
    "lead.first_message_sent",
    "lead.replied"
  ],
  "workspace_ids": null,
  "campaign_ids": null,
  "include_message_text": false,
  "header_names": [
    "x-api-key"
  ],
  "failing_since": null,
  "consecutive_failures": 0,
  "last_success_at": "2026-10-27T09:15:03.000Z",
  "version": 2,
  "created_at": "2026-10-20T08:00:00.000Z",
  "updated_at": "2026-10-22T12:30:00.000Z",
  "deliveries": {
    "pending": 0,
    "succeeded_24h": 38,
    "failed_24h": 0,
    "last_finished_at": "2026-10-27T09:15:03.000Z",
    "last_status_code": 200,
    "last_error_class": null
  }
}
```

## The WebhookDelivery object

One delivery of an event to a webhook endpoint. No lead data: the event feed returns the event itself by `event_id`.

| Field | Type | Description |
|---|---|---|
| `object` | string | Object type, always `webhook_delivery`. |
| `id` | string (uuid) | Delivery id. |
| `endpoint_id` | string (uuid) | Webhook endpoint. |
| `event_id` | string | Event id: the same as in the `webhook-id` header and in the event feed. |
| `event_type` | string or null | Event type, `webhook.test` for a test event, `unknown` for an unknown one, or `null` if the event is gone. Values: `lead.first_message_sent`, `lead.replied`, `message.received`, `lead.status_changed`, `campaign.status_changed`, `account.status_changed`, `message.sent`, `message.delivery_unknown`, `webhook.test`, `unknown`. |
| `status` | string | `pending` waits, `delivering` is being sent, `succeeded` delivered, `failed` not delivered, `cancelled` cancelled. Values: `pending`, `delivering`, `succeeded`, `failed`, `cancelled`, `unknown`. |
| `cancel_reason` | string or null | Why the delivery was cancelled: `scope_changed`, `endpoint_deleted`, `expired`, `other`. `null` if not cancelled. Values: `scope_changed`, `endpoint_deleted`, `expired`, `other`. |
| `attempt` | integer | Number of attempts. |
| `next_attempt_at` | string (date-time) or null | When the next attempt is due. Only for `pending`, otherwise `null`. |
| `last_status_code` | integer or null | Receiver's response code for the last attempt, or `null`. |
| `last_error_class` | string or null | Error class of the last attempt, or `null`. Values: `invalid_url`, `dns`, `ssrf_blocked`, `connect`, `tls`, `timeout`, `http`, `payload_too_large`, `unknown`. |
| `last_duration_ms` | integer or null | Duration of the last attempt in milliseconds, or `null`. |
| `created_at` | string (date-time) | When the event was queued for delivery. |
| `finished_at` | string (date-time) or null | When the delivery finished, or `null`. |

Example:

```json
{
  "object": "webhook_delivery",
  "id": "7c4e1a85-2d9b-4f63-a0e8-5b3f9c7d2e16",
  "endpoint_id": "6b2d9f40-3c1e-4a7b-8f5d-2e9c0a1b7d64",
  "event_id": "evt_1f8a3c5e7b9d4a2c6e0f8b1d3a5c7e9f",
  "event_type": "lead.replied",
  "status": "succeeded",
  "cancel_reason": null,
  "attempt": 1,
  "next_attempt_at": null,
  "last_status_code": 200,
  "last_error_class": null,
  "last_duration_ms": 184,
  "created_at": "2026-10-27T09:15:02.000Z",
  "finished_at": "2026-10-27T09:15:03.000Z"
}
```

## List webhook endpoints

`GET /v1/webhook-endpoints`

Webhook endpoints of your organization and their state: whether they accept events, since when they fail, the last error and deliveries in the last 24 hours, oldest first. Needs a key for all workspaces and campaigns: webhooks belong to the whole organization.

Permission: `webhooks:read`. Request weight: 1.

### Parameters

| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `limit` | query | integer | no | Number of objects to return per page. From 1 to 100. Defaults to `50`. |
| `cursor` | query | string | no | The `next_cursor` value from the previous response. Other filters must match the first request. |
| `status` | query | string | no | Only endpoints in this state. Values: `active`, `paused`, `disabled`. |

### Response

A list: `object`, `data`, `has_more`, `next_cursor`. Pagination: https://docs.melioralab.io/en/md/pagination.md

### Endpoint-specific errors

- `cursor_mismatch` (400): The cursor is corrupted, was issued for a different route or filters, or the key's permissions and scope have changed.
- `scope_required` (403): The key does not permit the operation. A command requires its kind permission.
- `not_found` (404): The object was not found or is outside the key's scope. An unknown path also returns 404.
- `cursor_expired` (410): The cursor expired: 15 minutes for lists, 30 days for the event feed.

Common errors: https://docs.melioralab.io/en/md/errors.md

### Example request

```bash
curl https://api.melioralab.io/v1/webhook-endpoints \
  -H "Authorization: Bearer $MELIORA_API_KEY"
```

### Example response

```json
{
  "object": "list",
  "data": [
    {
      "object": "webhook_endpoint",
      "id": "6b2d9f40-3c1e-4a7b-8f5d-2e9c0a1b7d64",
      "status": "active",
      "disabled_reason": null,
      "host": "hooks.example.com",
      "description": "CRM",
      "event_types": [
        "lead.first_message_sent",
        "lead.replied"
      ],
      "workspace_ids": null,
      "campaign_ids": null,
      "include_message_text": false,
      "header_names": [
        "x-api-key"
      ],
      "failing_since": null,
      "consecutive_failures": 0,
      "last_success_at": "2026-10-27T09:15:03.000Z",
      "version": 2,
      "created_at": "2026-10-20T08:00:00.000Z",
      "updated_at": "2026-10-22T12:30:00.000Z",
      "deliveries": {
        "pending": 0,
        "succeeded_24h": 38,
        "failed_24h": 0,
        "last_finished_at": "2026-10-27T09:15:03.000Z",
        "last_status_code": 200,
        "last_error_class": null
      }
    }
  ],
  "has_more": false,
  "next_cursor": null
}
```

## Get a webhook endpoint

`GET /v1/webhook-endpoints/{id}`

One webhook endpoint by id. A deleted endpoint gives 404.

Permission: `webhooks:read`. Request weight: 1.

### Parameters

| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `id` | path | string (uuid) | yes | Webhook endpoint id. |

### Response

Returns a `WebhookEndpoint` object.

### Endpoint-specific errors

- `scope_required` (403): The key does not permit the operation. A command requires its kind permission.
- `not_found` (404): The object was not found or is outside the key's scope. An unknown path also returns 404.

Common errors: https://docs.melioralab.io/en/md/errors.md

### Example request

```bash
curl https://api.melioralab.io/v1/webhook-endpoints/ \
  -H "Authorization: Bearer $MELIORA_API_KEY"
```

### Example response

```json
{
  "object": "webhook_endpoint",
  "id": "6b2d9f40-3c1e-4a7b-8f5d-2e9c0a1b7d64",
  "status": "active",
  "disabled_reason": null,
  "host": "hooks.example.com",
  "description": "CRM",
  "event_types": [
    "lead.first_message_sent",
    "lead.replied"
  ],
  "workspace_ids": null,
  "campaign_ids": null,
  "include_message_text": false,
  "header_names": [
    "x-api-key"
  ],
  "failing_since": null,
  "consecutive_failures": 0,
  "last_success_at": "2026-10-27T09:15:03.000Z",
  "version": 2,
  "created_at": "2026-10-20T08:00:00.000Z",
  "updated_at": "2026-10-22T12:30:00.000Z",
  "deliveries": {
    "pending": 0,
    "succeeded_24h": 38,
    "failed_24h": 0,
    "last_finished_at": "2026-10-27T09:15:03.000Z",
    "last_status_code": 200,
    "last_error_class": null
  }
}
```

## Delivery log

`GET /v1/webhook-endpoints/{id}/deliveries`

Event deliveries to one endpoint, newest first: state, attempts, response code and error. Helps find out why your CRM did not get an event. The event feed returns the event itself by `event_id`.

Permission: `webhooks:read`. Request weight: 2.

### Parameters

| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `id` | path | string (uuid) | yes | Webhook endpoint id. |
| `limit` | query | integer | no | Number of objects to return per page. From 1 to 100. Defaults to `50`. |
| `cursor` | query | string | no | The `next_cursor` value from the previous response. Other filters must match the first request. |
| `status` | query | string | no | Only deliveries in this state. Values: `pending`, `delivering`, `succeeded`, `failed`, `cancelled`. |

### Response

A list: `object`, `data`, `has_more`, `next_cursor`. Pagination: https://docs.melioralab.io/en/md/pagination.md

### Endpoint-specific errors

- `cursor_mismatch` (400): The cursor is corrupted, was issued for a different route or filters, or the key's permissions and scope have changed.
- `scope_required` (403): The key does not permit the operation. A command requires its kind permission.
- `not_found` (404): The object was not found or is outside the key's scope. An unknown path also returns 404.
- `cursor_expired` (410): The cursor expired: 15 minutes for lists, 30 days for the event feed.

Common errors: https://docs.melioralab.io/en/md/errors.md

### Example request

```bash
curl https://api.melioralab.io/v1/webhook-endpoints//deliveries \
  -H "Authorization: Bearer $MELIORA_API_KEY"
```

### Example response

```json
{
  "object": "list",
  "data": [
    {
      "object": "webhook_delivery",
      "id": "7c4e1a85-2d9b-4f63-a0e8-5b3f9c7d2e16",
      "endpoint_id": "6b2d9f40-3c1e-4a7b-8f5d-2e9c0a1b7d64",
      "event_id": "evt_1f8a3c5e7b9d4a2c6e0f8b1d3a5c7e9f",
      "event_type": "lead.replied",
      "status": "succeeded",
      "cancel_reason": null,
      "attempt": 1,
      "next_attempt_at": null,
      "last_status_code": 200,
      "last_error_class": null,
      "last_duration_ms": 184,
      "created_at": "2026-10-27T09:15:02.000Z",
      "finished_at": "2026-10-27T09:15:03.000Z"
    }
  ],
  "has_more": true,
  "next_cursor": "v1.k1.Qm9hcmRfMWZ4.c3RyaWN0bHlfb3BhcXVl.dGFnX2V4YW1wbGU.bWFjX2V4YW1wbGVfdmFsdWU"
}
```
