Webhooks send event data to an HTTPS endpoint you control, as it happens. Use them to push leads into your CRM, log conversations in your own database, or start a process in your backend.
Prerequisites
- A Pro or Max plan — On Plus, the section shows the plan you need. You can still view a saved configuration, switch the webhook off, and disconnect subscriptions.
- A public HTTPS endpoint — The URL must use HTTPS and be reachable from the internet. Local and private network addresses are blocked, and redirects aren't followed, so use the final endpoint URL.
- A saved chatbot — Save a new chatbot once before you add subscriptions.
Two Ways to Send Events
The Webhooks & Zapier section has two independent ways to deliver events. You can use either or both.
- Subscriptions — One destination URL per event. A subscription takes effect the moment you create it, with no publishing, and gets its own signing secret.
- Existing webhook — One URL that receives every event you tick. It's part of your chatbot draft, so changes reach your live chatbot when you save and publish.
Create a subscription
Open Webhooks & Zapier
Enter the destination and event
Create it
Save the signing secret

Set up the existing webhook
Switch it on and add your URL
Choose events
Copy the signing secret
Save, test, and publish

Event Types
| Event | Trigger |
|---|---|
lead.captured | A visitor submits the lead form or verifies with OTP |
conversation.started | A new conversation is created |
conversation.message | The visitor sends a message |
conversation.reply | The AI sends a reply |
appointment.booked | The chatbot books an appointment in the chat (listed once booking is enabled for your chatbot) |
Inspect Event Payloads
Under "Inspect event payloads", choose an event to see a sample body. Samples use recent real event data when your chatbot has some, and example data when it doesn't.
Payload reference for developers
Envelope
Every webhook POST body uses this envelope:
{
"eventType": "lead.captured", // one of the event types above
"chatbotId": "a1b2c3d4-...", // chatbot that triggered the event
"chatbotName": "Support Bot", // display name of the chatbot
"organizationId": "e5f6g7h8-...", // your organization ID
"timestamp": "2026-10-15T10:30:00Z", // when the event occurred (ISO 8601 UTC)
"deliveryId": "d9e0f1a2-...", // unique ID for this delivery
"data": { ... } // event-specific payload (below)
}lead.captured
{
"name": "Jane Smith", // string | null
"email": "jane@example.com", // string | null
"phone": "+1234567890", // string | null
"companyName": "Acme Inc", // string | null
"externalUserLoginId": "uuid-...", // string | null — login record ID
"externalUserId": "uuid-...", // the lead; matches externalUserId on conversation events
"tags": ["enterprise", "demo"], // string[] | null — conversation tags
"customFields": { "budget": "5k" } // custom lead-form answers, keyed by field id
}conversation.started
{
"conversationId": "uuid-...",
"externalUserId": "uuid-...", // string | null
"channel": "web", // "web", "whatsapp", "instagram" or "facebook"
"senderExternalId": "15551234567", // WhatsApp number or Instagram/Messenger ID; absent on web
"metadata": { "page": "/pricing" } // object | null — custom metadata from the chat session
}conversation.message
{
"conversationId": "uuid-...",
"message": "How do I reset my password?", // the visitor's message
"externalUserId": "uuid-...",
"channel": "web"
}conversation.reply
{
"conversationId": "uuid-...",
"messageId": "uuid-...", // ID of the AI reply
"userMessage": "How do I reset?", // the visitor message it answers
"reply": "To reset your password...", // the full AI reply
"externalUserId": "uuid-...",
"channel": "web"
}appointment.booked
{
"conversationId": "uuid-...",
"externalUserId": "uuid-...",
"provider": "calcom", // the booking calendar's provider
"calendarId": "...", // which calendar, when the chatbot has several
"meetingType": "Intro call",
"startUtc": "2026-10-20T15:00:00Z",
"endUtc": "2026-10-20T15:30:00Z",
"timeZone": "America/New_York",
"bookingId": "...",
"joinUrl": "https://...",
"cancelUrl": "https://...",
"attendeeName": "Jane Smith",
"attendeeEmail": "jane@example.com"
}Optional fields such as channel, senderExternalId, and the booking details are left out when they don't apply. Lead fields the visitor didn't give are sent as null.
Request Headers
| Header | Value | Description |
|---|---|---|
Content-Type | application/json | Always JSON |
X-Hyperleap-Event | e.g. lead.captured | The event type |
X-Hyperleap-Delivery-Id | UUID string | Unique delivery ID for idempotency |
X-Hyperleap-Signature | sha256={hex} | HMAC-SHA256 signature of the body, made with your signing secret |
Verifying Signatures
The X-Hyperleap-Signature header carries an HMAC-SHA256 signature of the raw request body, made with the signing secret. Verify it before trusting a request.
Node.js
const crypto = require('crypto');
function verifySignature(payload, secret, signatureHeader) {
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(payload, 'utf8')
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signatureHeader)
);
}Python
import hmac, hashlib
def verify_signature(payload: bytes, secret: str, signature_header: str) -> bool:
expected = 'sha256=' + hmac.new(
secret.encode(), payload, hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature_header)timingSafeEqual or compare_digest) to prevent timing attacks.Delivery History
"Delivery history" lists up to 100 recent deliveries with the event, status, and your endpoint's response code. Filter by destination or event, click "Refresh" to load new ones, and open any delivery to see the error and the exact payload sent.
Retries
- Up to 3 attempts per delivery, with a short, growing wait between them.
- 10-second timeout per attempt — your endpoint must respond within 10 seconds.
- What gets retried: timeouts, network errors, and HTTP 5xx, 408, and 429 responses. Other 4xx responses are treated as permanent and aren't retried.
- HTTP 410 Gone stops delivery for good: the matching subscription is deactivated.
Best Practices
- Respond with a 2xx status within 10 seconds, and do heavy processing after you've acknowledged the request.
- Use
deliveryIdto ignore duplicates: a retry can deliver the same event twice. - If a signing secret is ever exposed, regenerate it (existing webhook) or disconnect and recreate the subscription.
Zapier
The same section has an "Automate with Zapier" area. Hyperleap's Zapier app is still in development and isn't public in Zapier's app directory yet. Until it is, use webhooks or the API to connect other tools.
Next Steps
- Notifications — Get leads and transcripts by email instead
- CRM MCP — Ask Claude or ChatGPT about your leads
- API Reference — Full API documentation for programmatic access