Webhooks

Subscribe to email delivery events and receive real-time notifications.

Overview

MailSetu sends HTTP POST requests to your endpoint when email events occur. Supported events: • email.queued — Email added to send queue • email.scheduled — Scheduled send registered • email.sending — Handoff to SES started • email.delivered — Email accepted by the recipient's server • email.open — Recipient opened the email • email.click — Recipient clicked a link • email.bounce — Hard or soft bounce • email.complaint — Marked as spam • email.failed — Send failed after retries • email.canceled — Scheduled email was canceled

Create a webhook endpoint

Go to Dashboard → Webhooks → Add Endpoint. Add your HTTPS URL and select events to subscribe to.

Verify webhook signatures

Every webhook includes a X-MailSetu-Signature header. Verify it to ensure the request came from MailSetu.

typescript
import crypto from 'crypto'

export function verifyMailSetuSignature(
  rawBody: string,
  timestamp: string,
  signature: string,
  secret: string
): boolean {
  const payload = `${timestamp}.${rawBody}`
  const expected = 'v1=' + crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex')
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signature)
  )
}

// In your webhook handler:
app.post('/webhooks/mailsetu', express.raw({ type: 'application/json' }), (req, res) => {
  const sig = req.headers['x-mailsetu-signature'] as string
  const ts  = req.headers['x-mailsetu-timestamp'] as string
  const raw = req.body.toString()

  if (!verifyMailSetuSignature(raw, ts, sig, process.env.WEBHOOK_SECRET!)) {
    return res.status(401).send('Invalid signature')
  }

  const event = JSON.parse(raw)
  console.log(event.type, event.data.emailId)
  res.status(200).send('OK')
})

Webhook payload example

json
{
  "type": "email.delivered",
  "data": {
    "emailId": "email-xxxxxxxxxxxxxxxx",
    "to": ["user@example.com"],
    "from": "noreply@acme.co",
    "subject": "Welcome!",
    "status": "DELIVERED",
    "timestamp": "2026-04-23T10:30:00.000Z"
  }
}