Skip to main content
There are two ways to receive events. They differ in how they are delivered. Both send the same JSON body:
Your endpoint has 10 seconds to answer with any 2xx. GET /v1/webhooks/events lists every event type you can subscribe to. For double opt-in, LIST_SUBSCRIPTION_CONFIRMED is sent once for each list a confirmation click activates, with data of { "contactId", "email", "listId", "listName" }. CONTACT_CONFIRMED ({ "contactId", "email" }) is sent only when the contact itself goes from pending to active. See Webhooks.

Subscribing a REST hook

The response is 201 { "id": "…" }. Subscribing the same event and URL again returns the same id. To remove the subscription, call DELETE /v1/webhooks/{id}.

Verifying signatures

Endpoints registered under Settings → Webhooks with a secret receive these headers:
  • webhook-id: the message ID. It stays the same across retries, so use it to deduplicate.
  • webhook-timestamp: Unix seconds.
  • webhook-signature: v1,<base64 HMAC-SHA256> of "{webhook-id}.{webhook-timestamp}.{raw body}".
  • X-Signature-256: the legacy header, sha256=<hex HMAC-SHA256 of the raw body>.
If the secret starts with whsec_, base64-decode the rest to get the HMAC key. Otherwise the key is the secret’s UTF-8 bytes. Endpoints without a secret receive no signature headers.
Every delivery also carries X-Webhook-Event and X-Webhook-Attempt.

Retries and disabling

For dashboard endpoints, delivery is tried three times in a row, 1 and 2 seconds apart. After that it is retried on a schedule of +5 min, +30 min, +2 h, +5 h, +8 h, +8 h and +10 h. A 429 reschedules the delivery without counting it as a failure. An endpoint that keeps failing for more than 12 hours is disabled until you re-enable it in Settings → Webhooks. The full delivery schema, with every header and payload field, is in Settings → API reference in the app.