5.8 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
- Outgoing Emails: All emails automatically get Return-Path and tracking headers
- Bounce Collection: Bounces are sent to a dedicated mailbox (e.g., bounces@yourdomain.org)
- Bounce Processing: System reads the mailbox and categorizes bounces (hard/soft/unknown)
- Threshold Checking: Only definitive hard bounces count toward suppression thresholds
- 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
falseon 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
Test Universal Bounce System
# Test with suppressed email (should be blocked)
php artisan test:universal-bounce --scenario=suppressed --email=test@example.com
# Test with normal email (should work)
php artisan test:universal-bounce --scenario=normal --email=test@example.com
# Test with mixed recipients
php artisan test:universal-bounce --scenario=mixed
# Test specific mailable types
php artisan test:universal-bounce --scenario=normal --mailable=contact
Test Bounce Threshold System
# Test single bounce (no action)
php artisan test:bounce-system --scenario=single
# Test verification reset threshold (2 bounces)
php artisan test:bounce-system --scenario=threshold-verification
# Test suppression threshold (3 bounces)
php artisan test:bounce-system --scenario=threshold-suppression
# Test all scenarios
php artisan test:bounce-system --scenario=multiple
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
- 1 Hard Bounce: Recorded, no action taken
- 2 Hard Bounces: email_verified_at set to null for all profiles with this email
- 3 Hard Bounces: Email suppressed from all future mailings
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.