Skip to content

Webhooks

Webhooks deliver HTTP POST notifications to your endpoint when events occur.

EventTriggered when
lead.createdA new lead is created (HTTP 201 on POST /leads)
lead.updatedA lead is updated — including dedup merges (POST /leads with duplicate: true)
lead.deletedA lead is deleted
lead.scoredA lead score is recalculated
note.createdA note is added to a lead
note.updatedA note is edited
note.deletedA note is deleted

A merge never fires lead.created. Subscribe to lead.updated if you need to react to dedup write-backs.

Batch operations fire one grouped event instead of N individual events.

EventTriggered whenPayload shape
leads.createdNew inserts from POST /leads/batch{ ids: [...], count: N }
leads.updatedMerges from POST /leads/batch, or PUT /leads/batch{ ids: [...], count: N }
leads.deletedDELETE /leads/batch succeeds (for deleted ids){ ids: [...], count: N }
leads.scoredPOST /leads/score/batch runs{ leads: [{id, score}], count: N }

A single POST /leads/batch can emit both leads.created and leads.updated when the batch mixes inserts and merges.


GET /webhooks

POST /webhooks
{
"url": "https://your-server.com/hooks/klozeo",
"events": ["lead.created", "lead.updated"],
"secret": "optional-signing-secret"
}

Response 201 Created: Full webhook object.

If a secret is provided, each request includes an X-Klozeo-Signature header (HMAC-SHA256 of the raw request body, hex-encoded).


DELETE /webhooks/{id}

Response 204 No Content.


All timestamps are Unix seconds.

{
"event": "lead.created",
"timestamp": 1703520000,
"data": {
"id": "cl_...",
"name": "Acme",
"source": "website",
"score": 0,
"created_at": 1703520000
}
}

The data object is the full lead (or note) that triggered the event.


When a secret is set, verify each incoming request to prevent spoofed events:

import hmac, hashlib
def verify_webhook(payload_bytes: bytes, signature_header: str, secret: str) -> bool:
expected = hmac.new(secret.encode(), payload_bytes, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature_header)
# In your request handler:
body = request.get_data() # raw bytes, before any parsing
sig = request.headers.get("X-Klozeo-Signature", "")
if not verify_webhook(body, sig, "your-secret"):
return 401
Node.js
const crypto = require("crypto");
function verifyWebhook(rawBody, signatureHeader, secret) {
const expected = crypto
.createHmac("sha256", secret)
.update(rawBody)
.digest("hex");
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signatureHeader));
}

  • Your endpoint must return a 2xx status within 5 seconds to acknowledge receipt.
  • Design your handler to be idempotent — the same event may be delivered more than once in rare cases (network failures, retries).
  • If your endpoint is unreachable or returns a non-2xx status, deliveries are retried with exponential backoff.