---
title: Webhook Endpoints
description: Generated HTTP reference for the 10 operations the published OpenAPI document describes under webhook-endpoints.
---

# Webhook Endpoints

This page is generated from the published OpenAPI document. It is complete with respect to that document and says nothing about surfaces the document does not describe yet. See [what is generated here](/docs/api/reference) for what that means.

Base URL: `https://api.codespar.dev`

Every operation below requires a Bearer token. See [Authentication](/docs/concepts/authentication).

### GET `/v1/webhook-endpoints`

List triggers

**Query parameters**

| Name | Type | Required | Description |
|---|---|---|---|
| `before` | `string` | no | — |
| `event` | `string` | no | — |
| `limit` | `integer` | no | — |
| `server_id` | `string` | no | — |
| `status` | `"active"` \| `"paused"` \| `"error"` | no | — |

**Responses**

| Status | Body | Description |
|---|---|---|
| `200` | object | OK |
| `400` | object | Bad Request — the body or query did not match the schema. |

**Response `200`**

| Field | Type | Required | Description |
|---|---|---|---|
| `next_before` | `string,null` | yes | — |
| `triggers` | array of [Trigger](/docs/api/reference/schemas#trigger) | yes | — |

**Example response**

```json
{
  "triggers": [
    {
      "id": "trg_0000000000000000",
      "org_id": "org_0000000000000000",
      "name": "Example",
      "event": "string",
      "webhook_url": "https://example.com/hook",
      "status": "string",
      "total_runs": 0,
      "created_at": "2026-01-15T12:00:00.000Z",
      "signing_enabled": true
    }
  ]
}
```

**Example request**

```bash
curl -X GET https://api.codespar.dev/v1/webhook-endpoints \
  -H "Authorization: Bearer $CODESPAR_API_KEY"
```

### POST `/v1/webhook-endpoints`

Subscribe a webhook to an event

**Request body**

| Field | Type | Required | Description |
|---|---|---|---|
| `event` | `string` | yes | — |
| `name` | `string` | yes | — |
| `server_id` | `string` | no | — |
| `webhook_url` | `string (uri)` | yes | — |

**Responses**

| Status | Body | Description |
|---|---|---|
| `201` | [TriggerCreated](/docs/api/reference/schemas#triggercreated) | OK |
| `400` | object | Bad Request — the body or query did not match the schema. |

**Example request**

```bash
curl -X POST https://api.codespar.dev/v1/webhook-endpoints \
  -H "Authorization: Bearer $CODESPAR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
       "name": "Example",
       "event": "string",
       "server_id": "srv_0000000000000000",
       "webhook_url": "https://example.com/hook"
     }'
```

### POST `/v1/webhook-endpoints/deliveries/{delivery_id}/redeliver`

Queue a delivery to be sent again

**Path parameters**

| Name | Type | Required | Description |
|---|---|---|---|
| `delivery_id` | `string` | yes | Delivery id as decimal digits, the string form of the `bigserial` column. Up to 19 digits, which is the width of a signed 64 bit maximum; anything else is refused with 400 `invalid_delivery_id` before the database is touched. |

**Responses**

| Status | Body | Description |
|---|---|---|
| `202` | object | Accepted |
| `400` | object | `delivery_id` is not 1 to 19 decimal digits. |
| `404` | object | No delivery with that id whose webhook endpoint belongs to the caller's organization and project. |

**Response `202`**

| Field | Type | Required | Description |
|---|---|---|---|
| `event_id` | `string` | yes | — |
| `redelivery_id` | `string` | yes | Id of the NEW pending delivery row, bigint as string. |
| `trigger_id` | `string` | yes | — |
| `trigger_status` | `string` | yes | Status of the endpoint the delivery belongs to, read before the insert. Check it: the drain only claims rows whose endpoint is `active`, so a redelivery queued against a `paused` or `error` endpoint sits there until a patch reactivates it. |

**Example response**

```json
{
  "redelivery_id": "redelivery_0000000000000000",
  "trigger_id": "trg_0000000000000000",
  "event_id": "event_0000000000000000",
  "trigger_status": "string"
}
```

**Example request**

```bash
curl -X POST https://api.codespar.dev/v1/webhook-endpoints/deliveries/{delivery_id}/redeliver \
  -H "Authorization: Bearer $CODESPAR_API_KEY"
```

### POST `/v1/webhook-endpoints/retry-pending`

Drain the retry queue for this project

**Responses**

| Status | Body | Description |
|---|---|---|
| `200` | object | OK |

**Response `200`**

| Field | Type | Required | Description |
|---|---|---|---|
| `dead` | `integer` | yes | Attempts that failed on the last allowed attempt and were dead lettered. |
| `delivered` | `integer` | yes | — |
| `failed` | `integer` | yes | Attempts that failed and are scheduled for another retry. |
| `redispatched` | `integer` | yes | — |
| `scanned` | `integer` | yes | Rows the claim actually took, which is also the ceiling on the four counters below. |

**Example response**

```json
{
  "scanned": 0,
  "redispatched": 0,
  "delivered": 0,
  "failed": 0,
  "dead": 0
}
```

**Example request**

```bash
curl -X POST https://api.codespar.dev/v1/webhook-endpoints/retry-pending \
  -H "Authorization: Bearer $CODESPAR_API_KEY"
```

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

Read one webhook endpoint

**Path parameters**

| Name | Type | Required | Description |
|---|---|---|---|
| `id` | `string` | yes | Endpoint id, `trg_` followed by a 16 character nanoid, as minted by the create call. |

**Responses**

| Status | Body | Description |
|---|---|---|
| `200` | object | OK |
| `404` | object | No webhook endpoint with that id in the caller's organization and project. |

**Response `200`**

| Field | Type | Required | Description |
|---|---|---|---|
| `created_at` | `string (date-time)` | yes | — |
| `event` | `string` | yes | The event name this endpoint subscribes to, dot separated lowercase. |
| `id` | `string` | yes | — |
| `last_run_at` | `string,null (date-time)` | yes | Timestamp of the last DELIVERED attempt, on the same rule as `total_runs`. |
| `name` | `string` | yes | — |
| `org_id` | `string` | yes | — |
| `project_id` | `string,null` | yes | Nullable in the row type this response is serialized from; a later migration sets the column NOT NULL, so an endpoint created since then always carries one. |
| `server_id` | `string,null` | yes | — |
| `signing_enabled` | `boolean` | yes | Whether a signing secret exists. The secret itself is never read back: the serializer drops the vault reference and reports only this boolean. |
| `status` | `string` | yes | `active`, `paused` or `error`. Left as an open string because the column is `text` with no CHECK constraint: `active` and `paused` are what a patch may set, and `error` is what the dispatcher writes on its own when an endpoint auto-pauses after enough consecutive dead deliveries. |
| `total_runs` | `integer` | yes | Deliveries that landed. A failed or dead attempt does not count, which is why this can sit at 0 while the deliveries listing is full of rows. |
| `webhook_url` | `string` | yes | — |

**Example response**

```json
{
  "id": "obj_0000000000000000",
  "org_id": "org_0000000000000000",
  "name": "Example",
  "event": "string",
  "webhook_url": "https://example.com/hook",
  "status": "string",
  "total_runs": 0,
  "created_at": "2026-01-15T12:00:00.000Z",
  "signing_enabled": true
}
```

**Example request**

```bash
curl -X GET https://api.codespar.dev/v1/webhook-endpoints/{id} \
  -H "Authorization: Bearer $CODESPAR_API_KEY"
```

### DELETE `/v1/webhook-endpoints/{id}`

Delete a webhook endpoint

**Path parameters**

| Name | Type | Required | Description |
|---|---|---|---|
| `id` | `string` | yes | Endpoint id, `trg_` followed by a 16 character nanoid, as minted by the create call. |

**Responses**

| Status | Body | Description |
|---|---|---|
| `204` | — | No Content |
| `404` | object | No webhook endpoint with that id in the caller's organization and project. |
| `503` | object | The signing secret could not be purged, so nothing was deleted. Retriable. |

**Example request**

```bash
curl -X DELETE https://api.codespar.dev/v1/webhook-endpoints/{id} \
  -H "Authorization: Bearer $CODESPAR_API_KEY"
```

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

List recent delivery attempts

**Path parameters**

| Name | Type | Required | Description |
|---|---|---|---|
| `id` | `string` | yes | Endpoint id, `trg_` followed by a 16 character nanoid, as minted by the create call. |

**Query parameters**

| Name | Type | Required | Description |
|---|---|---|---|
| `limit` | `integer` | no | Rows to return. Clamped into the range 1 to 200: a larger or smaller value is reduced or raised rather than refused, and a value that does not parse as a number falls back to 50. |

**Responses**

| Status | Body | Description |
|---|---|---|
| `200` | object | OK |
| `404` | object | No webhook endpoint with that id in the caller's organization and project. |

**Response `200`**

| Field | Type | Required | Description |
|---|---|---|---|
| `deliveries` | array of object | yes | — |

**Example response**

```json
{
  "deliveries": [
    {
      "id": "obj_0000000000000000",
      "trigger_id": "trg_0000000000000000",
      "event_id": "event_0000000000000000",
      "attempt": 0,
      "status": "string",
      "created_at": "2026-01-15T12:00:00.000Z"
    }
  ]
}
```

**Example request**

```bash
curl -X GET https://api.codespar.dev/v1/webhook-endpoints/{id}/deliveries \
  -H "Authorization: Bearer $CODESPAR_API_KEY"
```

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

Inspect one delivery attempt

**Path parameters**

| Name | Type | Required | Description |
|---|---|---|---|
| `delivery_id` | `string` | yes | Delivery id as decimal digits, the string form of the `bigserial` column. Up to 19 digits, which is the width of a signed 64 bit maximum; anything else is refused with 400 `invalid_delivery_id` before the database is touched. |
| `id` | `string` | yes | Endpoint id, `trg_` followed by a 16 character nanoid, as minted by the create call. |

**Responses**

| Status | Body | Description |
|---|---|---|
| `200` | object | OK |
| `400` | object | `delivery_id` is not 1 to 19 decimal digits. |
| `404` | object | Either no such webhook endpoint in the caller's organization and project, or no delivery with that id belonging to it. Both answer the same code, on purpose. |

**Response `200`**

| Field | Type | Required | Description |
|---|---|---|---|
| `delivery` | object | yes | — |
| `event` | object | yes | — |
| `request` | object | yes | — |
| `response` | object | yes | — |

**Example response**

```json
{
  "delivery": {
    "id": "obj_0000000000000000",
    "trigger_id": "trg_0000000000000000",
    "event_id": "event_0000000000000000",
    "attempt": 0,
    "status": "string",
    "created_at": "2026-01-15T12:00:00.000Z"
  },
  "request": {
    "headers": {
      "Content-Type": "string",
      "X-CodeSpar-Event": "string",
      "X-CodeSpar-Event-Id": "xcodesparevent_0000000000000000",
      "X-CodeSpar-Trigger-Id": "xcodespartrigger_0000000000000000",
      "X-CodeSpar-Attempt": "string"
    },
    "body": "string"
  },
  "response": {},
  "event": {
    "id": "obj_0000000000000000",
    "source": "string",
    "event_type": "string",
    "payload": {},
    "received_at": "2026-01-15T12:00:00.000Z"
  }
}
```

**Example request**

```bash
curl -X GET https://api.codespar.dev/v1/webhook-endpoints/{id}/deliveries/{delivery_id} \
  -H "Authorization: Bearer $CODESPAR_API_KEY"
```

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

List dead lettered deliveries

**Path parameters**

| Name | Type | Required | Description |
|---|---|---|---|
| `id` | `string` | yes | Endpoint id, `trg_` followed by a 16 character nanoid, as minted by the create call. |

**Query parameters**

| Name | Type | Required | Description |
|---|---|---|---|
| `limit` | `integer` | no | Rows to return. Clamped into the range 1 to 200: a larger or smaller value is reduced or raised rather than refused, and a value that does not parse as a number falls back to 50. |

**Responses**

| Status | Body | Description |
|---|---|---|
| `200` | object | OK |
| `404` | object | No webhook endpoint with that id in the caller's organization and project. |

**Response `200`**

| Field | Type | Required | Description |
|---|---|---|---|
| `dead_letters` | array of object | yes | — |

**Example response**

```json
{
  "dead_letters": [
    {
      "id": "obj_0000000000000000",
      "event_id": "event_0000000000000000",
      "attempt": 0,
      "created_at": "2026-01-15T12:00:00.000Z"
    }
  ]
}
```

**Example request**

```bash
curl -X GET https://api.codespar.dev/v1/webhook-endpoints/{id}/dlq \
  -H "Authorization: Bearer $CODESPAR_API_KEY"
```

### POST `/v1/webhook-endpoints/{id}/rotate-secret`

Mint a new signing secret for a webhook endpoint

**Path parameters**

| Name | Type | Required | Description |
|---|---|---|---|
| `id` | `string` | yes | Endpoint id, `trg_` followed by a 16 character nanoid, as minted by the create call. |

**Responses**

| Status | Body | Description |
|---|---|---|
| `200` | object | OK |
| `404` | object | No webhook endpoint with that id in the caller's organization and project. |
| `503` | object | The new secret could not be persisted, so no rotation occurred and the previous secret is still in force. Retriable. |

**Response `200`**

| Field | Type | Required | Description |
|---|---|---|---|
| `secret` | `string` | yes | The new signing secret in plaintext, 32 random bytes as hex, shown EXACTLY ONCE. Later reads expose only `signing_enabled`, and a lost secret is replaced by rotating again. |
| `signing_enabled` | `true` | yes | — |
| `trigger_id` | `string` | yes | — |

**Example response**

```json
{
  "trigger_id": "trg_0000000000000000",
  "signing_enabled": true,
  "secret": "string"
}
```

**Example request**

```bash
curl -X POST https://api.codespar.dev/v1/webhook-endpoints/{id}/rotate-secret \
  -H "Authorization: Bearer $CODESPAR_API_KEY"
```

