# Webhooks API

## What this covers

Signed, retried event delivery per project. The signing secret is returned once at creation; deliveries that exhaust their retries are readable as dead letters.

Every example below runs as written once these are exported:

```bash
export DVAARIK_API_KEY="dvk_..."        # project API key, server side only
export DVAARIK_TOKEN="<account access token>"   # console session, from POST /console/auth/login
export ENDPOINT_ID="<uuid>"
export PROJECT_ID="<uuid>"
```

A `<value>` inside a JSON body is a value you supply. Responses are JSON; every error body is `{ "detail": … }`. Each schema is expanded once per page, where it first appears.

### Machine door errors

Raised by the project-key dependency before any handler on this page runs.

| Status | Cause |
| --- | --- |
| `401` | `Missing API key` or `Invalid API key` — the `X-Api-Key` header is absent, unknown, or revoked. |
| `403` | `API key is not scoped to a project`, `Project unavailable`, or `API key is missing the required scope '<scope>'`. |
| `429` | The project's `api_rpm` ceiling was exceeded. Honour `Retry-After`; `X-RateLimit-*` headers are on every response. |

Scopes used on this page: `webhook:read`, `webhook:write`. Mint a key with only these.

### Account door errors

| Status | Cause |
| --- | --- |
| `401` | No account session was presented, or it is invalid or expired. Refresh once, then re-authenticate. |
| `403` | The account is suspended or its email is not verified. |

## Operations — project API key

Call these from your own server with a project key. Never from a browser or a mobile bundle.

### GET `/v2/projects/{project_id}/webhooks`

List the project's endpoints. `include_disabled=true` also returns disabled ones.

| Detail | Value |
| --- | --- |
| Door | Project API key — `X-Api-Key: $DVAARIK_API_KEY` |
| Scope | `webhook:read` |
| Operation | `listWebhookEndpoints` |
| Success | `200` `WebhookEndpointInfo[]` |

| Parameter | In | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `project_id` | path | uuid | yes |  |
| `include_disabled` | query | boolean | no |  |

#### `WebhookEndpointInfo`

| Field | Type | Required | Notes |
| --- | --- | --- | --- |
| `created_at` | date-time | yes |  |
| `event_types` | string[] | yes |  |
| `id` | uuid | yes |  |
| `name` | string | yes |  |
| `project_id` | uuid | yes |  |
| `revision` | integer | yes |  |
| `secret_hint` | string | yes |  |
| `status` | string | yes |  |
| `updated_at` | date-time | yes |  |
| `url` | string | yes |  |

| Error | When |
| --- | --- |
| `422` | The request body or query is not valid. `detail` is an array of `{loc, msg, type}` and `loc` names the offending field. |

Plus the machine-door errors above. Full list on the [errors page](/docs/errors).

```bash
curl -sS -X GET "https://api.developers.dvaarik.com/v2/projects/$PROJECT_ID/webhooks" \
  -H "X-Api-Key: $DVAARIK_API_KEY"
```

### POST `/v2/projects/{project_id}/webhooks`

Register a public HTTPS endpoint. The `201` carries the signing secret once; no route returns it again.

| Detail | Value |
| --- | --- |
| Door | Project API key — `X-Api-Key: $DVAARIK_API_KEY` |
| Scope | `webhook:write` |
| Operation | `createWebhookEndpoint` |
| Success | `201` `CreatedWebhookEndpoint` |

| Parameter | In | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `project_id` | path | uuid | yes |  |

Request body: `CreateWebhookEndpointRequest`.

#### `CreateWebhookEndpointRequest`

| Field | Type | Required | Notes |
| --- | --- | --- | --- |
| `event_types` | string[] | no | max 50 item(s) |
| `name` | string | yes | min length 1; max length 120 |
| `url` | string | yes | min length 8; max length 2048 |

#### `CreatedWebhookEndpoint`

The 201, and the ONLY response that ever contains the secret.

