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
- 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
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_thresholdhard bounces:email_verified_atset to null for all profilessuppression_thresholdhard 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.