Skip to content
Docs

Overview

Get signed HTTP notifications when emails are delivered, bounce, are opened or clicked, arrive on your inbound domain, or when resources change.

Updated Oct 1, 2026

Webhooks push events from Emailit to your application as they happen, so you don’t have to poll the API. Use them to update delivery status in your database, stop emailing addresses that bounce or complain, react to opens and clicks, process inbound email, or sync contacts and suppressions.

How it works

  1. You register an endpoint. Create a webhook with a name and an HTTPS URL in the dashboard or with the API. Emailit generates a signing secret for it.
  2. Something happens. Emailit records an event, for example email.delivered, and queues it for every enabled webhook that subscribes to that event type and whose payload filter matches.
  3. Emailit sends a batch. Within seconds, Emailit sends a POST request whose body is a JSON array of up to 100 queued events, oldest first. Requests to one webhook are sent one at a time.
  4. Your endpoint answers. Any 2xx status within 30 seconds marks every event in the batch as delivered. Anything else, including a timeout or a redirect, is a failure, and the batch is retried for about three days.

Each request has these headers:

Header Value
Content-Type application/json
User-Agent Emailit-Webhooks/2.0
X-Emailit-Signature Hex HMAC-SHA256 of {timestamp}.{raw body}, keyed with the webhook secret.
X-Emailit-Timestamp Unix time in seconds when the request was signed.

See Webhook requests for the full format and Request signature for verification code.

Quick example

This Express handler verifies the signature, then handles each event in the batch:

webhook.js
import crypto from 'node:crypto';
import express from 'express';

const app = express();

app.post('/webhooks/emailit', express.raw({ type: 'application/json' }), (req, res) => {
  const timestamp = req.get('X-Emailit-Timestamp') ?? '';
  const signature = req.get('X-Emailit-Signature') ?? '';
  const expected = crypto
    .createHmac('sha256', process.env.EMAILIT_WEBHOOK_SECRET) // whsec_...
    .update(`${timestamp}.`)
    .update(req.body)
    .digest('hex');

  const fresh = Math.abs(Date.now() / 1000 - Number(timestamp)) <= 300;
  const a = Buffer.from(signature);
  const b = Buffer.from(expected);
  const valid = a.length === b.length && crypto.timingSafeEqual(a, b);
  if (!fresh || !valid) return res.status(401).send('Invalid signature');

  for (const event of JSON.parse(req.body)) {
    switch (event.type) {
      case 'email.delivered':
        console.log(`Delivered ${event.data.object.id} to ${event.data.object.to}`);
        break;
      case 'email.bounced':
      case 'email.complained':
        console.log(`Stop sending to ${event.data.object.to}`);
        break;
    }
  }

  res.sendStatus(200);
});

app.listen(3000);

What you can subscribe to

Webhooks cover email delivery and engagement (email.delivered, email.bounced, email.clicked and more), inbound mail (email.received), and changes to domains, audiences, subscribers, contacts, templates, suppressions, email verifications and campaigns. A new webhook created in the dashboard receives every event; you can narrow it to specific types later. See Event types.

On Pro, Business and Custom plans, a payload filter can limit a webhook to events that match rules, for example only emails to one domain.

Limits

Pay as you goProBusinessCustom
Webhook endpoints310100100
Webhook payload filters—Up to 25 rulesUp to 25 rulesUp to 25 rules

The webhook page shows how many endpoints you’re using, for example 2/3 used. When you reach the limit, Add webhook is disabled until you delete one or upgrade. Each webhook can send at most 5 test events per minute.

Get started

Create an endpoint, choose events, add filters and send a test.
Check that requests come from Emailit, in six languages.
Every event Emailit sends and when.
What happens when your endpoint is down.

Was this page helpful?

Thanks for the feedback.

Thanks, we read every message.