| Field | Type | Required | Notes |
| --- | --- | --- | --- |
| `created_at` | date-time | yes |  |
| `event_types` | string[] | yes |  |
| `id` | uuid | yes |  |
| `name` | string | yes |  |
| `project_id` | uuid | yes |  |
| `revision` | integer | yes |  |
| `secret_hint` | string | yes |  |
| `signing_secret` | string | yes |  |
| `status` | string | yes |  |
| `updated_at` | date-time | yes |  |
| `url` | string | yes |  |

| Error | When |
| --- | --- |
| `422` | The request body or query is not valid. `detail` is an array of `{loc, msg, type}` and `loc` names the offending field. |

Plus the machine-door errors above. Full list on the [errors page](/docs/errors).

```bash
curl -sS -X POST "https://api.developers.dvaarik.com/v2/projects/$PROJECT_ID/webhooks" \
  -H "X-Api-Key: $DVAARIK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "primary",
    "url": "https://example.com/hooks/dvaarik",
    "event_types": [
      "call.completed",
      "call.failed"
    ]
  }'
```

### DELETE `/v2/projects/{project_id}/webhooks/{endpoint_id}`

Disable the endpoint, bump its revision and cancel queued deliveries.

| Detail | Value |
| --- | --- |
| Door | Project API key — `X-Api-Key: $DVAARIK_API_KEY` |
| Scope | `webhook:write` |
| Operation | `disableWebhookEndpoint` |
| Success | `200` `WebhookEndpointInfo` |

| Parameter | In | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `project_id` | path | uuid | yes |  |
| `endpoint_id` | path | uuid | yes |  |

| Error | When |
| --- | --- |
| `422` | The request body or query is not valid. `detail` is an array of `{loc, msg, type}` and `loc` names the offending field. |

Plus the machine-door errors above. Full list on the [errors page](/docs/errors).

```bash
curl -sS -X DELETE "https://api.developers.dvaarik.com/v2/projects/$PROJECT_ID/webhooks/$ENDPOINT_ID" \
  -H "X-Api-Key: $DVAARIK_API_KEY"
```

### POST `/v2/projects/{project_id}/webhooks/{endpoint_id}/test`

Queue a signed `webhook.ping`. The `202` means the delivery was recorded, not that it arrived.

| Detail | Value |
| --- | --- |
| Door | Project API key — `X-Api-Key: $DVAARIK_API_KEY` |
| Scope | `webhook:write` |
| Operation | `testWebhookEndpoint` |
| Success | `202` `WebhookTestResult` |

| Parameter | In | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `project_id` | path | uuid | yes |  |
| `endpoint_id` | path | uuid | yes |  |

#### `WebhookTestResult`

What a test ping queued, not what a receiver said. `queued` is False when an identical ping is already waiting: the dedupe key is the endpoint plus its revision plus the minute, so leaning on the button does not turn into a burst against the developer's own server. The delivery id is returned either way so the caller can find it in the dead letters.

| Field | Type | Required | Notes |
| --- | --- | --- | --- |
| `delivery_id` | uuid | yes |  |
| `endpoint_id` | uuid | yes |  |
| `event_type` | string | yes |  |
| `queued` | boolean | yes |  |

| Error | When |
| --- | --- |
| `422` | The request body or query is not valid. `detail` is an array of `{loc, msg, type}` and `loc` names the offending field. |

Plus the machine-door errors above. Full list on the [errors page](/docs/errors).

```bash
curl -sS -X POST "https://api.developers.dvaarik.com/v2/projects/$PROJECT_ID/webhooks/$ENDPOINT_ID/test" \
  -H "X-Api-Key: $DVAARIK_API_KEY"
```

### GET `/v2/projects/{project_id}/webhooks/dead-letters`

List deliveries that exhausted their retries, with attempt counts and the last error.

| Detail | Value |
| --- | --- |
| Door | Project API key — `X-Api-Key: $DVAARIK_API_KEY` |
| Scope | `webhook:read` |
| Operation | `listWebhookDeadLetters` |
| Success | `200` `DeadLetterInfo[]` |

