Webhooks
Webhook-Abonnements folgen einer API im RestHooks-Stil. Zapier legt Abonnements automatisch an und entfernt sie wieder, sobald ein Auslöser aktiviert oder deaktiviert wird.
Abonnieren
POST /api/v1/webhooks/subscribe
Authorization: Bearer lzy_at_your_access_token
Content-Type: application/json
{
"target_url": "https://hooks.zapier.com/hooks/catch/123/abc/",
"event_types": ["lead.created"],
"filters": {
"chatbot_id": "optional-chatbot-id"
}
}
Die Antwort enthält das Abonnement und ein Signaturgeheimnis. Dieses wird nur einmal angezeigt.
Auflisten und Löschen
| Endpunkt | Methode |
|---|---|
/api/v1/webhooks | GET |
/api/v1/webhooks/:id | GET |
/api/v1/webhooks/:id | DELETE |
Alle Webhook-Endpunkte setzen den Scope webhooks:manage voraus.
Ereignistypen
| Ereignis | Beschreibung |
|---|---|
lead.created | Ein neuer Lead wurde erfasst. |
lead.qualified | Ein Lead hat die Qualifizierungsschwelle erreicht. |
conversation.message_received | In einem Gespräch ist eine neue Nachricht eingegangen. |
ticket.created | Ein Support-Ticket wurde angelegt. |
meeting.scheduled | Ein Termin wurde gebucht. |
meeting.cancelled | Ein Termin wurde storniert. |
Sicherheit der Zustellung
Leezy sendet zu jedem Webhook diese Header:
| Header | Zweck |
|---|---|
X-Leezy-Signature | HMAC-SHA-256-Signatur über <timestamp>.<raw_body>, mit Präfix sha256=. |
X-Leezy-Timestamp | Unix-Zeitstempel (Sekunden), Teil der signierten Eingabe. |
X-Leezy-Event | Ereignistyp. |
X-Leezy-Delivery | ID des Zustellprotokolls. |
Zur Prüfung wird der HMAC über ${timestamp}.${raw_body} mit dem Abonnementgeheimnis neu berechnet und mit X-Leezy-Signature verglichen. Der Zeitstempel ist Teil der signierten Eingabe — Empfänger MÜSSEN Zustellungen ablehnen, deren X-Leezy-Timestamp mehr als fünf Minuten von der eigenen Zeit abweicht. Ohne dieses Zeitfenster ließe sich eine abgefangene Nachricht beliebig oft wiederholen, da derselbe Rumpf stets dieselbe Signatur ergibt.
Beispiel zur Prüfung (Node.js):
import crypto from "node:crypto";
function verifyLeezySignature(rawBody, headers, secret) {
const timestamp = headers["x-leezy-timestamp"];
const signature = headers["x-leezy-signature"];
const ageSeconds = Math.abs(Date.now() / 1000 - Number(timestamp));
if (!timestamp || ageSeconds > 300) return false;
const expected = "sha256=" + crypto
.createHmac("sha256", secret)
.update(`${timestamp}.${rawBody}`)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected),
);
}