CanWeChat
Docs

Webhooks

Receive signed CanWeChat events in your own backend.

Headers

X-CanWeChat-Id, X-CanWeChat-Timestamp, and X-CanWeChat-Signature: v1=<hex>. The signed string is timestamp.raw-json-body using HMAC-SHA256.

Verify

Compute HMAC-SHA256 of `${timestamp}.${rawBody}` with the signing secret shown once at creation or rotation. Compare to the hex after v1=. Reject if the timestamp is too old for your tolerance.

code
import { createHmac, timingSafeEqual } from "node:crypto";

function verify(rawBody, timestamp, signatureHeader, secret) {
  const expected = "v1=" + createHmac("sha256", secret).update(`${timestamp}.${rawBody}`).digest("hex");
  const a = Buffer.from(expected);
  const b = Buffer.from(signatureHeader);
  return a.length === b.length && timingSafeEqual(a, b);
}

Payload

Events include conversation.created and message.created. Payloads include conversation IDs, visitor identity when provided, page context, message source, and timestamps. They do not include integration tokens or auth secrets.

Example event

Store the event ID before processing so retries do not create duplicate records in your system.

code
{
  "id": "evt_01J7...",
  "type": "message.created",
  "createdAt": "2026-09-10T09:20:00.000Z",
  "conversation": {
    "id": "conv_01J7...",
    "status": "open",
    "pageUrl": "https://example.com/pricing"
  },
  "visitor": {
    "id": "vis_01J7...",
    "name": "Ada Lovelace",
    "email": "ada@example.com"
  },
  "message": {
    "id": "msg_01J7...",
    "senderType": "visitor",
    "source": "widget",
    "body": "Do you support idempotency keys?"
  }
}

Retries

Failed deliveries retry from a Cloudflare Queue. Delivery history records event type, delivery status, HTTP code, attempt number, timestamp, and a bounded response snippet.

Use the dashboard delivery log to confirm that your endpoint returned a 2xx response. Rotate the signing secret if it is exposed, then update your backend before sending another test event.