EEmailForDevs.com
Compliance9 min read

Email List Hygiene: Automated Bounce Handling and Suppression

Build robust bounce processing and suppression list management

SO

Sarah Okonkwo

Deliverability Specialist

· June 18, 2025

Why List Hygiene Directly Impacts Deliverability

Email service providers like Gmail and Microsoft track your bounce rate as a primary signal of sender quality. If more than 2% of your emails bounce, you are on a path toward deliverability problems. Above 5%, you risk being throttled or blocked entirely. Every hard bounce to an invalid address tells the receiving server that you do not maintain your list, and that signal poisons your reputation for all recipients on that domain.

List hygiene is not a one-time cleanup. It is an ongoing process that should be automated into your email infrastructure. Hard bounces need immediate suppression. Soft bounces need monitoring and eventual suppression. Unengaged subscribers need re-engagement sequences followed by removal. Spam complaints need immediate suppression. Building these automations protects your sender reputation around the clock.

Understanding Bounce Types

Hard bounces are permanent delivery failures. The email address does not exist, the domain does not exist, or the receiving server has permanently rejected your message. Hard bounces should be added to your suppression list immediately and never emailed again. Common SMTP codes for hard bounces include 550 (mailbox not found), 551 (user not local), and 552 (mailbox full, though this is sometimes a soft bounce).

Soft bounces are temporary delivery failures. The recipient\'s mailbox is full, the server is temporarily unavailable, or the message is too large. Soft bounces should be retried (most ESPs handle this automatically) and monitored over time. If an address soft bounces consistently across three or more consecutive sends, treat it as a hard bounce and suppress it.

// Bounce classification and handling logic
type BounceType = "hard" | "soft" | "block" | "spam-complaint";

interface BounceEvent {
  email: string;
  type: BounceType;
  code: string;
  message: string;
  timestamp: string;
}

async function handleBounce(event: BounceEvent): Promise<void> {
  switch (event.type) {
    case "hard":
    case "spam-complaint":
      // Immediate suppression
      await suppressionList.add({
        email: event.email,
        reason: event.type,
        code: event.code,
        suppressedAt: event.timestamp
      });
      break;

    case "soft":
      // Track consecutive soft bounces
      const count = await bounceTracker.increment(event.email);
      if (count >= 3) {
        await suppressionList.add({
          email: event.email,
          reason: "consecutive-soft-bounces",
          code: event.code,
          suppressedAt: event.timestamp
        });
      }
      break;

    case "block":
      // Log and alert - may indicate IP/domain reputation issue
      await alerting.notify({
        severity: "warning",
        message: `Send blocked by ${extractDomain(event.email)}: ${event.message}`
      });
      break;
  }
}

Building a Suppression List System

Your suppression list is the single most important data structure in your email system. Before every send, you must check the recipient against this list. The list should include hard bounces, spam complaints, manual unsubscribes, and addresses that have been consistently unresponsive. It should be stored in a fast-lookup data store (Redis, DynamoDB, or a database with a unique index on the email column).

// Suppression list implementation with Brew
import { Brew } from "@brew/sdk";

const brew = new Brew({ apiKey: process.env.BREW_API_KEY });

// Brew maintains a suppression list automatically,
// but you can also manage it via API

// Add to suppression list
await brew.suppression.add({
  email: "bounced@example.com",
  reason: "hard_bounce"
});

// Check before sending (Brew does this automatically)
const status = await brew.suppression.check("user@example.com");
if (status.suppressed) {
  console.log(`Suppressed: ${status.reason} at ${status.suppressedAt}`);
}

// Bulk import suppression entries
await brew.suppression.import({
  entries: [
    { email: "old1@example.com", reason: "hard_bounce" },
    { email: "old2@example.com", reason: "spam_complaint" },
  ]
});

A critical design decision is whether your suppression list is global (one list for all email types) or segmented (separate lists for marketing, transactional, etc.). For CAN-SPAM compliance, spam complaints and unsubscribes from marketing email should not suppress transactional email like password resets. However, hard bounces should be global since the address simply does not exist. Most modern ESPs including Brew, SendGrid, and Postmark support this distinction natively.

Webhook-Based Bounce Processing

The most reliable way to process bounces is through ESP webhooks rather than parsing bounce emails. Every major ESP provides webhook notifications for bounces, spam complaints, and unsubscribes. Set up a webhook endpoint, validate the incoming payloads, and process events in near real-time.

// Express webhook handler for bounce processing
import express from "express";
import crypto from "crypto";

const app = express();

app.post("/webhooks/email-events", express.json(), async (req, res) => {
  // Verify webhook signature (implementation varies by ESP)
  const signature = req.headers["x-brew-signature"] as string;
  const isValid = verifyWebhookSignature(
    JSON.stringify(req.body),
    signature,
    process.env.BREW_WEBHOOK_SECRET!
  );

  if (!isValid) {
    return res.status(401).json({ error: "Invalid signature" });
  }

  const events = req.body.events;
  for (const event of events) {
    switch (event.type) {
      case "email.bounced":
        await handleBounce(event);
        break;
      case "email.complained":
        await handleSpamComplaint(event);
        break;
      case "email.unsubscribed":
        await handleUnsubscribe(event);
        break;
    }
  }

  res.status(200).json({ received: true });
});

Re-Engagement Campaigns

Before removing inactive subscribers from your list, give them a chance to re-engage. A re-engagement sequence serves two purposes: it recovers some percentage of disengaged subscribers, and it gives you a defensible reason to remove the rest. Typically, you define "inactive" as no opens or clicks in the last 90-180 days.

A three-email re-engagement sequence works well. The first email (sent at day 0) should acknowledge the inactivity with a subject line like "We miss you" or "Are you still interested in [product]?" The second email (day 7) offers something genuinely valuable: a free resource, a discount, or exclusive content. The third email (day 14) is the final notice: "We\'ll remove you from our list unless you click here to stay subscribed."

After the third email, wait 7 days. Anyone who has not engaged with any of the three emails should be moved to a suppressed-inactive segment. Do not delete them permanently since you may want to reach them through other channels. But stop emailing them. Your active list shrinks, but your engagement metrics and deliverability improve. Platforms like brew.new can automate this entire re-engagement workflow, including the final suppression step.

Ongoing Maintenance and Monitoring

Set up automated monitoring for your list health metrics. Track your bounce rate, spam complaint rate, and unsubscribe rate on every send. Set alerts for when any metric exceeds its threshold: bounce rate above 2%, spam complaint rate above 0.1%, or unsubscribe rate above 0.5%. These thresholds are industry standards that Gmail\'s Postmaster Tools and Microsoft\'s SNDS use to evaluate sender reputation.

Run list validation periodically (quarterly at minimum) using a service like ZeroBounce, NeverBounce, or Kickbox. These services check your list against known invalid addresses, spam traps, and disposable email providers. The cost is minimal compared to the deliverability damage from sending to bad addresses. Some ESPs, including Mailgun and Brew, include built-in list validation as part of their platform, making this a seamless part of your workflow rather than a separate process.

SO

Sarah Okonkwo

Deliverability Specialist

Sarah helps companies land in the inbox, not the spam folder. Her background spans DNS authentication, ISP relations, and compliance.