Webhooks
Get an HTTPS request from Knock when something happens in your account, such as a booked meeting or a finished enrichment request, instead of polling for it.
A webhook tells your system about something that happened in your Knock account. You give Knock an HTTPS URL and the event types you want, and Knock sends a POST request to that URL every time one of those events occurs: a contact books a meeting, a meeting moves, an enrichment request finishes.
Webhooks or polling
- Use webhooks to react to events: create a CRM task when a meeting is booked, update a calendar when it moves, start a job when enrichment results are ready. Knock sends you each event, so you don't spend rate limit on requests that find nothing new.
- Use polling to read or backfill data:
GET /v1/meetingsandGET /v1/enrichment-requests/{enrichRequestId}return the full current state, including everything that happened before you created a webhook.
Most integrations use both. A webhook says that something changed and carries the main fields; the API holds the full record. For example, enrichment_request.completed tells you a request is final, and you then read its results with GET /v1/enrichment-requests/{enrichRequestId}.
Keep a slow poll as a safety net for anything critical: webhooks are delivered at least once, but your endpoint can be down for longer than Knock keeps retrying (see Delivery and retries).
How webhooks work
A webhook is one URL and the event types it receives, with its own id, an optional name, an optional secret token and a status: active or paused.
- Several event types per webhook. One webhook can receive any of the event types: list them in its
events, and change the list at any time. Branch on the body'stypeto tell them apart. - One webhook per URL. A URL can belong to only one of your webhooks: a second create with the same URL gets
409. - Several URLs per event type. The same event type can go to several webhooks. Each one gets its own delivery of the event, with the same event
id, and theknock-webhook-idheader says which webhook it's for. - Up to 50 webhooks per account, paused ones included. See Limits.
- One secret token per webhook. It's optional. You choose it, and Knock sends it as is in the
knock-webhook-tokenheader of every delivery to that webhook, so your receiver can check that the request came from Knock. See Verify each request. - Pause without deleting. A
pausedwebhook gets nothing until you set it back toactive. Pausing drops every delivery not sent yet, retries included. See Pause and resume a webhook. - At least once, in any order. The same event can arrive more than once, and events can arrive out of order. See Delivery and retries.
- Answer fast. Knock waits 5 seconds for your answer, doesn't follow redirects and never reads the response body: only the status code counts, and
Retry-Afteron a429or503.
Verify each request
Anyone who learns your URL can send it a request, so your endpoint must check that each one came from Knock. Knock uses a secret token, the same way Telegram's Bot API does:
- You choose a long random value and set it as the webhook's
secretToken, when you create the webhook or later withPATCH /v1/webhooks/{webhookId}. - Knock sends it back, exactly as you set it, in the
knock-webhook-tokenheader of every delivery to that webhook. - Your receiver compares the header with the value it expects, in constant time, and answers
401if they differ.
Knock doesn't add any other proof to a delivery, so the token is the whole check. It proves the sender knows your token, but it doesn't detect a changed body or a replayed request, and it travels with every request. Treat it like a password: keep it out of source code and logs, and replace it if it's ever exposed. Knock only delivers over HTTPS, so the header is encrypted on the way. See Verify webhooks for the check in full and for rotating the token.
Endpoints
The webhooks API is on the same host as the rest of the Knock API, under https://app.knock-ai.com/v1/webhooks.
| Method | Path | What it does |
|---|---|---|
POST | /v1/webhooks | Create a webhook for one URL and one or more event types. Returns 201 with the webhook. |
GET | /v1/webhooks | List your webhooks, oldest first, a page at a time (limit, offset), optionally only those for one event type (?event=). Returns 200. |
GET | /v1/webhooks/{webhookId} | Get a webhook. Returns 200. |
PATCH | /v1/webhooks/{webhookId} | Change a webhook's name, URL, event types, status or secret token. Set status to pause or resume it. Returns 200 with the webhook. |
DELETE | /v1/webhooks/{webhookId} | Delete a webhook. Knock stops sending to it at once, retries included. Returns 204. |
POST | /v1/webhooks/{webhookId}/ping | Send a test event to the webhook's URL and report what your endpoint answered. Returns 200. |
POST | /v1/webhooks/test | Test a URL: send a test event to a URL before you create its webhook, and report what your endpoint answered. Returns 200. |
GET | /v1/webhooks/event-types | List the event types a webhook can subscribe to, with the JSON Schema and an example of each body. Returns 200. |
GET | /v1/webhooks/stats | Get delivery stats by hour for the last 24 hours or 3 days, for one webhook or your whole account. Returns 200. |
GET | /v1/webhooks/{webhookId}/secret-token | Read a webhook's secret token. Returns 200. |
Listing and reading webhooks, event types and stats need a key with the Admin, Editor, Viewer, CRM Admin or AI Manager role: an Enrichment API key can't. Creating, changing, deleting, pinging, testing a URL and reading a secret token need a key with the Admin role. See Authentication. Errors for every route are listed under Manage webhooks.
These routes have no per-account rate limit: only the per-IP limit applies, 3,000 requests per minute by default. On top of it, you can ping each webhook once every 10 seconds, and your account can test a URL once every 10 seconds. Over any of them, a call gets 429 with a Retry-After header. The deliveries Knock sends to your endpoints never count. See Rate limits.
Quickstart
Generate a secret token
Your receiver checks every request with a secret token you choose. Generate a long random value: 1 to 256 characters, using only A-Z, a-z, 0-9, _ and -. A 64-character hex string fits.
export KNOCK_WEBHOOK_TOKEN=$(openssl rand -hex 32)Store the same value where your receiver can read it, such as the KNOCK_WEBHOOK_TOKEN environment variable, never in source code.
Create a webhook
Send the URL that will receive events, the event types to subscribe it to and your token. Create it paused, so no event reaches it before your receiver is ready. The URL must be public HTTPS on port 443 (see URL requirements). The name is an optional label.
curl -X POST https://app.knock-ai.com/v1/webhooks \
-H "x-api-key: $KNOCK_API_KEY" \
-H "Content-Type: application/json" \
-d @- <<EOF
{
"name": "CRM sync",
"url": "https://hooks.yourcompany.com/webhooks/knock",
"events": ["meeting.booked", "meeting.rescheduled"],
"status": "paused",
"secretToken": "$KNOCK_WEBHOOK_TOKEN"
}
EOF{
"id": "1c9e5b7a-3f2d-4e8a-b6c1-7d0f2a4e9b35",
"name": "CRM sync",
"url": "https://hooks.yourcompany.com/webhooks/knock",
"events": ["meeting.booked", "meeting.rescheduled"],
"status": "paused",
"hasSecretToken": true,
"createdAt": "2026-11-02T09:30:00.000Z"
}Keep the id: you need it to ping, change or delete the webhook. Knock never returns the token with the webhook: hasSecretToken says it has one. A token outside the allowed characters or length gets 400 with code webhooks.invalid-secret-token, and no webhook is created. To add enrichment_request.completed to the same URL later, change the webhook's events.
Verify every request in your receiver
Compare the knock-webhook-token header with your token in constant time before you trust the request. Hashing both sides first gives two values of the same length, which timingSafeEqual requires.
npm install expressimport { createHash, timingSafeEqual } from 'node:crypto';
import express from 'express';
const expected = createHash('sha256').update(process.env.KNOCK_WEBHOOK_TOKEN).digest();
function tokenMatches(received) {
if (typeof received !== 'string') return false;
const actual = createHash('sha256').update(received).digest();
return timingSafeEqual(actual, expected);
}
// Event IDs already handled. Use your database in production.
const handled = new Set();
const app = express();
app.post('/webhooks/knock', express.json(), (req, res) => {
if (!tokenMatches(req.get('knock-webhook-token'))) {
return res.sendStatus(401);
}
const event = req.body;
res.sendStatus(200);
if (handled.has(event.id)) return;
handleEvent(event)
// Mark the event handled only once its work succeeded, so a duplicate delivery retries a failure.
.then(() => handled.add(event.id))
.catch((error) => console.error(`Failed to handle event ${event.id}`, error));
});
async function handleEvent(event) {
switch (event.type) {
case 'webhook.ping':
console.log('Ping for webhook', event.data.webhookId);
break;
case 'meeting.booked':
console.log('Meeting booked', event.data.meetingId, event.data.startAt);
break;
default:
console.log('Ignoring event type', event.type);
}
}
app.listen(3000, () => console.log('Listening on port 3000'));A matching token proves the request came from someone who knows it. See Verify webhooks for Python, rotating the token and what the token doesn't protect.
Respond with 2xx fast
Answer with any 2xx status within 5 seconds, then do the work. The receiver above responds first and handles the event afterwards. Knock reads only the status code (and Retry-After on a 429 or 503): the response body is never read, so leave it empty. In production, store the event first (see the inbox pattern), so a crash after the response doesn't lose it.
Return 401 when verification fails. Knock retries a 401 or 403, which covers the moment between changing your token and deploying the new value. Don't answer with a redirect: Knock doesn't follow it, and counts it as a failure. See Delivery and retries for which answers are retried, duplicates and ordering.
Send a test ping
Deploy the receiver, then ask Knock to send it a webhook.ping event. The ping carries the webhook's secret token, like every other delivery to it:
curl -X POST https://app.knock-ai.com/v1/webhooks/1c9e5b7a-3f2d-4e8a-b6c1-7d0f2a4e9b35/ping \
-H "x-api-key: $KNOCK_API_KEY"Knock sends the ping right away, waits up to 5 seconds for your endpoint and reports what it answered:
{
"eventId": "5e2b9c4a-7d1f-4a3e-8b6c-0f9d2e1a3c7b",
"delivered": true,
"statusCode": 200,
"errorCode": null,
"durationMs": 184,
"retryAfterSeconds": 10
}"delivered": true means your receiver accepted the request, so verification passed. Then resume the webhook with {"status": "active"} to start receiving events. A ping is never retried. You can ping each webhook once every 10 seconds: retryAfterSeconds says when Knock accepts the next one, and a ping sent sooner gets 429. See Test a webhook with a ping for every field, and What the result tells you when delivered is false.
To check a receiver before you create its webhook, send it the same test event with POST /v1/webhooks/test.
Events
| Event type | Sent when |
|---|---|
meeting.booked | A contact books a meeting with your team through Knock. |
meeting.rescheduled | A meeting booked through Knock moves to a new time through Knock. |
enrichment_request.completed | An enrichment request you submitted finished. |
webhook.ping | You ping a webhook (POST /v1/webhooks/{webhookId}/ping) or test a URL (POST /v1/webhooks/test). Only for testing, and never in a webhook's events. |
The webhook events reference shows the envelope every event shares, the headers, each event's data fields and an example. New event types are announced in the changelog.
Next steps
Manage webhooks
Create, list, change, pause, ping and delete webhooks, test a URL, read delivery stats, set each webhook's secret token, and every error.
Verify webhooks
The delivery headers, the secret token check, rotating the token and its limits.
Handle events
Choose event types, read the envelope, and process events once, in any order.
Delivery and retries
What counts as delivered, the retry schedule, duplicates and ordering.
Webhook events
The envelope, the headers and every event type's data fields.
Routing API
Route a lead with a routing rule you set up in the Knock dashboard, and get the segment, owner and enrichment back in one call.
Manage webhooks
Create, list, change, pause and delete webhooks with the API, set each webhook's secret token, test a URL, read event types and delivery stats, and handle ever…