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

# Webhooks

> Receive real-time notifications when events occur in your Klara applications

Webhooks allow you to receive HTTP POST notifications when key events happen in your Klara applications. Instead of polling the API, configure a webhook endpoint and Klara will push events to you in real time.

## Supported events

| Event                           | Trigger                                                                                                  |
| ------------------------------- | -------------------------------------------------------------------------------------------------------- |
| `application.opened`            | End user opens the Klara portal for the first time                                                       |
| `application.company_selected`  | End user picks their company from the registry search                                                    |
| `application.stage_completed`   | End user completes a stage of the application form                                                       |
| `application.submitted`         | End user submits their application                                                                       |
| `application.decision_received` | A provider decision has been recorded via the [Record Decision](/api-reference/record-decision) endpoint |
| `webhook.test`                  | Manually sent test ping from the dashboard                                                               |

## Payload format

All webhook payloads follow the same envelope structure:

```json theme={null}
{
  "id": "evt_abc123",
  "type": "application.submitted",
  "timestamp": "2026-01-15T10:30:00Z",
  "applicationId": "550e8400-e29b-41d4-a716-446655440000",
  "organizationId": "org_123",
  "data": {
    // Event-specific fields
  }
}
```

| Field            | Type     | Description                                   |
| ---------------- | -------- | --------------------------------------------- |
| `id`             | `string` | Unique event ID (use for idempotency)         |
| `type`           | `string` | Event type (e.g., `application.submitted`)    |
| `timestamp`      | `string` | ISO 8601 timestamp of when the event occurred |
| `applicationId`  | `string` | UUID of the related application               |
| `organizationId` | `string` | UUID of your organization                     |
| `data`           | `object` | Event-specific payload (see below)            |

### application.opened

Fired when the end user opens their Klara portal link for the first time.

```json theme={null}
{
  "id": "evt_abc123",
  "type": "application.opened",
  "timestamp": "2026-01-15T10:30:00Z",
  "applicationId": "550e8400-e29b-41d4-a716-446655440000",
  "organizationId": "org_123",
  "data": {
    "applicationRef": "APP-001",
    "openedAt": "2026-01-15T10:30:00Z"
  }
}
```

### application.company\_selected

Fired when the end user picks their company from the registry name search. The application's `company_name` and `company_number` now carry the registry-validated identity.

```json theme={null}
{
  "id": "evt_jkl012",
  "type": "application.company_selected",
  "timestamp": "2026-01-15T10:32:00Z",
  "applicationId": "550e8400-e29b-41d4-a716-446655440000",
  "organizationId": "org_123",
  "data": {
    "company_name": "ACME FINANCIAL LTD",
    "company_number": "12345678",
    "country": "GB",
    "source": "companies_house",
    "previous_company_name": "Acme Financial"
  }
}
```

#### data fields

| Field                   | Type                  | Description                                                    |
| ----------------------- | --------------------- | -------------------------------------------------------------- |
| `company_name`          | `string`              | Registry-validated company name                                |
| `company_number`        | `string`              | Company registration number                                    |
| `country`               | `string`              | ISO 3166-1 alpha-2 country code                                |
| `source`                | `string`              | Registry the identity came from (e.g., `companies_house`)      |
| `previous_company_name` | `string \| undefined` | Name on the application before the selection, when it differed |

### application.stage\_completed

Fired each time a stage of the application form transitions to complete — for example when the end user finishes the company details step and moves on. Use it to track progress in real time instead of polling.

```json theme={null}
{
  "id": "evt_mno345",
  "type": "application.stage_completed",
  "timestamp": "2026-01-15T11:45:00Z",
  "applicationId": "550e8400-e29b-41d4-a716-446655440000",
  "organizationId": "org_123",
  "data": {
    "application_id": "550e8400-e29b-41d4-a716-446655440000",
    "config_id": "kyc-standard",
    "status": "in_progress",
    "stage": {
      "id": "company",
      "name": "Company Details",
      "index": 0,
      "total": 5
    },
    "stages_completed": ["company"],
    "stage_data": {
      "fields": {
        "company_name": "Acme Financial Ltd",
        "company_number": "12345678",
        "company_country": "GB"
      }
    }
  }
}
```

#### data fields

| Field              | Type       | Description                                                                                                                                                         |
| ------------------ | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `application_id`   | `string`   | UUID of the application                                                                                                                                             |
| `config_id`        | `string`   | Configuration ID used for this application                                                                                                                          |
| `status`           | `string`   | Application status at the time of the event                                                                                                                         |
| `stage`            | `object`   | The stage that just completed: `id`, `name`, `index` (zero-based position), `total` (number of stages)                                                              |
| `stages_completed` | `string[]` | **All** currently complete stage IDs, not just this one — a stage completed before your first event (e.g., pre-filled by registry enrichment) is still visible here |
| `stage_data`       | `object`   | The completed stage's content (see below)                                                                                                                           |

#### stage\_data

The shape depends on the stage type:

