Skip to main content

Webhooks

Webhook subscriptions use a RestHooks-style API. Zapier creates and deletes subscriptions automatically when a user enables or disables a trigger.

Subscribe

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"
}
}

The response returns the subscription and a signing secret. The secret is shown only once.

List and Delete

EndpointMethod
/api/v1/webhooksGET
/api/v1/webhooks/:idGET
/api/v1/webhooks/:idDELETE

All webhook endpoints require the webhooks:manage scope.

Event Types

EventDescription
lead.createdA new lead was captured.
lead.updatedA lead's fields changed.
lead.qualifiedA lead reached the qualification threshold.
lead.convertedA lead was marked as converted to a customer.
lead.score_changedA lead's qualification score changed.
conversation.startedA new conversation was opened.
conversation.endedA conversation was closed.
conversation.message_receivedA new message arrived in a conversation.
conversation.sentiment_negativeA conversation reached negative sentiment.
conversation.sentiment_positiveA conversation reached positive sentiment.
message.feedback_negativeA user gave negative feedback on a message.
message.feedback_positiveA user gave positive feedback on a message.
ticket.createdA support ticket was created.
ticket.updatedA support ticket was updated.
ticket.resolvedA support ticket was marked as resolved.
meeting.scheduledA meeting was booked.
meeting.cancelledA meeting was cancelled.
meeting.completedA meeting was marked as completed.
meeting.reminderA reminder for an upcoming meeting fired.
agent.tool_executedAn AI agent tool was executed.
agent.tool_failedAn AI agent tool execution failed.
usage.threshold_reachedThe workspace hit a quota threshold.

Delivery Security

Leezy sends each webhook with these headers:

HeaderPurpose
X-Leezy-SignatureHMAC SHA-256 signature over <timestamp>.<raw_body>, prefixed with sha256=.
X-Leezy-TimestampUnix timestamp (seconds) included in the signed input.
X-Leezy-EventEvent type.
X-Leezy-DeliveryDelivery log ID.

To verify a delivery, recompute the HMAC over ${timestamp}.${raw_body} using the subscription secret and compare with X-Leezy-Signature. The timestamp is part of the signed input — receivers MUST also reject deliveries whose X-Leezy-Timestamp is more than five minutes away from the receiver's current time. Without the timestamp window, a captured payload could be replayed indefinitely because the same body always produces the same signature.

Verification example (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),
);
}