# Scalekit Webhooks

[Scalekit](https://scalekit.com) provides authentication for B2B SaaS (organizations, SSO, SCIM directory sync, RBAC, sessions) and AI agents (connected accounts, tool calling). Its webhooks report organization, user, directory, SSO, role/permission and connected-account events. Every delivery is signed with **Standard Webhooks** (`webhook-id`, `webhook-timestamp`, `webhook-signature`).

## When to Use This Skill

* How do I receive Scalekit webhooks?
* How do I verify a Scalekit webhook signature?
* Where do I find my Scalekit webhook signing secret (`whsec_...`)?
* How do I handle `organization.directory.user_created` or `organization.sso_enabled` events?
* Why does Scalekit webhook verification fail?
* Why do Hookdeck retries of a Scalekit webhook fail verification after a few minutes?

## Verification (core)

| Header | Value |
| --- | --- |
| `webhook-id` | Delivery ID, the same on every retry |
| `webhook-timestamp` | Unix seconds of this attempt |
| `webhook-signature` | One or more space-separated `v1,<base64 HMAC-SHA256>` entries |

Signed content is `{webhook-id}.{webhook-timestamp}.{raw body}`. The key is the base64-**decoded** part of the secret after `whsec_`. Verify with the `standardwebhooks` library (npm, PyPI), which compares every `v1` entry in constant time:

```javascript
const { Webhook } = require('standardwebhooks');

// rawBody: exact request body string, NOT re-serialised JSON
const prefix = req.get('webhook-id') ? 'webhook' : 'svix'; // fall back to svix-* like Hookdeck
const event = new Webhook(process.env.SCALEKIT_WEBHOOK_SECRET).verify(rawBody, {
  'webhook-id': req.get(`${prefix}-id`),
  'webhook-timestamp': req.get(`${prefix}-timestamp`),
  'webhook-signature': req.get(`${prefix}-signature`),
}); // returns the parsed event; throws → respond 400

```

Python: `from standardwebhooks.webhooks import Webhook`, then `Webhook(secret).verify(await request.body(), {...same three keys...})`. Scalekit's own SDKs offer `verifyWebhookPayload(secret, headers, payload)`; the library above uses the same algorithm.

**Timestamp window.** The library always enforces a fixed 5-minute window. **Hookdeck retries and replays resend the original timestamp, so the library rejects late retries after 5 minutes.** A manual verifier (HMAC-SHA256 over `{id}.{timestamp}.{raw body}`, every `v1,` entry compared in constant time) where the window can be dropped or widened is in [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/scalekit-webhooks/references/verification.md).

> **For complete handlers with tests**, see [examples/express/](https://github.com/hookdeck/webhook-skills/tree/main/skills/scalekit-webhooks/examples/express/), [examples/nextjs/](https://github.com/hookdeck/webhook-skills/tree/main/skills/scalekit-webhooks/examples/nextjs/), [examples/fastapi/](https://github.com/hookdeck/webhook-skills/tree/main/skills/scalekit-webhooks/examples/fastapi/).

## Event Envelope

```jsonc
{ "spec_version": "1", "id": "evt_100000000000000001", "object": "DirectoryUser",
  "environment_id": "env_100000000000000001", "occurred_at": "2026-10-01T09:30:00.000Z",
  "organization_id": "org_100000000000000001", "type": "organization.directory.user_created",
  "data": { "id": "diruser_100000000000000001", "email": "kai@example.com", "active": true } }

```

The event type is the top-level `type`. `id` (`evt_…`) is unique per event and differs from `webhook-id`; `object` names the data's type and is omitted on domain events; `organization_id` is not sent on `user.*` events. Dedupe on `webhook-id` or `id`, not `data.id`.

## Common Event Types

| Event | Fires when |
| --- | --- |
| `organization.created` / `organization.updated` / `organization.deleted` | An organization changes |
| `organization.directory.user_created` / `_updated` / `_deleted` | A SCIM directory user is provisioned, changed or removed |
| `organization.directory.group_created` / `_updated` / `_deleted` | A directory group changes |
| `organization.directory_enabled` / `organization.directory_disabled` | A directory is switched on or off |
| `organization.sso_enabled` / `organization.sso_disabled` | An SSO connection is switched on or off |
| `user.signup` / `user.login` / `user.logout` | A user signs up, logs in or out |
| `user.organization_membership_created` / `_updated` / `_deleted` | A membership changes |
| `role.created` / `role.updated` | A role changes |

> **All documented types** (43; the list may not be exhaustive): [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/scalekit-webhooks/references/overview.md) and [Scalekit's webhook guide](https://docs.scalekit.com/guides/webhooks-best-practices/).

## Delivery Semantics

* Return a `2xx` within **30 seconds** (`201` recommended); enqueue and process asynchronously, but verify before queueing. Missing headers or a bad signature: return `400` (or `401`).
* Any other status, including `4xx`, is retried: 8 attempts (immediately, 5 s, 5 min, 30 min, 2 h, 5 h, 10 h, 10 h), about 27 h 35 min. An endpoint failing every delivery for 5 days is disabled.
* At least once and possibly out of order: dedupe, and compare `occurred_at` before overwriting newer data.
* No challenge or validation request is documented.

## Environment Variables

```bash
SCALEKIT_WEBHOOK_SECRET=whsec_xxxxx   # full whsec_ value; handlers fail closed (500) without it

```

One signing secret per endpoint, from **Dashboard > Webhooks > any endpoint**.

## Local Development

```bash
# Express / Next.js (use 8000 for FastAPI). No install or account required.
npx hookdeck-cli listen 3000 scalekit --path /webhooks/scalekit

```

## Reference Materials

* [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/scalekit-webhooks/references/overview.md) - Envelope, event types, delivery and ordering
* [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/scalekit-webhooks/references/setup.md) - Dashboard setup, signing secret, retries, testing
* [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/scalekit-webhooks/references/verification.md) - Library and manual verification, timestamp window, debugging