Skip to main content
Webhooks let your backend systems react to Knock2 events the moment they happen — no polling required. When a company is identified on your site, a lead score changes, or a play fires, Knock2 sends an HTTP POST request to the URL you register, carrying a signed JSON payload with everything you need to update your CRM, trigger a sales alert, or kick off an automation.

Supported Events

Create a Webhook

Register a new webhook endpoint by POSTing to /v1/webhooks with your destination URL and the list of events you want to subscribe to.
secret_prefix is a preview only — it shows a truncated portion of your signing secret, not the full value. Retrieve the full signing key with the endpoint below.
Creating and deleting webhook subscriptions requires the webhooks:write scope; listing them and fetching the signing key requires webhooks:read.

Payload Structure

Every webhook delivery POSTs the event’s full JSON payload directly as the request body — there’s no {"event": ..., "data": ...} envelope wrapping it. The event type is instead carried in the X-Knock-Event request header. Every payload includes a product_slug identifying which tenant the event fired for (see the Parent/Child Fan-in section below). Beyond that, the shape is specific to each event: account.identified and contact.identified carry the full account/contact record, with an id you can re-fetch via GET /v1/accounts/{id} or GET /v1/contacts/{id}. score.changed carries its own id plus a company_lead_id or person_lead_id — there’s no GET /v1/scores/{id}, so use that id to look the current score back up via GET /v1/scores?account_id= / ?contact_id=. play.triggered carries play_id/contact_ids rather than a top-level id. signal.detected nests its record (including its own id) under data. tenant.limit_reached and tenant.limit_approaching describe a threshold being crossed rather than a single fetchable record, so neither has an id at all. signal.detected is the one exception: its own payload happens to include event and data keys as part of its normal shape (see its tab below) — that’s a property of that specific event, not a general envelope every event is wrapped in. Here is a real sample payload for each event type:
workflow.triggered is a deprecated alias of play.triggered — Knock2 dispatches it with the exact same payload shown in the play.triggered tab above (play_id/play_name are simply aliased as workflow_id/workflow_name). Existing subscriptions keep receiving it; don’t subscribe new integrations to it.
These samples come from the same fixture data used by the sandbox environment and are kept in sync with real dispatch payloads by an automated check — if a field looks off, it’s out of date rather than approximate, so let us know.

Verifying Signatures

Knock2 signs every webhook payload with HMAC-SHA256 and delivers the signature in the X-Knock-Signature request header. Always verify the signature before processing the payload to ensure the request genuinely came from Knock2. Verification is a standard HMAC-SHA256 check over the raw request body bytes (not a parsed/re-serialized object):

Getting Your Signing Key

The secret you pass to the snippets above is retrieved with GET /v1/webhooks/{webhook_id}/secret (requires the webhooks:read scope):
Unlike your API key, the signing key is never stored — it’s re-derived from the subscription ID on every request, so you can fetch it again at any time. There’s no “shown only once” restriction, and nothing is invalidated by requesting it more than once.

List Webhooks

Retrieve all registered webhook subscriptions for your account:
This endpoint is not paginated — GET /v1/webhooks returns all active subscriptions for your tenant in a single response, without has_more/next_cursor.

Delete a Webhook

Remove a webhook subscription by sending a DELETE request with the webhook ID:
A successful deletion returns 204 No Content with an empty body. Knock2 stops delivering events to that URL immediately.
Webhook endpoint URLs must use HTTPS. Plain HTTP URLs are rejected at registration time to ensure your payload data is encrypted in transit. If you’re testing locally, use a tunneling tool such as ngrok to simulate deliveries safely.

Parent/Child Fan-in

A subscription you create with your own (parent) API key automatically covers events fired for every one of your child tenants too — you don’t need to register a separate subscription per child, and tenants created after the subscription already exists are covered from the moment they’re created. Every delivery includes a product_slug field so you can tell which tenant an event actually came from. If a child tenant also has its own subscription for the same event type, both fire — duplicate delivery across a parent-level and child-level subscription is the accepted default, since deliveries are already dedupable by X-Knock-Delivery-Id.

Delivery and Retries

Deliveries are at-least-once — design your handler to be idempotent (dedupe on the event payload’s id). If your endpoint doesn’t respond successfully, Knock2 retries with the following backoff schedule: 1 minute, 5 minutes, 30 minutes, then 2 hours after the initial attempt. After the 5th attempt fails, the delivery is marked dead-lettered and no further retries occur.