Agent skill
Kombo Webhooks Skill
Receive and verify Kombo webhooks. Use when setting up Kombo webhook handlers, debugging X-Kombo-Signature verification, or handling unified HRIS/ATS/LMS events like data-changed, integration-created, integration-state-changed, or issue-status-changed.
Install this skill
npx skills add hookdeck/webhook-skills --skill kombo-webhooks
When to Use This Skill
- How do I receive Kombo webhooks?
- How do I verify the
X-Kombo-Signatureheader? - How do I handle
data-changedorintegration-state-changedevents? - Why is my Kombo webhook signature verification failing?
Verification (core)
Kombo signs the raw request body (JSON with two-space indentation) with HMAC-SHA256. The key is your Kombo Webhook Secret used as its UTF-8 bytes (do not base64-decode it). The digest is sent in X-Kombo-Signature as base64url without padding. Encode your HMAC as base64url, strip any = padding, and compare it with the header text in constant time. Never re-serialize the parsed JSON: use the exact bytes received.
const crypto = require('crypto');
function verifyKomboWebhook(rawBody, signatureHeader, secret) {
if (!signatureHeader) return false;
const expected = crypto.createHmac('sha256', secret).update(rawBody).digest('base64url');
const provided = Buffer.from(signatureHeader.replace(/=+$/, ''));
return provided.length === expected.length && crypto.timingSafeEqual(provided, Buffer.from(expected));
}
import base64, hashlib, hmac
def verify_kombo_webhook(raw_body: bytes, signature_header: "str | None", secret: str) -> bool:
if not signature_header:
return False
expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).digest()
# Compare base64url text (Kombo strips the padding) so trailing junk can't decode to a match
expected_b64 = base64.urlsafe_b64encode(expected).rstrip(b"=")
return hmac.compare_digest(signature_header.rstrip("=").encode(), expected_b64)
Kombo has no webhook-verification SDK, so verify manually. In Express read the body with express.raw({ type: '*/*' }) so any content type is verified.
For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.
Common Event Types
The event name is the type field in the JSON body (there is no event header). Payload shape: { "id": "...", "type": "<event>", "data": { ... } }.
Event (type) | Triggered When |
|---|---|
data-changed | Data changed in Kombo (sync or upstream webhook); debounced to one per 30s |
sync-finished | A sync finished (deprecated; use data-changed) |
integration-created | An integration is created |
integration-deleted | An integration is deleted |
integration-state-changed | Integration state becomes ACTIVE, INVALID or INACTIVE |
connection-flow-failed | An error occurred in the connection flow (can still succeed later) |
issue-status-changed | Kombo raises or resolves an issue (change: raised / resolved) |
assessment:order-received | An assessment is ordered for a candidate in the customer's tool |
Treat type as an open set: acknowledge unknown events with 200.
For the full event reference, see Kombo webhook docs.
Important Headers
| Header | Description |
|---|---|
X-Kombo-Signature | Unpadded base64url HMAC-SHA256 of the raw body; use this to verify |
Environment Variables
KOMBO_WEBHOOK_SECRET=your_kombo_webhook_secret # Kombo dashboard > Configuration > Webhooks (per environment)
Local Development
# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 kombo --path /webhooks/kombo
Reference Materials
- references/overview.md - Kombo webhook concepts and events
- references/setup.md - Dashboard configuration guide
- references/verification.md - Signature verification details