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

# Webhooks

> Receive real-time events for Quotes and Commitments.

Webhooks deliver commitment lifecycle events to your own endpoints. Each delivery is signed so you can verify it came from Kordless. Webhooks are the integration surface for downstream booking, CRM, or operations systems.

## Event catalog

The public webhook catalog covers the commitment boundary — the moment Kordless stops and your downstream systems take over:

| Event type                | When it fires                                                   |
| ------------------------- | --------------------------------------------------------------- |
| `commitment.created`      | A commitment (hold, deposit, or handoff) is created on a quote. |
| `commitment.confirmed`    | A commitment handoff is confirmed with an external reference.   |
| `commitment.hold_expired` | A hold's expiry time has passed without confirmation.           |
| `commitment.released`     | A commitment is released (canceled or expired).                 |

<Info>
  Webhooks deliver commitment events only. Quote creation, quote review, and Book lifecycle events are not part of the public webhook catalog.
</Info>

## Endpoint lifecycle

Webhook endpoints are managed from the Developers page. Only an organization admin can create, update, or delete endpoints.

### Creating an endpoint

1. Go to **Developers** and open the **Webhooks** tab.
2. Choose **Create endpoint**.
3. Provide an HTTPS URL (12–2048 characters).
4. Select the environment (Test or Live).
5. Optionally subscribe to a subset of event types. If you omit event types, the endpoint receives all events in the catalog.
6. Copy the signing secret — it is shown once.

### Updating an endpoint

You can update the URL, event subscriptions, active status, and description without creating a new endpoint. Changes take effect immediately.

### Rolling the secret

Rolling the signing secret generates a new one immediately — there is no grace period. Update your verification code to use the new secret before or immediately after rolling. Deliveries signed with the old secret will fail verification.

### Deleting an endpoint

Deleting an endpoint immediately stops deliveries. Pending deliveries are cascaded — an in-flight delivery finds no endpoint row and terminates cleanly.

## Delivery

### How deliveries work

1. An event fires (for example, a commitment is created).
2. Kordless records a delivery row and POSTs the event payload to your endpoint.
3. Your endpoint responds with a `2xx` status to acknowledge receipt.
4. If your endpoint returns a non-`2xx` or is unreachable, Kordless retries with exponential backoff.

### Delivery headers

Every delivery includes these headers:

| Header                 | Description                                                                           |
| ---------------------- | ------------------------------------------------------------------------------------- |
| `content-type`         | `application/json`                                                                    |
| `user-agent`           | `Kordless-Webhook/1.0`                                                                |
| `x-kordless-signature` | `sha256=<hex>` — HMAC-SHA256 of the raw body, keyed by the endpoint's signing secret. |
| `x-kordless-event`     | The event type (for example, `commitment.created`).                                   |
| `x-kordless-delivery`  | The unique delivery ID.                                                               |

### Delivery timeout

Kordless waits up to 10 seconds for your endpoint to respond. If your endpoint does not respond within 10 seconds, the delivery is marked as failed and retried.

### Idempotency

Deliveries are unique on (endpoint, event). Two effects can never double-fire the same event to the same endpoint. A redelivered effect for an already-delivered delivery returns `200` without re-POSTing.

### Response excerpt

Kordless records the first 1,024 bytes of your endpoint's response body. This helps with debugging — you can see what your endpoint returned — but no secrets or payloads are leaked.

## Signature verification

Verify every delivery by computing the HMAC-SHA256 of the raw request body with your signing secret and comparing it to the `x-kordless-signature` header:

```javascript theme={null}
import { createHmac } from "node:crypto";

function verifySignature(rawBody, signatureHeader, secret) {
  const expected = createHmac("sha256", secret).update(rawBody).digest("hex");
  const provided = signatureHeader.replace("sha256=", "");
  return expected === provided;
}

// In your handler:
const rawBody = await request.text();
const signature = request.headers.get("x-kordless-signature");
if (!verifySignature(rawBody, signature, WEBHOOK_SECRET)) {
  return new Response("Invalid signature", { status: 401 });
}
const event = JSON.parse(rawBody);
```

<Warning>
  Always verify the signature before processing the event. An unverified webhook could be a spoofed request.
</Warning>

## Payload shape

The payload is a JSON object with the event type, the commitment details, and a timestamp:

```json theme={null}
{
  "event": "commitment.created",
  "commitment_id": "cm_abc123",
  "quote_id": "qt_def456",
  "account_id": "acct_ghi789",
  "state": "held",
  "source": "api",
  "hold_expires_at": "2026-08-05T12:00:00Z",
  "deposit": {
    "state": "pending",
    "amount_cents": 5000,
    "currency": "USD"
  },
  "external_ref": null,
  "created_at": "2026-07-29T12:00:00Z"
}
```

## Environment isolation

Webhook endpoints are scoped to one environment. A Test endpoint receives only Test events; a Live endpoint receives only Live events. When you create an endpoint, you choose the environment it subscribes to.

## Retry behavior

| Scenario                   | What happens                                       |
| -------------------------- | -------------------------------------------------- |
| Endpoint returns `2xx`     | Delivery marked as `ok`. No retry.                 |
| Endpoint returns non-`2xx` | Delivery marked as `failed`. Retried with backoff. |
| Endpoint unreachable       | Delivery marked as `failed`. Retried with backoff. |
| Endpoint deleted           | Delivery skipped. No retry.                        |
| Already delivered          | Skipped. No re-POST.                               |

After the maximum retry attempts, the delivery is dead-lettered and no longer retried.
