Skip to content

Overview

Subscribe your endpoint to WhatSetter events. Deliveries are signed:

Header Value
X-Whatsetter-Event the event type
X-Whatsetter-Delivery unique delivery id (evt_…): use it to dedupe
X-Whatsetter-Timestamp ISO-8601
X-Whatsetter-Api-Version 2026-06-01
X-Whatsetter-Signature sha256=<hex>: HMAC-SHA256 of the raw body with your subscription secret

Verify (Node.js):

const crypto = require('crypto');
const expected = 'sha256=' + crypto.createHmac('sha256', SECRET).update(rawBody, 'utf8').digest('hex');
const valid = crypto.timingSafeEqual(Buffer.from(header), Buffer.from(expected));

Events:

  • contact.qualified / contact.not_qualified: a lead was (dis)qualified by the AI
  • booking.created: a call was booked in chat
  • message.received: a lead sent an inbound message (high volume)
  • agent.disconnected: a WhatsApp number went offline

Delivery is at-least-once: dedupe on event_id. Payloads carry is_test: true when triggered from a test.

Payload shape (contact.qualified):

{
  "event_type": "contact.qualified",
  "event_id": "evt_3f2a…",
  "api_version": "2026-06-01",
  "occurred_at": "2026-08-05T09:12:44.001Z",
  "team_id": "…",
  "campaign": { "id": "…", "slug": "…", "name": "…" },
  "contact": {
    "id": "…",
    "lead_id": "33612345678@c.us",
    "phone": "33612345678",
    "name": "Marie Dupont",
    "external_reference": "4471",
    "classification": "QUALIFIED",
    "ai_reasoning": "…",
    "conversation_url": "https://app.whatsetter.com/…"
  },
  "is_test": false
}

Matching events to your own records. Send external_reference on POST /campaigns/{id}/leads and read it back from contact.external_reference — that is the intended join key. It is null for leads created from the dashboard. If you match on the phone number instead, note that contact.phone is digits only, without a leading +, while POST /campaigns/{id}/leads echoes it with the +: compare digits on both sides.