Signed webhook deliveries backed by the real event model

Bavimail emits outbound lifecycle events, inbound receipt events, domain verification events, and test pings. Verification should match the actual SDK helper and header contract.

Last updated May 7, 2026

Event Types

These event names come from the backend enum and should be treated as the public contract for webhook subscriptions.

EventDescription
email.inbound.receivedInbound email accepted and parsed.
email.outbound.sentOutbound delivery or engagement lifecycle event.
email.outbound.failedOutbound delivery or engagement lifecycle event.
email.outbound.openedOutbound delivery or engagement lifecycle event.
email.outbound.clickedOutbound delivery or engagement lifecycle event.
email.outbound.scheduledOutbound delivery or engagement lifecycle event.
email.outbound.cancelledOutbound delivery or engagement lifecycle event.
email.outbound.deliveredOutbound delivery or engagement lifecycle event.
email.outbound.bouncedOutbound delivery or engagement lifecycle event.
email.outbound.complainedOutbound delivery or engagement lifecycle event.
domain.verifiedDomain verification state update.
domain.failedDomain verification state update.
webhook.testWebhook test delivery.

Verification flow

Verify the raw JSON payload against the exact signature headers before trusting the event body. The webhook secret is hex-encoded and is used as the HMAC key.

import { verifyWebhookSignature } from 'bavimail'

app.post('/webhooks/email', async (req, res) => {
  const payload = JSON.stringify(req.body)
  const event = await verifyWebhookSignature(
    payload,
    req.headers['x-webhook-signature'],
    req.headers['x-webhook-timestamp'],
    process.env.BAVIMAIL_WEBHOOK_SECRET!,
  )

  switch (event.eventType) {
    case 'email.outbound.delivered':
    case 'email.outbound.opened':
    case 'email.outbound.clicked':
    case 'email.outbound.bounced':
      await reconcileEmailState(event)
      break
  }

  res.status(200).end()
})

Delivery contract

Headers
x-webhook-signaturex-webhook-timestamp
Store the event id, event type, and timestamp so retries can be handled idempotently in your own system.
Do not document suppression updates as webhook events. The current emitted set is outbound lifecycle, inbound receipt, domain verification, and test traffic only.

Verify x-webhook-signature and x-webhook-timestamp before parsing the body as a trusted event.