Skip to content
Last updated

This page explains how to receive real-time notifications from the Kanbert API via webhooks. You can register one or more webhook endpoints that will be called when specific events happen in your workspace.


Overview

  • You register webhooks via the public API.
  • Each webhook belongs to your integration user and is scoped to your domain/workspace.
  • For every delivery we sign the JSON payload with an HMAC SHA-256 signature, using a per-subscription secret.
  • Your endpoint must respond with a 2xx HTTP status within a few seconds to be considered successful.

Webhook Registration & Permissions

All webhook management endpoints require the integration scope. You'll find a detailed description of the endpoints in the API Reference.

Create a webhook via the endpoint to get started.

Webhooks can be scoped by Project or Client. Specific webhooks for task:* and comment:* have to be scoped to a Project.


Delivery, retries, timeouts, and debouncing

  • HTTP method: POST
  • Headers include Content-Type: application/json and a signature header (see below).
  • Timeout: if your endpoint doesn’t respond within ~3 seconds, the attempt is considered failed.
  • Retries: failed deliveries are retried automatically (default total tries: 3) with exponential backoff.
  • Success criteria: any 2xx response code.

Debouncing

  • To avoid noisy bursts when a resource is created/updated multiple times within a very short time window, webhook deliveries are debounced.
  • The payload's data contains the latest model state at the time of delivery.

Special rules within the debounce window:

  • Create absorbs updates: if a resource is created and then updated quickly, a single webhook is sent with event = …:create and data reflecting the latest state after those updates.
  • Delete suppression for pending create: if a delete/force-delete occurs before the debounced delivery of a create, the pending delivery is cancelled and no webhook is sent at all.
  • Update → delete sequence: if the window starts with an update and a delete/force-delete happens before send, two webhooks are delivered in order: first …:update (latest data), then …:delete.

Security: verifying signatures

Every webhook request contains an HMAC SHA-256 signature of the JSON payload, computed with your webhook’s secret.

  • Header name: Signature
  • Algorithm: HMAC-SHA256 over the exact request body (raw JSON string)
  • Key: your signature_secret_key from creation

Example verification (pseudo code):

expected = hex_hmac_sha256(raw_body, signature_secret_key)
received = request.headers['Signature']
if !timingSafeEqual(expected, received): reject(401)

Notes:

  • Always validate the Signature before processing.
  • Use a timing-safe comparison to avoid timing attacks.
  • Only accept application/json and the expected fields.

Payload structure

Each delivery has this base shape. "data" always contains the full model data. "changes" and "previous" only contain changed attributes and are not always present.

{
  "event": "EVENT",
  "resource": {
    "type": "TYPE",
    "id": "ID"
  },
  "triggered_by": "UserData",
  "data": "ModelData"
}

Fields:

  • event: the event name.
  • resource.type: the resource type involved (e.g., contact, company).
  • resource.id: the obfuscated identifier of the resource in our system.
  • triggered_by: the user data that triggered the event.
  • data: Current model data.

Example: contact created

{
  "event": "contact:create",
  "resource": {
    "type": "contact",
    "id": "X9abC3"
  },
  "triggered_by": {
    "id": "yxZ21d",
    "email": "user@emai",
    "first_name": "Ada",
    "last_name": "Lovelace",
    "type": "user"
  },
  "data": {
    "first_name": "Ada",
    "last_name": "Lovelace",
    "email": "ada@example.com"
  }
}

Available events

You can subscribe to one event per webhook

  • contact:create
  • contact:update
  • contact:delete
  • company:create
  • company:update
  • company:delete
  • task:create
  • task:update
  • task:delete
  • comment:create
  • comment:update
  • comment:delete

This list reflects the current WebhookEvent enum used by the API.


Best practices

  • Use a unique, secret URL for your webhook endpoint.
  • Restrict accepted methods to POST and validate Content-Type: application/json.
  • Verify the Signature header using your stored signature_secret_key.
  • Respond quickly (within a couple of seconds); you can enqueue heavy work and return 204 No Content immediately.
  • Implement idempotency by de-duplicating events on your side if needed.
  • Log failed verifications and non-2xx responses for troubleshooting.

Troubleshooting

  • 409 on create: a webhook with the same url and event already exists for your user.
  • Signature mismatches: compare against the exact raw body you received and the correct secret; watch out for whitespace transformations.
  • Long processing times: return early with a 2xx and handle work asynchronously to avoid timeouts and retries.