# Webhooks API
Receive signed Klarefi events, send test deliveries, and acknowledge handoff delivery
Source: https://www.klarefi.com/docs/api/webhooks

Klarefi delivers signed events to webhook endpoints registered in the dashboard.
Use webhooks as integration signals, then read the current case or package when
your system needs canonical state.

> **Use your workspace API base URL**
>
> Set `KLAREFI_API_BASE_URL` to the value in **Settings → Developer** before
> using API examples. Current workspaces may use a
> `https://<deployment>.convex.site` URL.

<Mermaid
  title="Webhook delivery flow"
  chart={`sequenceDiagram
  participant K as Klarefi
  participant R as Your receiver
  participant A as Your app

K->>R: POST signed event
R->>R: verify X-Klarefi-Signature
R->>R: dedupe by delivery_id
R-->>K: 2xx acknowledgement
R->>A: enqueue internal work
A->>K: GET /api/v1/cases/{caseId}
K-->>A: current case record
`}
/>

`v1.webhook.test` is emitted only by the test endpoint. Subscription events use
the catalog names in the dashboard, such as `v1.intake.submitted` and
`v1.case.completed`.

## Event shape

```json
{
  "event_id": "evt_01J7W9S4TSQ0T4SH43JYB9W5R8",
  "delivery_id": "whd_01J7W9S52C2ZK1XFY2GM7Z7SAK",
  "event_type": "v1.case.completed",
  "semantic_event_type": "case.completed",
  "schema_version": "v1",
  "created_at": "2026-05-01T12:34:56.000Z",
  "occurred_at": "2026-05-01T12:34:50.000Z",
  "org_id": "org_abc123",
  "case_id": "case_abc123",
  "session_id": null,
  "external_case_id": "claim_12345",
  "external_applicant_id": "applicant_789",
  "external_customer_id": "customer_456",
  "idempotency_key": "session_claim_12345",
  "trace_id": "trace_claim_12345",
  "data": {
    "case_status": "completed"
  }
}
```

Deliveries are at least once. Process duplicates idempotently by `delivery_id`.

## Retry schedule

Klarefi makes up to 5 delivery attempts. After a failed attempt, retries are
scheduled after 60 seconds, 5 minutes, 30 minutes, and 2 hours.

After the final failed attempt, the delivery is marked `failed`. Klarefi emits
`webhook.delivery_failed`, delivered as catalog event
`v1.webhook.delivery_failed` for matching webhook subscriptions, unless the
failed delivery was already a delivery-failed notification. Klarefi also creates
an integration notification. If a `case.completed` handoff delivery exhausts
its attempts, the case handoff state is marked dispatch failed.

## Signature verification

Each delivery includes:

```http
X-Klarefi-Signature: t=1704067200,v1=abc123def456...
```

The signature is:

```text
HMAC-SHA256(signing_secret, timestamp + "." + raw_body)
```

Use the raw request body before JSON parsing.

Install the SDK:

```bash
npm install @klarefi/node
```

```ts
import { constructEvent, KlarefiWebhookSignatureError } from "@klarefi/node";

export async function POST(request: Request) {
  const rawBody = await request.text();

  try {
    const event = constructEvent(
      rawBody,
      request.headers.get("X-Klarefi-Signature"),
      process.env.KLAREFI_WEBHOOK_SECRET!,
    );

    if (event.event_type === "v1.case.completed") {
      // Read the case package and update your system of record.
    }

    return Response.json({ received: true });
  } catch (error) {
    if (error instanceof KlarefiWebhookSignatureError) {
      return new Response("Invalid signature", { status: 401 });
    }
    throw error;
  }
}
```

## Test a webhook receiver

Required scope: `webhooks:test`

```bash
npx klarefi webhooks test \
  --endpoint-url https://your-app.example.com/webhooks/klarefi \
  --signing-secret whsec_your_signing_secret
```

Use this command when you already have `KLAREFI_API_KEY` configured. The HTTP
equivalent is:

```bash
curl -X POST "$KLAREFI_API_BASE_URL/api/v1/webhooks/test" \
  -H "Authorization: Bearer $KLAREFI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "endpoint_url": "https://your-app.example.com/webhooks/klarefi",
    "signing_secret": "whsec_your_signing_secret"
  }'
```

```json
{
  "success": true,
  "status_code": 200
}
```

To verify a captured payload locally:

```bash
npx klarefi webhooks verify \
  --payload payload.json \
  --signature "t=1704067200,v1=..." \
  --secret whsec_your_signing_secret
```

## Acknowledge a delivery

Required scope: `webhooks:acknowledge`

With an existing `Klarefi` client:

```ts
await klarefi.webhooks.acknowledge("whd_123", {
  acknowledgement_id: "ack_claim_12345",
});
```

```bash
curl -X POST "$KLAREFI_API_BASE_URL/api/v1/webhooks/deliveries/whd_123/ack" \
  -H "Authorization: Bearer $KLAREFI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "acknowledgement_id": "ack_claim_12345" }'
```

```json
{
  "acknowledged": true,
  "already_acknowledged": false,
  "delivery_id": "whd_123",
  "case_id": "case_abc123",
  "handoff_state": "acknowledged"
}
```

Acknowledgement is useful when your system wants Klarefi to record that a
decision-ready handoff has been received.
