Files
timebank-cc-public/references/MAIL_BOUNCE_SYSTEM.md
T

6.2 KiB

Mail Bounce System Documentation

Overview

This system provides universal email bounce handling that works with any SMTP server by automatically intercepting all outgoing emails, checking for suppressed recipients, and processing bounce notifications from a dedicated mailbox.

Key Features

  • Universal Protection: Automatically protects ALL mailables (newsletters, contact forms, notifications, etc.)
  • SMTP Server Agnostic: Works with any SMTP server that supports Return-Path headers
  • Threshold-Based Suppression: Conservative approach with configurable bounce thresholds
  • Multi-Profile Support: Handles Users, Organizations, Banks, and Admins
  • Email Verification Management: Resets verification status based on bounce patterns
  • Comprehensive Logging: Tracks all bounce processing and suppression actions

How It Works

  1. Outgoing Emails: All emails automatically get Return-Path and tracking headers
  2. Bounce Collection: Bounces are sent to a dedicated mailbox (e.g., bounces@yourdomain.org)
  3. Bounce Processing: System reads the mailbox and categorizes bounces (hard/soft/unknown)
  4. Threshold Checking: Only definitive hard bounces count toward suppression thresholds
  5. Automatic Protection: Suppressed emails are blocked from all future mailings

Configuration

Configure bounce thresholds in config/timebank-cc.php under mailing.bounce_thresholds:

  • verification_reset_threshold: Hard bounces before email_verified_at is reset (default: 2)
  • suppression_threshold: Hard bounces before email is suppressed (default: 3)
  • counting_window_days: Time window for counting bounces (default: 30 days)

Configure automatic cleanup in config/timebank-cc.php under mailing.bounce_thresholds.automatic_cleanup:

  • day_of_week: Day for cleanup (0=Sunday, 1=Monday, etc.) (default: 1)
  • time: Time to run cleanup in 24-hour format (default: '03:00')
  • cleanup_days: Days after which to delete soft bounces (default: 90)
  • cleanup_bounce_types: Bounce types to cleanup, hard bounces are preserved (default: ['soft', 'unknown'])

Set bounce mailbox in .env:

  • BOUNCE_PROCESSING_ENABLED: Enable/disable bounce processing (set to false on local/staging without IMAP)
  • MAIL_BOUNCE_ADDRESS: Return-Path address for bounces
  • BOUNCE_MAILBOX, BOUNCE_HOST, etc.: IMAP settings for bounce processing

Artisan Commands

Process Bounce Emails

# Test bounce processing (dry run)
php artisan mailings:process-bounces --dry-run

# Process bounces and delete from mailbox
php artisan mailings:process-bounces --delete

# Process with custom mailbox settings
php artisan mailings:process-bounces --mailbox=bounces@example.com --host=imap.example.com --username=bounces@example.com --password=secret --ssl --delete

Manage Bounced Emails

# Show bounce statistics and threshold information
php artisan mailings:manage-bounces stats

# List all bounced emails
php artisan mailings:manage-bounces list

# Check bounce counts for specific email
php artisan mailings:manage-bounces check-thresholds --email=user@example.com

# Check all emails against thresholds
php artisan mailings:manage-bounces check-thresholds

# Manually suppress an email
php artisan mailings:manage-bounces suppress --email=problem@example.com

# Remove suppression from an email
php artisan mailings:manage-bounces unsuppress --email=fixed@example.com

# Clean up old soft bounces
php artisan mailings:manage-bounces cleanup --days=90

Simulate or process bounces — bounce:simulate

Works in two modes depending on environment:

Inject mode (local dev — no mailbox needed):

# Check current state without recording
php artisan bounce:simulate --email=user@example.com --status

# Record a hard bounce (triggers threshold logic)
php artisan bounce:simulate --email=user@example.com

# Record a soft bounce (no threshold action)
php artisan bounce:simulate --email=user@example.com --type=soft

# Reset records between tests
php artisan bounce:simulate --email=user@example.com --clear

# Directly suppress without threshold logic
php artisan bounce:simulate --email=user@example.com --suppress

IMAP mode (production — reads real bounce emails from .env mailbox):

# Dry run: verify connection and parsing, record nothing
php artisan bounce:simulate --imap --dry-run

# Process and record all bounces found
php artisan bounce:simulate --imap

# Process, record, and clear mailbox
php artisan bounce:simulate --imap --delete

Skips gracefully with a warning if IMAP credentials are not set in .env.

See references/BOUNCE_SIMULATE_COMMAND.md for full documentation.

Bounce Types

  • Hard Bounce: Permanent failure (user unknown, invalid domain) - counts toward thresholds
  • Soft Bounce: Temporary failure (mailbox full, server down) - recorded but ignored
  • Unknown: Unclassified bounces - recorded but ignored

Threshold Actions

Thresholds are configured in config/timebank_cc.php under mailing.bounce_thresholds. Current defaults:

  • verification_reset_threshold hard bounces: email_verified_at set to null for all profiles
  • suppression_threshold hard bounces: Email suppressed from all future mailings

Only bounces matching a definitive_hard_bounce_pattern AND with type in counted_bounce_types count toward thresholds.

Automatic Scheduling

The system automatically schedules the following tasks in app/Console/Kernel.php:

Bounce Processing: Every hour (only when BOUNCE_PROCESSING_ENABLED=true)

$schedule->command('mailings:process-bounces --delete')
         ->hourly()
         ->withoutOverlapping()
         ->when(fn() => config('app.bounce_processing_enabled', false));

Note: Set BOUNCE_PROCESSING_ENABLED=false in .env on local development and staging environments to prevent IMAP connection errors.

Automatic Cleanup: Configurable via timebank-cc.php config (default: Mondays at 3:00 AM)

  • Cleans up soft bounces older than the configured days (default: 90 days)
  • Preserves hard bounces for suppression decisions
  • Schedule automatically updates based on config settings

Logging

The system logs all bounce processing and suppression actions with "MAILING:" prefix in Laravel logs. Monitor logs to track system activity and troubleshoot issues.