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 29, 2026
Event Types
These event names come from the backend enum and should be treated as the public contract for webhook subscriptions.
| Event | Description |
|---|---|
email.inbound.received | Inbound email accepted and parsed. |
email.outbound.sent | Outbound delivery or engagement lifecycle event. |
email.outbound.failed | Outbound delivery or engagement lifecycle event. |
email.outbound.opened | Outbound delivery or engagement lifecycle event. |
email.outbound.clicked | Outbound delivery or engagement lifecycle event. |
email.outbound.scheduled | Outbound delivery or engagement lifecycle event. |
email.outbound.cancelled | Outbound delivery or engagement lifecycle event. |
email.outbound.delivered | Outbound delivery or engagement lifecycle event. |
email.outbound.bounced | Outbound delivery or engagement lifecycle event. |
email.outbound.complained | Outbound delivery or engagement lifecycle event. |
domain.verified | Domain verification state update. |
domain.failed | Domain verification state update. |
webhook.test | Webhook 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-signature x-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.
How does Bavimail handle webhook signatures and retries?
Every webhook is signed with an HMAC header (x-webhook-signature) and timestamped (x-webhook-timestamp). Your handler verifies both and returns 2xx to acknowledge. On non-2xx or timeout, Bavimail retries with exponential backoff. The 13 supported event types are documented at /docs/webhooks.
Do you handle bounce processing?
Automatically. Bavimail processes bounces, complaints, and unsubscribes in real-time. Suppression lists update instantly so you never send to a bad address twice.
Event meanings
| Event | Description |
|---|---|
email.inbound.received | A new inbound email was received and parsed. |
email.outbound.sent | An outbound email was accepted for delivery. |
email.outbound.delivered | An outbound email was delivered to the recipient's mail server. |
email.outbound.opened | A recipient opened an outbound email (pixel tracking). |
email.outbound.clicked | A recipient clicked a link in an outbound email. |
email.outbound.bounced | An outbound email bounced (hard or soft). |
email.outbound.complained | A recipient marked an outbound email as spam. |
email.outbound.failed | An outbound email permanently failed to send. |
email.outbound.scheduled | An outbound email was scheduled for future delivery. |
email.outbound.cancelled | A scheduled outbound email was cancelled before sending. |
domain.verified | A domain passed DNS verification. |
domain.failed | A domain failed DNS verification after retries. |
webhook.test | A test event sent when you create or test a webhook endpoint. |
Example wire payload
The following delivery event illustrates the snake_case envelope. Event-specific data varies; see the inbound guide for an inbound example.
{
"event_id": "evt_abc123",
"event_type": "email.outbound.delivered",
"timestamp": "2026-04-11T14:30:00Z",
"data": {
"email_id": "em_xyz789",
"alias_id": "alias_abc123",
"to_email": "[email protected]",
"subject": "Hello from Bavimail",
"delivered_at": "2026-04-11T14:30:00Z"
}
}Receiving events
- Return a successful response quickly and process the event asynchronously.
- Be idempotent. Use the
event_idfield to deduplicate. Bavimail may deliver the same event more than once. - Subscribe only to events you need. Reduces noise and processing load.
Receive email and route it to your application
Parse messages, download attachments, and route inbound traffic via API and webhooks.
Official SDKs with language-specific capability differences
The SDKs do not expose identical surfaces. This page shows the real package names, constructors, auth modes, webhook helpers, and resource coverage for each maintained client.