Webhooks Reference
Payload shapes and HMAC signature verification for lead and handover webhook events.
Webhooks are configured from Settings & Account. They fire when a visitor submits the enquiry form or when a chat is escalated to a human operator.
Events
| Event | When it fires |
|---|---|
lead.created | Visitor submits the enquiry / lead form |
conversation.handover_requested | AI escalates a chat to a human (Human Handover enabled) |
New webhooks created from Settings subscribe to both events by default. Existing webhooks keep their stored event list — update them in Settings if you need handover events on an older endpoint.
lead.created payload
{
"event": "lead.created",
"timestamp": "2026-07-15T09:00:00.000Z",
"data": {
"id": "lead_uuid",
"agent_id": "agent_uuid",
"visitor_name": "Jane Doe",
"visitor_email": "jane@example.com",
"visitor_message": "Do you offer annual billing?",
"status": "new",
"created_at": "2026-07-15T09:00:00.000Z"
}
}conversation.handover_requested payload
{
"event": "conversation.handover_requested",
"timestamp": "2026-08-10T01:30:00.000Z",
"data": {
"conversation_id": "conversation_uuid",
"agent_id": "agent_uuid",
"agent_name": "Support Bot",
"reason": "Visitor asked to speak to a person",
"summary": "User cannot reset password via self-service",
"requested_at": "2026-08-10T01:30:00.000Z"
}
}Use the conversation ID to deep-link operators to
/dashboard/inbox?c={conversation_id} (same link included in handover notification emails).
Request headers
| Header | Description |
|---|---|
Content-Type | application/json |
User-Agent | Mozo-Webhooks/1.0 |
X-Mozo-Signature | Present only if the webhook has a secret configured |
Verifying the signature
If you set a secret when creating the webhook, every request includes an HMAC-SHA256
signature of the raw JSON body, hex-encoded, in the X-Mozo-Signature header:
import { createHmac, timingSafeEqual } from "crypto";
function isValidMozoSignature(rawBody, signatureHeader, secret) {
const expected = createHmac("sha256", secret)
.update(rawBody)
.digest("hex");
const a = Buffer.from(signatureHeader ?? "");
const b = Buffer.from(expected);
return a.length === b.length && timingSafeEqual(a, b);
}Compute the signature over the exact raw request body bytes, before any JSON parsing — re-serializing the parsed object can produce a different byte sequence and fail verification.
Scope
A webhook can be scoped to a single agent, or left agent-less to receive events from every agent in your organization. Webhooks can be individually enabled, disabled, tested, or deleted from Settings.
See also Human Handover for the operator Inbox flow that complements webhook notifications.