| Parameter | In | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `project_id` | path | uuid | yes |  |
| `limit` | query | integer | no |  |

#### `DeadLetterInfo`

| Field | Type | Required | Notes |
| --- | --- | --- | --- |
| `attempts` | integer | yes |  |
| `created_at` | date-time | yes |  |
| `endpoint_id` | uuid or null | no |  |
| `event_type` | string or null | no |  |
| `id` | uuid | yes |  |
| `kind` | string | yes |  |
| `last_error` | string or null | no |  |
| `max_attempts` | integer | yes |  |
| `updated_at` | date-time | yes |  |

| Error | When |
| --- | --- |
| `422` | The request body or query is not valid. `detail` is an array of `{loc, msg, type}` and `loc` names the offending field. |

Plus the machine-door errors above. Full list on the [errors page](/docs/errors).

```bash
curl -sS -X GET "https://api.developers.dvaarik.com/v2/projects/$PROJECT_ID/webhooks/dead-letters?limit=20" \
  -H "X-Api-Key: $DVAARIK_API_KEY"
```

## Operations — account session

The same resource through the console door. These are what the web console calls; both doors return the same shapes.

### GET `/console/v2/projects/{project_id}/webhooks`

List the project's endpoints. `include_disabled=true` also returns disabled ones.

| Detail | Value |
| --- | --- |
| Door | Account session — `Authorization: Bearer $DVAARIK_TOKEN` |
| Operation | `consoleListWebhookEndpoints` |
| Success | `200` `WebhookEndpointInfo[]` |

| Parameter | In | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `project_id` | path | uuid | yes |  |
| `include_disabled` | query | boolean | no |  |

| Error | When |
| --- | --- |
| `401` | No account session was presented, or it is invalid or expired. |
| `403` | The account is suspended or its email is not verified. |
| `404` | The project or the addressed resource does not exist for this owner. A resource owned by another developer is deliberately indistinguishable from one that never existed. |
| `422` | The request body or query is not valid. `detail` is an array of `{loc, msg, type}` and `loc` names the offending field. |

Plus the account-door errors above. Full list on the [errors page](/docs/errors).

```bash
curl -sS -X GET "https://api.developers.dvaarik.com/console/v2/projects/$PROJECT_ID/webhooks" \
  -H "Authorization: Bearer $DVAARIK_TOKEN"
```

### POST `/console/v2/projects/{project_id}/webhooks`

Register a public HTTPS endpoint. The `201` carries the signing secret once; no route returns it again.

| Detail | Value |
| --- | --- |
| Door | Account session — `Authorization: Bearer $DVAARIK_TOKEN` |
| Operation | `consoleCreateWebhookEndpoint` |
| Success | `201` `CreatedWebhookEndpoint` |

| Parameter | In | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `project_id` | path | uuid | yes |  |

Request body: `CreateWebhookEndpointRequest`.

| Error | When |
| --- | --- |
| `401` | No account session was presented, or it is invalid or expired. |
| `403` | The account is suspended or its email is not verified. |
| `404` | The project or the addressed resource does not exist for this owner. A resource owned by another developer is deliberately indistinguishable from one that never existed. |
| `409` | The project or resource is in a state that refuses this change. |
| `422` | The request body or query is not valid. `detail` is an array of `{loc, msg, type}` and `loc` names the offending field. |
| `503` | Webhook secret storage (the credential keyring) is unavailable. |

Plus the account-door errors above. Full list on the [errors page](/docs/errors).

```bash
curl -sS -X POST "https://api.developers.dvaarik.com/console/v2/projects/$PROJECT_ID/webhooks" \
  -H "Authorization: Bearer $DVAARIK_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "primary",
    "url": "https://example.com/hooks/dvaarik",
    "event_types": [
      "call.completed",
      "call.failed"
    ]
  }'
```

