# Webhooks

> The Webhooks operations of the Kabu API: signed event delivery (Standard Webhooks).

Signed event delivery (Standard Webhooks).

Generated from the contract, version `2.0.0-draft.2` ([OpenAPI](/openapi.yaml)). Base URL `https://usekabu.com/api`. Errors are problem details, each [code](/docs/errors) with its own page.

## `POST /v2/webhook_endpoints` · Register an endpoint

- **Auth:** an API key, or a signed-in human's token (the Kabu apps).
- **Scopes:** `webhooks:write`.
- **Signed-in humans:** `admin`.

| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `Idempotency-Key` | header | string, at most 255 characters | no | Retry-safe key. For messages, sessions and responses it binds to the created object for its life; elsewhere for 24 h. |
| `Kabu-Org` | header | string | no | Select a live membership (default personal org); non-member is 404. Human tokens are live-only. Caller user id is distinct from billing owner. |

**Request body**: [WebhookEndpointCreate](#schema-WebhookEndpointCreate).

| Status | Means | Body |
|---|---|---|
| `201` | OK | [WebhookEndpointWithSecret](#schema-WebhookEndpointWithSecret) |
| `4XX` | Error | [Problem](/docs/api/discovery#schema-Problem) |
| `5XX` | Error | [Problem](/docs/api/discovery#schema-Problem) |

```sh
curl -X POST "https://usekabu.com/api/v2/webhook_endpoints" \
  -H "Authorization: Bearer $KABU_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"url":"…","events":["…"]}'
```

## `GET /v2/webhook_endpoints` · List endpoints

- **Auth:** an API key, or a signed-in human's token (the Kabu apps).
- **Scopes:** `webhooks:write`.
- **Signed-in humans:** `admin`.

| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `limit` | query | integer, from 1 to 100 | no |  |
| `after` | query | string | no | Opaque cursor from next_cursor. |
| `Kabu-Org` | header | string | no | Select a live membership (default personal org); non-member is 404. Human tokens are live-only. Caller user id is distinct from billing owner. |

| Status | Means | Body |
|---|---|---|
| `200` | OK | [WebhookEndpointList](#schema-WebhookEndpointList) |
| `4XX` | Error | [Problem](/docs/api/discovery#schema-Problem) |
| `5XX` | Error | [Problem](/docs/api/discovery#schema-Problem) |

```sh
curl "https://usekabu.com/api/v2/webhook_endpoints" \
  -H "Authorization: Bearer $KABU_API_KEY"
```

## `GET /v2/webhook_endpoints/{endpoint_id}` · Get an endpoint

- **Auth:** an API key, or a signed-in human's token (the Kabu apps).
- **Scopes:** `webhooks:write`.
- **Signed-in humans:** `admin`.

| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `endpoint_id` | path | string | yes | A `whe_…` id. |
| `Kabu-Org` | header | string | no | Select a live membership (default personal org); non-member is 404. Human tokens are live-only. Caller user id is distinct from billing owner. |

| Status | Means | Body |
|---|---|---|
| `200` | OK | [WebhookEndpoint](#schema-WebhookEndpoint) |
| `4XX` | Error | [Problem](/docs/api/discovery#schema-Problem) |
| `5XX` | Error | [Problem](/docs/api/discovery#schema-Problem) |

```sh
curl "https://usekabu.com/api/v2/webhook_endpoints/$ENDPOINT_ID" \
  -H "Authorization: Bearer $KABU_API_KEY"
```

## `PATCH /v2/webhook_endpoints/{endpoint_id}` · Update an endpoint

- **Auth:** an API key, or a signed-in human's token (the Kabu apps).
- **Scopes:** `webhooks:write`.
- **Signed-in humans:** `admin`.

| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `endpoint_id` | path | string | yes | A `whe_…` id. |
| `Kabu-Org` | header | string | no | Select a live membership (default personal org); non-member is 404. Human tokens are live-only. Caller user id is distinct from billing owner. |

**Request body**: [WebhookEndpointUpdate](#schema-WebhookEndpointUpdate).

| Status | Means | Body |
|---|---|---|
| `200` | OK | [WebhookEndpoint](#schema-WebhookEndpoint) |
| `4XX` | Error | [Problem](/docs/api/discovery#schema-Problem) |
| `5XX` | Error | [Problem](/docs/api/discovery#schema-Problem) |

```sh
curl -X PATCH "https://usekabu.com/api/v2/webhook_endpoints/$ENDPOINT_ID" \
  -H "Authorization: Bearer $KABU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'
```

## `DELETE /v2/webhook_endpoints/{endpoint_id}` · Delete an endpoint

- **Auth:** an API key, or a signed-in human's token (the Kabu apps).
- **Scopes:** `webhooks:write`.
- **Signed-in humans:** `admin`.

| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `endpoint_id` | path | string | yes | A `whe_…` id. |
| `Kabu-Org` | header | string | no | Select a live membership (default personal org); non-member is 404. Human tokens are live-only. Caller user id is distinct from billing owner. |

| Status | Means | Body |
|---|---|---|
| `204` | Done |  |
| `4XX` | Error | [Problem](/docs/api/discovery#schema-Problem) |
| `5XX` | Error | [Problem](/docs/api/discovery#schema-Problem) |

```sh
curl -X DELETE "https://usekabu.com/api/v2/webhook_endpoints/$ENDPOINT_ID" \
  -H "Authorization: Bearer $KABU_API_KEY"
```

## `POST /v2/webhook_endpoints/{endpoint_id}/rotate_secret` · Rotate the signing secret (old one valid for 24 h)

- **Auth:** an API key, or a signed-in human's token (the Kabu apps).
- **Scopes:** `webhooks:write`.
- **Signed-in humans:** `admin`.

| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `endpoint_id` | path | string | yes | A `whe_…` id. |
| `Idempotency-Key` | header | string, at most 255 characters | no | Retry-safe key. For messages, sessions and responses it binds to the created object for its life; elsewhere for 24 h. |
| `Kabu-Org` | header | string | no | Select a live membership (default personal org); non-member is 404. Human tokens are live-only. Caller user id is distinct from billing owner. |

| Status | Means | Body |
|---|---|---|
| `200` | OK | [WebhookEndpointWithSecret](#schema-WebhookEndpointWithSecret) |
| `4XX` | Error | [Problem](/docs/api/discovery#schema-Problem) |
| `5XX` | Error | [Problem](/docs/api/discovery#schema-Problem) |

```sh
curl -X POST "https://usekabu.com/api/v2/webhook_endpoints/$ENDPOINT_ID/rotate_secret" \
  -H "Authorization: Bearer $KABU_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"
```

## `POST /v2/webhook_endpoints/{endpoint_id}/test` · Send a signed ping now

- **Auth:** an API key, or a signed-in human's token (the Kabu apps).
- **Scopes:** `webhooks:write`.
- **Signed-in humans:** `admin`.

| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `endpoint_id` | path | string | yes | A `whe_…` id. |
| `Idempotency-Key` | header | string, at most 255 characters | no | Retry-safe key. For messages, sessions and responses it binds to the created object for its life; elsewhere for 24 h. |
| `Kabu-Org` | header | string | no | Select a live membership (default personal org); non-member is 404. Human tokens are live-only. Caller user id is distinct from billing owner. |

| Status | Means | Body |
|---|---|---|
| `200` | OK | [WebhookAttempt](#schema-WebhookAttempt) |
| `4XX` | Error | [Problem](/docs/api/discovery#schema-Problem) |
| `5XX` | Error | [Problem](/docs/api/discovery#schema-Problem) |

```sh
curl -X POST "https://usekabu.com/api/v2/webhook_endpoints/$ENDPOINT_ID/test" \
  -H "Authorization: Bearer $KABU_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"
```

## `GET /v2/webhook_endpoints/{endpoint_id}/attempts` · Delivery attempts

- **Auth:** an API key, or a signed-in human's token (the Kabu apps).
- **Scopes:** `webhooks:write`.
- **Signed-in humans:** `admin`.

| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `endpoint_id` | path | string | yes | A `whe_…` id. |
| `limit` | query | integer, from 1 to 100 | no |  |
| `after` | query | string | no | Opaque cursor from next_cursor. |
| `Kabu-Org` | header | string | no | Select a live membership (default personal org); non-member is 404. Human tokens are live-only. Caller user id is distinct from billing owner. |

| Status | Means | Body |
|---|---|---|
| `200` | OK | [WebhookAttemptList](#schema-WebhookAttemptList) |
| `4XX` | Error | [Problem](/docs/api/discovery#schema-Problem) |
| `5XX` | Error | [Problem](/docs/api/discovery#schema-Problem) |

```sh
curl "https://usekabu.com/api/v2/webhook_endpoints/$ENDPOINT_ID/attempts" \
  -H "Authorization: Bearer $KABU_API_KEY"
```

## `POST /v2/webhook_endpoints/{endpoint_id}/replay` · Redeliver events after a checkpoint (bounded job)

`until` defaults to the checkpoint current when the job is created. At most 7 days and 100,000 events per job, one running job per endpoint, delivered at the endpoint's normal rate.

- **Auth:** an API key, or a signed-in human's token (the Kabu apps).
- **Scopes:** `webhooks:write`.
- **Signed-in humans:** `admin`.

| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `endpoint_id` | path | string | yes | A `whe_…` id. |
| `Idempotency-Key` | header | string, at most 255 characters | no | Retry-safe key. For messages, sessions and responses it binds to the created object for its life; elsewhere for 24 h. |
| `Kabu-Org` | header | string | no | Select a live membership (default personal org); non-member is 404. Human tokens are live-only. Caller user id is distinct from billing owner. |

**Request body**: [WebhookReplay](#schema-WebhookReplay).

| Status | Means | Body |
|---|---|---|
| `202` | OK | [Job](#schema-Job) |
| `4XX` | Error | [Problem](/docs/api/discovery#schema-Problem) |
| `5XX` | Error | [Problem](/docs/api/discovery#schema-Problem) |

```sh
curl -X POST "https://usekabu.com/api/v2/webhook_endpoints/$ENDPOINT_ID/replay" \
  -H "Authorization: Bearer $KABU_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"after":"…"}'
```

## `POST your endpoint (event)` · Webhook: An Event, signed per Standard Webhooks

Kabu sends `POST` to each of your webhook endpoints, signed per Standard Webhooks: see [Webhooks](/docs/webhooks) to verify it.

| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `webhook-id` | header | string | yes |  |
| `webhook-timestamp` | header | string | yes |  |
| `webhook-signature` | header | string | yes |  |

**Request body**: [Event](/docs/api/events#schema-Event).

| Status | Means | Body |
|---|---|---|
| `2XX` | Accepted. Anything else is retried with backoff for 3 days. |  |

## Schemas

### WebhookEndpointCreate

| Property | Type | Required | Description |
|---|---|---|---|
| `url` | string (uri) | yes | Public HTTPS only; the resolved address is re-checked on every attempt. |
| `events` | array of string, at least 1 items | yes | Event types, or ["*"]. |
| `description` | string, at most 200 characters | no |  |
| `api_version` | string, matching `^\d{4}-\d{2}-\d{2}$` | no |  |

Unknown properties are refused.

### WebhookEndpointWithSecret

Type: [WebhookEndpoint](#schema-WebhookEndpoint) and object.

### WebhookEndpoint

| Property | Type | Required | Description |
|---|---|---|---|
| `object` | `"webhook_endpoint"` | yes |  |
| `id` | string | yes |  |
| `url` | string | yes |  |
| `events` | array of string | yes |  |
| `status` | string | yes | enabled, disabled or failing (open). |
| `api_version` | string | yes |  |
| `description` | string or null | yes |  |
| `created_at` | [Timestamp](/docs/api/messages#schema-Timestamp) | yes |  |

### WebhookEndpointList

| Property | Type | Required | Description |
|---|---|---|---|
| `object` | `"list"` | yes |  |
| `data` | array of [WebhookEndpoint](#schema-WebhookEndpoint) | yes |  |
| `has_more` | boolean | yes |  |
| `next_cursor` | string or null | yes |  |

### WebhookEndpointUpdate

| Property | Type | Required | Description |
|---|---|---|---|
| `url` | string (uri) | no |  |
| `events` | array of string | no |  |
| `description` | string, at most 200 characters | no |  |
| `enabled` | boolean | no |  |

Unknown properties are refused.

### WebhookAttempt

| Property | Type | Required | Description |
|---|---|---|---|
| `id` | string | yes |  |
| `event` | string | yes |  |
| `checkpoint` | string | no | Opaque events checkpoint at this attempt's event, for choosing a console replay boundary. Never derive it from event id or time. Optional for older attempt records; existing exclusive after semantics are unchanged. |
| `status_code` | integer or null | yes |  |
| `latency_ms` | integer or null | yes |  |
| `ok` | boolean | yes |  |
| `error` | string or null | yes |  |
| `at` | [Timestamp](/docs/api/messages#schema-Timestamp) | yes |  |

### WebhookAttemptList

| Property | Type | Required | Description |
|---|---|---|---|
| `object` | `"list"` | yes |  |
| `data` | array of [WebhookAttempt](#schema-WebhookAttempt) | yes |  |
| `has_more` | boolean | yes |  |
| `next_cursor` | string or null | yes |  |

### WebhookReplay

| Property | Type | Required | Description |
|---|---|---|---|
| `after` | string | yes |  |
| `until` | string | no | A checkpoint. Default: now. |

Unknown properties are refused.

### Job

| Property | Type | Required | Description |
|---|---|---|---|
| `object` | `"job"` | yes |  |
| `id` | string | yes |  |
| `status` | string | yes |  |
| `created_at` | [Timestamp](/docs/api/messages#schema-Timestamp) | yes |  |