* **Content stages** (e.g., `company`, `questionnaire`) carry `stage_data.fields` — the stage's visible field values. Sensitive fields are excluded, same as `application.submitted`.
* **People stages** (`directors`, `owners`, `representatives`) carry `stage_data.people` — confirmed people for that stage, each with `full_name`, `first_name`, `last_name`, `roles`, and `nationality` when known.

```json theme={null}
{
  "stage_data": {
    "people": [
      {
        "full_name": "Jane Smith",
        "first_name": "Jane",
        "last_name": "Smith",
        "nationality": "GB",
        "roles": ["director", "ubo"]
      }
    ]
  }
}
```

<Note>
  Stages can complete more than once — if the end user edits a completed stage and re-completes it, another event fires. The confirmation step does not emit `stage_completed`; listen for `application.submitted` instead.
</Note>

### application.submitted

Fired when the end user submits their application.

```json theme={null}
{
  "id": "evt_def456",
  "type": "application.submitted",
  "timestamp": "2026-01-16T14:20:00Z",
  "applicationId": "550e8400-e29b-41d4-a716-446655440000",
  "organizationId": "org_123",
  "data": {
    "applicationId": "550e8400-e29b-41d4-a716-446655440000",
    "submittedAt": "2026-01-16T14:20:00Z",
    "fields": {
      "company_name": "Acme Ltd",
      "company_number": "12345678",
      "company_country": "GB",
      "industry": "technology",
      "expected_monthly_volume": 50000
      // ... additional fields vary by client configuration.
      // Sensitive fields are excluded.
    },
    "people": [
      {
        "full_name": "Jane Smith",
        "nationality": "GB",
        "roles": ["director", "ubo"]
      }
    ]
  }
}
```

#### data fields

| Field           | Type     | Description                                                 |
| --------------- | -------- | ----------------------------------------------------------- |
| `applicationId` | `string` | UUID of the submitted application                           |
| `submittedAt`   | `string` | ISO 8601 submission timestamp                               |
| `fields`        | `object` | Non-sensitive application fields (see note below)           |
| `people`        | `array`  | Confirmed people — `full_name`, `nationality`, `roles` only |

### application.decision\_received

Fired when a downstream provider decision is reported to Klara via the [Record Decision](/api-reference/record-decision) endpoint.

```json theme={null}
{
  "id": "evt_ghi789",
  "type": "application.decision_received",
  "timestamp": "2026-04-20T14:32:05Z",
  "applicationId": "550e8400-e29b-41d4-a716-446655440000",
  "organizationId": "org_123",
  "data": {
    "application_id": "550e8400-e29b-41d4-a716-446655440000",
    "decision_type": "approved",
    "decided_at": "2026-04-20T14:32:00Z",
    "received_at": "2026-04-20T14:32:05Z"
  }
}
```

#### data fields

| Field            | Type     | Description                                                                     |
| ---------------- | -------- | ------------------------------------------------------------------------------- |
| `application_id` | `string` | UUID of the application                                                         |
| `decision_type`  | `string` | One of `approved`, `rejected`, `referred`                                       |
| `decided_at`     | `string` | ISO 8601 timestamp supplied by the caller — when the provider made the decision |
| `received_at`    | `string` | ISO 8601 timestamp when Klara recorded the decision                             |

### webhook.test

Sent when you click **Send test** in the dashboard.

```json theme={null}
{
  "id": "evt_test789",
  "type": "webhook.test",
  "timestamp": "2026-01-15T12:00:00Z",
  "applicationId": null,
  "organizationId": "org_123",
  "data": {
    "message": "This is a test webhook from Klara."
  }
}
```

## Configuring webhooks

1. Go to **Settings → Integrations → Webhooks** in your Klara dashboard
2. Click **Add Endpoint**
3. Enter your HTTPS URL and select the events you want to receive
4. Copy the signing secret — you'll need it to verify payloads

<Note>
  You can configure up to **5 webhook endpoints** per organization. Webhook URLs must use HTTPS.
</Note>

## Delivery & retries

Klara delivers webhooks asynchronously. If your endpoint is unavailable, delivery is retried automatically:

| Property         | Value                              |
| ---------------- | ---------------------------------- |
| **Timeout**      | 30 seconds per attempt             |
| **Max attempts** | 5 (1 initial + 4 retries)          |
| **Backoff**      | Exponential (\~12s, 2.5m, 30m, 6h) |
| **Success**      | Any HTTP `2xx` response            |

### Auto-disable

If an endpoint fails **5 consecutive deliveries** (across any events), it is automatically disabled. You must re-enable it manually from the dashboard. The failure counter resets on any successful delivery.

## Verifying signatures

Every webhook includes HMAC-SHA256 signatures so you can verify payloads are genuinely from Klara. See the [Webhook Signatures](/webhook-signatures) guide for implementation details.

## Best practices

* **Respond quickly** — return a `200` response before processing the event. Use a queue for heavy work.
* **Handle duplicates** — use the `id` field for idempotency. The same event may be delivered more than once.
* **Verify signatures** — always validate the `X-Klara-Signature` header before trusting the payload.
* **Use HTTPS** — webhook URLs must use HTTPS. HTTP URLs are rejected.
