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

EventWhen it fires
lead.createdVisitor submits the enquiry / lead form
conversation.handover_requestedAI 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

JSON
{
  "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

JSON
{
  "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

HeaderDescription
Content-Typeapplication/json
User-AgentMozo-Webhooks/1.0
X-Mozo-SignaturePresent 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.