### DELETE `/console/v2/projects/{project_id}/webhooks/{endpoint_id}`

Disable the endpoint, bump its revision and cancel queued deliveries.

| Detail | Value |
| --- | --- |
| Door | Account session — `Authorization: Bearer $DVAARIK_TOKEN` |
| Operation | `consoleDisableWebhookEndpoint` |
| Success | `200` `WebhookEndpointInfo` |

| Parameter | In | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `endpoint_id` | path | uuid | yes |  |
| `project_id` | path | uuid | yes |  |

| Error | When |
| --- | --- |
| `401` | No account session was presented, or it is invalid or expired. |
| `403` | The account is suspended or its email is not verified. |
| `404` | The project or the addressed resource does not exist for this owner. A resource owned by another developer is deliberately indistinguishable from one that never existed. |
| `409` | The project or resource is in a state that refuses this change. |
| `422` | The request body or query is not valid. `detail` is an array of `{loc, msg, type}` and `loc` names the offending field. |

Plus the account-door errors above. Full list on the [errors page](/docs/errors).

```bash
curl -sS -X DELETE "https://api.developers.dvaarik.com/console/v2/projects/$PROJECT_ID/webhooks/$ENDPOINT_ID" \
  -H "Authorization: Bearer $DVAARIK_TOKEN"
```

### POST `/console/v2/projects/{project_id}/webhooks/{endpoint_id}/test`

Queue a signed `webhook.ping`. The `202` means the delivery was recorded, not that it arrived.

| Detail | Value |
| --- | --- |
| Door | Account session — `Authorization: Bearer $DVAARIK_TOKEN` |
| Operation | `consoleTestWebhookEndpoint` |
| Success | `202` `WebhookTestResult` |

| Parameter | In | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `endpoint_id` | path | uuid | yes |  |
| `project_id` | path | uuid | yes |  |

| Error | When |
| --- | --- |
| `401` | No account session was presented, or it is invalid or expired. |
| `403` | The account is suspended or its email is not verified. |
| `404` | The project or the addressed resource does not exist for this owner. A resource owned by another developer is deliberately indistinguishable from one that never existed. |
| `409` | The project or resource is in a state that refuses this change. |
| `422` | The request body or query is not valid. `detail` is an array of `{loc, msg, type}` and `loc` names the offending field. |

Plus the account-door errors above. Full list on the [errors page](/docs/errors).

```bash
curl -sS -X POST "https://api.developers.dvaarik.com/console/v2/projects/$PROJECT_ID/webhooks/$ENDPOINT_ID/test" \
  -H "Authorization: Bearer $DVAARIK_TOKEN"
```

### GET `/console/v2/projects/{project_id}/webhooks/dead-letters`

List deliveries that exhausted their retries, with attempt counts and the last error.

| Detail | Value |
| --- | --- |
| Door | Account session — `Authorization: Bearer $DVAARIK_TOKEN` |
| Operation | `consoleListWebhookDeadLetters` |
| Success | `200` `DeadLetterInfo[]` |

| Parameter | In | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `project_id` | path | uuid | yes |  |
| `limit` | query | integer | no |  |

| Error | When |
| --- | --- |
| `401` | No account session was presented, or it is invalid or expired. |
| `403` | The account is suspended or its email is not verified. |
| `404` | The project or the addressed resource does not exist for this owner. A resource owned by another developer is deliberately indistinguishable from one that never existed. |
| `422` | The request body or query is not valid. `detail` is an array of `{loc, msg, type}` and `loc` names the offending field. |

Plus the account-door errors above. Full list on the [errors page](/docs/errors).

```bash
curl -sS -X GET "https://api.developers.dvaarik.com/console/v2/projects/$PROJECT_ID/webhooks/dead-letters?limit=20" \
  -H "Authorization: Bearer $DVAARIK_TOKEN"
```

---

Source: https://developers.dvaarik.com/docs/api/webhooks · every page as one file: https://developers.dvaarik.com/docs.md
