Back to Blog
WebhooksTutorialAPI

Complete Guide to Email Webhooks: Implementation & Best Practices

February 5, 2026 8 min read

Email webhooks are the backbone of modern email delivery systems. They enable your application to react to email events in real-time. In this guide, we'll walk through implementing email webhooks with Void Relay.

What Are Email Webhooks?

Email webhooks are HTTP callbacks that notify your application when email events occur:

  • Real-time Delivery Tracking: Know instantly when emails are delivered or fail
  • Bounce Management: Handle hard and soft bounces automatically
  • Status Updates: Track sent, delivered, and failed email statuses

Webhook Event Types

email.sent

Email was successfully accepted by your SMTP server

email.delivered

Email was successfully delivered to recipient

email.bounced

Email was rejected by the recipient's mail server

email.opened

Recipient opened the email

Step 1: Configure Your Webhook URL

  1. Log in to your dashboard
  2. Navigate to Webhooks in the sidebar
  3. Click Add Webhook
  4. Enter your endpoint URL (must be HTTPS)
  5. Select the events you want to receive

Step 2: Verify Webhook Payloads

Implement webhook verification by checking the signature header against your webhook secret:

import { createHmac } from 'crypto';

// Your webhook secret from the dashboard
const WEBHOOK_SECRET = process.env.RELAY_WEBHOOK_SECRET;

// In your webhook handler
function verifyWebhookSignature(signature, payload) {
  const expectedSignature = createHmac('sha256', WEBHOOK_SECRET)
    .update(payload)
    .digest('hex');
  
  return signature === expectedSignature;
}

const signature = request.headers['x-webhook-signature'];
const payload = request.body;

if (verifyWebhookSignature(signature, payload)) {
  const event = JSON.parse(payload);
  handleWebhookEvent(event);
} else {
  throw new Error('Invalid webhook signature');
}

Implement signature verification using your webhook secret from the dashboard.

Step 3: Next.js Example

// app/api/webhooks/email/route.ts
import { createHmac } from 'crypto';
import { NextRequest, NextResponse } from 'next/server';

const WEBHOOK_SECRET = process.env.RELAY_WEBHOOK_SECRET!;

function verifySignature(signature: string, payload: string): boolean {
  const expectedSignature = createHmac('sha256', WEBHOOK_SECRET)
    .update(payload)
    .digest('hex');
  return signature === expectedSignature;
}

export async function POST(request: NextRequest) {
  const payload = await request.text();
  const signature = request.headers.get('x-webhook-signature');

  if (!signature || !verifySignature(signature, payload)) {
    return NextResponse.json({ error: 'Invalid signature' }, { status: 401 });
  }

  const event = JSON.parse(payload);
  
  switch (event.type) {
    case 'email.delivered':
      await handleEmailDelivered(event.data);
      break;
    case 'email.bounced':
      await handleEmailBounced(event.data);
      break;
  }

  return NextResponse.json({ success: true });
}

Webhook Best Practices

  • 1. Always Verify Signatures: Implement signature verification using your webhook secret
  • 2. Respond Quickly: Return 200 OK within 5 seconds
  • 3. Handle Duplicates: Webhooks may be delivered multiple times
  • 4. Implement Idempotency: Use event IDs to track processed events

Ready to Implement Webhooks?

Start building with Void Relay's webhook system today.

Share this article