Webhook Use Case

Use our webhooks to be notified about finished syncs. This allows you to fetch any changes that occurred during a sync and to keep your database up to date without scheduling/polling. Easily test webhooks with Webhook.site.

Enable Webhooks

You can configure your Kombo webhooks in the dashboard directly in the webhooks tab. After creating the first webhook, a random webhook secret will be generated and shown. You can learn how to use it in Validate the data.


Data Schema

Our webhooks follow this schema:

  "id": "5gjAtURLPbnTiwgkaBfiA3WJ",
  "type": "sync-finished",
  "data": {
    "sync_id": "B89SCXXho7Yw8PGo8AKJxLn4",
    "sync_state": "SUCCEEDED",
    "sync_started_at": "2021-09-01T12:00:00.000Z",
    "sync_ended_at": "2021-09-01T12:30:00.000Z",
    "sync_duration_seconds": 1800,
    "integration_id": "personio:CBNMt7dSNCzBdnRTx87dev4E",
    "integration_tool": "personio",
    "integration_category": "HRIS",
    "end_user": {
      "origin_id": "36123",
      "creator_email": "user@example.com",
      "organization_name": "Acme, Inc."
    "log_url": "https://app.kombo.dev/env/production/logs/C3xUo6XAsB2sbKC7M1gyXaRX"

Possible values for the sync_state on the sync-finished webhook:

enum SyncState = {
  "SUCCEEDED", // Sync completed successfully
  "CANCELLED", // Sync was cancelled
  "FAILED", // Failed with unrecoverable errors
  "PARTIALLY_FAILED", // Failed with partial errors
  "AUTHENTICATION_FAILED", // Authentication failed
  "TIMED_OUT", // Timed out before completion

Validate the data

Anyone could post data to your webhook URL. That’s why we’re signing each request with a secret specific to your Kombo account. The secret is called Kombo Webhook Secret and will be sent to you when we activate Webhooks for you.

Each valid webhook POST from us will include the X-Kombo-Signature header. Validate it by:

  1. Using HMAC-SHA256 to sign the request body with your Kombo Webhook Secret
  2. Digest with base64url encoding.

Our ‘base64url’ encoding does not include the optional trailing ’=’ padding characters.

Here are some examples of how you could achieve this in a few different languages:

import { createHmac } from 'node:crypto'

const signatureHeader = req.headers['x-kombo-signature']

// Get the raw UTF-8-encoded string body because this was used for signing
const body = request.body

// Or if you use a framework that parses the body for you, you can use this.
// Note: This is not the same as JSON.stringify(request.body)
const body = JSON.stringify(request.body, null, 2)

const signature = createHmac('sha256', KOMBO_WEBHOOK_SECRET)
.update(body, 'utf8')

const isValidRequest = signature === signatureHeader

You have to use exactly the same string we use for signing. This means that you should optimally use the raw body. Alternatively, you can manually turn the parsed body into a string again. In that case, make sure to use the exact same encoding and indentation (e.g., JSON.stringify(body, null, 2) in JavaScript).

Testing webhooks

The list of all created webhook has two buttons on the right. One is for deleting the webhook, the other is for sending a test request. This test request will contain dummy data, but will use the correct data schema.

Be careful when using this feature in production! It will send invalid data to your webhook.

Testing locally

For local development you can use a service like localtunnel or ngrok. These tools allow you to expose a local port through a public URL provided by them. The setup is very straightforward: Once you’ve started a tunnel, simply add the provided URL as a webhook in the Kombo dashboard. You can then send test requests to validate that your endpoint is working properly.