Download the PHP package r0bdiabl0/laravel-email-tracker without Composer
On this page you can find all versions of the php package r0bdiabl0/laravel-email-tracker. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download r0bdiabl0/laravel-email-tracker
More information about r0bdiabl0/laravel-email-tracker
Files in r0bdiabl0/laravel-email-tracker
Package laravel-email-tracker
Short Description Multi-provider email tracking for Laravel - track opens, clicks, bounces, complaints across SES, Resend, Postal, and more
License MIT
Informations about the package laravel-email-tracker
Laravel Email Tracker
A multi-provider email tracking and bounce management package for Laravel 11+ that provides unified tracking for opens, clicks, bounces, complaints, and deliveries across AWS SES, Resend, Postal, Mailgun, SendGrid, and Postmark. Includes optional suppression to automatically skip sending to problematic addresses.
Table of Contents
- What This Package Does
- What This Package Does NOT Do
- Requirements
- Installation
- Configuration
- Basic Usage
- Webhook Setup
- AWS SES Setup
- Resend Setup
- Mailgun Setup
- SendGrid Setup
- Postmark Setup
- Postal Setup
- Security Considerations
- One-Click Unsubscribe (RFC 8058)
- Events
- Suppression (Bounce Management)
- Database Schema
- Querying Data
- Migrating from juhasev/laravel-ses
- Admin Panel Plugins
- Laravel Boost AI Integration
- Extending
- Custom Providers
- Custom Models
- Testing
- Troubleshooting
- Contributing
- License
- Credits
What This Package Does
- Tracks Sent Emails - Stores records of all emails sent through the package with their message IDs
- Open Tracking - Injects a 1x1 tracking pixel to detect when recipients open emails
- Link Click Tracking - Rewrites links to track when recipients click them, with click counts
- Bounce Handling - Receives and processes bounce notifications from email providers via webhooks
- Complaint Handling - Tracks spam complaints reported by recipients
- Delivery Confirmation - Records successful deliveries reported by email providers
- Batch Grouping - Organize emails into named batches for campaigns or bulk sends
- Multi-Provider Support - Unified interface across 6 major email providers
- Suppression - Optionally skip sending to previously bounced or complained addresses (bounce management)
- One-Click Unsubscribe - RFC 8058 compliant List-Unsubscribe headers for improved deliverability
- Event Dispatching - Laravel events for all tracking activities for your own listeners
What This Package Does NOT Do
- Does NOT send emails - This package tracks emails sent via Laravel's mail system. You still need to configure Laravel Mail with your provider (SES, Mailgun, etc.)
- Does NOT provide SMTP services - You need your own email provider account
- Does NOT guarantee open tracking accuracy - Many email clients block tracking pixels. Open tracking should be considered a lower-bound estimate
- Does NOT track replies - This package tracks delivery events, not incoming mail
- Does NOT provide analytics dashboards - It stores data in your database; you build your own reports or use tools like Filament
- Does NOT provide a template builder - You design emails using Laravel's Mailable and Blade views (which are fully supported and tracked)
- Does NOT replace your email provider's dashboard - It supplements it with data in your own database
Requirements
- PHP 8.2+
- Laravel 11.0+
- An email provider account (AWS SES, Resend, Postal, Mailgun, SendGrid, or Postmark)
Installation
Run the install command:
This will:
- Publish the configuration file to
config/email-tracker.php - Publish the migrations
- Optionally run the migrations
Configuration
Environment Variables
Add these to your .env file:
Table Names
By default, tables are created without a prefix:
sent_emailsemail_opensemail_bouncesemail_complaintsemail_linksbatches
With a prefix like tracker:
tracker_sent_emailstracker_email_bounces- etc.
Enable Providers
Enable only the providers you use:
Transport Configuration
The package provides custom Symfony transports for providers that need HTTP API access for full tracking support. Configure these in your config/mail.php:
Provider Transport Summary:
| Provider | Transport Type | SDK Required |
|---|---|---|
| AWS SES | Laravel built-in (ses) |
aws/aws-sdk-php (included) |
| Resend | Package transport (resend) |
resend/resend-php (optional) |
| Postal | Package transport (postal) |
postal/postal (optional) |
| Mailgun | Symfony built-in (mailgun) |
symfony/mailgun-mailer |
| Postmark | Symfony built-in (postmark) |
symfony/postmark-mailer |
| SendGrid | SMTP | None |
Install optional SDKs as needed:
Using Multiple Providers
You can enable multiple providers simultaneously and switch between them per-send:
Each provider has its own webhook endpoint. When you receive bounce/complaint notifications, they'll be routed to the correct handler based on the URL:
- SES:
POST /email-tracker/webhook/ses - Resend:
POST /email-tracker/webhook/resend - Mailgun:
POST /email-tracker/webhook/mailgun - etc.
The provider column in the database tracks which service sent each email, allowing you to query statistics by provider.
Basic Usage
Sending Tracked Emails
Using the TracksWithEmail Trait (Optional)
Add the trait to your Mailable for convenience methods:
Using the Notification Channel (Optional)
Webhook Setup
Your email provider will send event notifications (bounces, complaints, deliveries) to these webhook URLs. You must configure these URLs in each provider's dashboard.
Webhook URLs
| Provider | Webhook URL |
|---|---|
| AWS SES | https://your-app.com/email-tracker/webhook/ses/bouncehttps://your-app.com/email-tracker/webhook/ses/complainthttps://your-app.com/email-tracker/webhook/ses/delivery |
| Resend | https://your-app.com/email-tracker/webhook/resend |
| Postal | https://your-app.com/email-tracker/webhook/postal |
| Mailgun | https://your-app.com/email-tracker/webhook/mailgun |
| SendGrid | https://your-app.com/email-tracker/webhook/sendgrid |
| Postmark | https://your-app.com/email-tracker/webhook/postmark |
AWS SES Setup
- Create SNS topics for bounces, complaints, and deliveries in AWS Console
- Add HTTPS subscriptions pointing to your webhook URLs
- Configure your SES domain/email to publish to these SNS topics
- The package automatically validates SNS message signatures
Resend Setup
- Go to Resend Dashboard > Webhooks
- Add a new webhook pointing to
https://your-app.com/email-tracker/webhook/resend - Select events:
email.bounced,email.complained,email.delivered - Copy the signing secret (starts with
whsec_) to your.env
Mailgun Setup
- Go to Mailgun Dashboard > Sending > Webhooks
- Add webhook URLs for Permanent Failures, Temporary Failures, and Delivered
- Copy your webhook signing key to your
.env
SendGrid Setup
- Go to SendGrid Dashboard > Settings > Mail Settings > Event Webhook
- Set the HTTP POST URL to
https://your-app.com/email-tracker/webhook/sendgrid - Select events: Bounced, Spam Reports, Delivered
- Enable Event Webhook Security and copy the verification key
Postmark Setup
- Go to Postmark > Servers > Your Server > Webhooks
- Add webhooks for Bounces, Spam Complaints, and Deliveries
- Set the webhook URL and optionally configure Basic Auth for security
Postal Setup
- Go to your Postal server admin panel
- Add a webhook endpoint pointing to
https://your-app.com/email-tracker/webhook/postal - Configure the shared secret key in your
.env
Security Considerations
Webhook Signature Validation
All providers support webhook signature validation to ensure requests are authentic:
| Provider | Validation Method | Required Config |
|---|---|---|
| AWS SES | SNS certificate validation | Automatic |
| Resend | Svix HMAC-SHA256 | webhook_secret |
| Mailgun | HMAC-SHA256 | webhook_signing_key |
| SendGrid | ECDSA P-256 | verification_key |
| Postmark | Header token or Basic Auth | webhook_token |
| Postal | Header token | webhook_key |
Important: In development, validation is skipped if no secret is configured. In production, always configure your webhook secrets.
Protecting Webhook Routes
The webhook routes are public by default (no auth middleware). This is required because email providers need to access them. Security is provided through signature validation.
If you need additional protection, you can:
- Configure IP allowlists in your web server (nginx/Apache)
- Add custom middleware in the config:
CSRF Protection
Webhook routes must be excluded from CSRF protection since they receive POST requests from external services. The package routes are loaded outside the web middleware group, but if your application applies CSRF middleware globally, you need to exclude the webhook routes.
Add to your bootstrap/app.php (Laravel 11+):
Or in app/Http/Middleware/VerifyCsrfToken.php (Laravel 10):
One-Click Unsubscribe (RFC 8058)
The package supports RFC 8058 compliant one-click unsubscribe headers, which are now required by Gmail, Yahoo, and other major email providers for bulk senders. This feature improves deliverability and helps you comply with sender requirements.
How It Works
- When enabled, the package adds
List-UnsubscribeandList-Unsubscribe-Postheaders to your emails - Email clients show an "Unsubscribe" button in their UI
- When clicked, a POST request is sent to your app's signed unsubscribe endpoint
- The package validates the signature and fires an
EmailUnsubscribeEvent - You handle the business logic in your event listener
Enabling Unsubscribe Headers
Option 1: Global (All Tracked Emails)
Option 2: Per-Email
Configuration
Customizing the Unsubscribe URL
By default the package generates a non-expiring Laravel signed URL pointing at the
email-tracker.unsubscribe route. Set unsubscribe.signature_expiration above 0 to make those
links time-limited — not recommended, since a recipient who opens the email after the window can
no longer unsubscribe.
To use a completely different URL scheme (for example a persistent token URL that does not depend
on APP_KEY), bind your own implementation of UnsubscribeUrlGenerator:
The package always owns the RFC 8058 header assembly (List-Unsubscribe, List-Unsubscribe-Post,
and the optional mailto: fallback) — you only supply the URL, so there is exactly one header path
and no risk of duplicate List-Unsubscribe headers.
Handling Unsubscribe Events
Register a listener for the EmailUnsubscribeEvent:
CSRF Protection
The unsubscribe endpoint needs to be excluded from CSRF protection (it receives POST requests from external email clients):
Note: If you configured a custom route prefix via
EMAIL_TRACKER_ROUTE_PREFIX, update the CSRF exclusion paths accordingly.
Security Recommendations
-
Rate Limiting: Consider adding rate limiting middleware to your
HandleUnsubscribelistener or at the route level to prevent abuse: - Signature Expiration: For added security, set
signature_expirationto expire unsubscribe links after a reasonable time (e.g., 720 hours / 30 days)
What This Feature Does NOT Do
- Does NOT manage subscription lists - you define what "unsubscribe" means for your app
- Does NOT store unsubscribe preferences - you update your own user/subscription models
- Does NOT decide per-list vs global unsubscribe - your listener implements this logic
Events
The package dispatches events for all tracking activities. Listen to these in your EventServiceProvider:
Example Listener
Suppression (Bounce Management)
Automatically skip sending to bounced or complained addresses. This is disabled by default - enable it to protect your sender reputation:
When enabled, suppression works automatically across all sending methods:
EmailTracker::send()facadeTracksWithEmailtrait on MailablesEmailTrackerChannelfor Notifications
If a suppressed address is detected, an AddressSuppressedException is thrown with the email and reason.
Manual Suppression Checking
You can also check suppression manually:
Database Schema
The package creates the following tables (with optional prefix):
sent_emails
| Column | Type | Description |
|---|---|---|
| id | bigint | Primary key |
| provider | string | Email provider (ses, resend, etc.) |
| message_id | string | Provider's message ID |
| string | Recipient email address | |
| batch_id | bigint | Optional batch reference |
| sent_at | timestamp | When email was sent |
| delivered_at | timestamp | When delivery was confirmed |
| bounce_tracking | boolean | Whether bounce tracking is enabled |
| complaint_tracking | boolean | Whether complaint tracking is enabled |
| delivery_tracking | boolean | Whether delivery tracking is enabled |
email_bounces
| Column | Type | Description |
|---|---|---|
| id | bigint | Primary key |
| provider | string | Email provider |
| sent_email_id | bigint | Reference to sent email |
| type | string | Bounce type (Permanent/Transient) |
| string | Bounced email address | |
| bounced_at | timestamp | When bounce occurred |
| metadata | json | Raw webhook payload (for diagnostic details) |
email_complaints
| Column | Type | Description |
|---|---|---|
| id | bigint | Primary key |
| provider | string | Email provider |
| sent_email_id | bigint | Reference to sent email |
| type | string | Complaint type (spam, etc.) |
| string | Complaining email address | |
| complained_at | timestamp | When complaint occurred |
| metadata | json | Raw webhook payload (for diagnostic details) |
email_opens
| Column | Type | Description |
|---|---|---|
| id | bigint | Primary key |
| sent_email_id | bigint | Reference to sent email |
| beacon_identifier | string | Unique identifier for tracking pixel |
| opened_at | timestamp | When email was opened |
email_links
| Column | Type | Description |
|---|---|---|
| id | bigint | Primary key |
| sent_email_id | bigint | Reference to sent email |
| link_identifier | string | Unique identifier for link tracking |
| original_url | text | Original link URL |
| clicked | boolean | Whether link has been clicked |
| click_count | integer | Number of clicks |
batches
| Column | Type | Description |
|---|---|---|
| id | bigint | Primary key |
| name | string | Batch identifier |
Metadata Storage Considerations
The metadata column in email_bounces and email_complaints tables can store raw webhook payloads from email providers. This provides valuable diagnostic information but is disabled by default.
Configuration:
Important: Even when store_metadata is false, the raw webhook payload is still available in event listeners via the metadata property. This allows you to process diagnostic information in real-time without persisting it to the database.
When to enable persistent storage:
- You need to analyze bounce/complaint patterns historically
- You want to debug delivery issues after the fact
- You're building reporting dashboards that query metadata
- You don't have real-time event listeners processing webhooks
When to keep disabled (default):
- You process events in real-time via listeners
- You store relevant data in your own application tables
- You want to minimize database storage
- You have PII concerns about storing raw payloads
What metadata contains:
- Full webhook payload from the email provider
- SMTP error codes and diagnostic messages
- Email addresses and timestamps
- Provider-specific debugging information
Storage considerations:
- Payloads vary by provider (typically 1-5 KB per record)
- High-volume senders should monitor database growth
- Consider implementing a cleanup job for old records
Privacy considerations:
- Metadata may contain email addresses (PII)
- Apply appropriate data retention policies
- Ensure database access controls are in place
Example cleanup job:
Querying Data
Migrating from juhasev/laravel-ses
If you're migrating from juhasev/laravel-ses:
The migration will:
- Rename tables (remove
laravel_ses_prefix) - Add
providercolumn with default'ses' - Output new webhook URLs for AWS SNS configuration
Backwards Compatibility
The SesMail facade is aliased to EmailTracker:
Enable legacy routes to keep old webhook URLs working:
Admin Panel Plugins
Filament Plugin
For Filament v3/v4 users, install the companion plugin for dashboard widgets, statistics, and resource pages:
Features:
- Dashboard Widgets - Stats overview, delivery charts, health scores, recent activity
- Resource Pages - Browse, search, and filter sent emails, bounces, and complaints
- Statistics Service - Query aggregated stats for custom integrations
Register in your Filament panel provider:
See r0bdiabl0/laravel-email-tracker-filament for full documentation.
Nova Plugin
For Laravel Nova v4/v5 users, install the companion plugin for resource management:
Features:
- Sent Emails Resource - Browse, search, filter by provider and status
- Bounces Resource - View bounce records with type badges
- Complaints Resource - Track spam complaints
- Read-Only - Safe viewing without accidental modifications
The resources are auto-registered. See r0bdiabl0/laravel-email-tracker-nova for customization options.
Laravel Boost AI Integration
This package includes Laravel Boost AI guidelines and skills to help AI assistants generate correct code for your email tracking implementation.
When you run php artisan boost:install in your Laravel application, Boost automatically loads:
- AI Guidelines - Package overview, API examples, and configuration reference
- Skills - Interactive commands for common tasks:
/send-tracked-email- Send emails with tracking, batches, and unsubscribe headers/handle-email-events- Create event listeners for bounces, complaints, opens, clicks/create-email-provider- Build custom provider integrations/setup-suppression- Configure bounce management
No additional configuration required - Boost discovers the package's AI resources automatically.
Extending
Custom Providers
This package is fully extensible. You can add support for any email provider by implementing your own webhook handler.
Step 1: Create your provider class
Extend AbstractProvider which implements EmailProviderInterface:
Step 2: Register your provider
In your AppServiceProvider or a dedicated service provider:
Step 3: Add configuration (optional)
Step 4: Configure webhooks in your email provider
Your custom provider's webhook endpoint is automatically registered at:
Configure this URL in your email provider's dashboard/settings:
- Set the webhook URL to
https://your-app.com/email-tracker/webhook/custom-smtp - Select event types to receive (bounces, complaints, deliveries, opens, clicks)
- Configure authentication - if your provider supports webhook signing:
- Copy the signing secret/key from your provider
- Add it to your
.env:EMAIL_TRACKER_CUSTOM_SMTP_SECRET=your-secret-here
- Test the webhook - most providers have a "send test" feature
The package handles routing automatically - any POST request to /email-tracker/webhook/{provider-name} will be routed to your provider's handleWebhook() method.
AbstractProvider Helper Methods
The AbstractProvider base class provides useful helper methods:
Example using the helper methods in your handleWebhook():
Custom Models
Override default models:
Your custom model should extend the package model or implement the contract:
Testing
Troubleshooting
Webhooks not receiving data
- Verify the webhook URL is accessible from the internet
- Check your web server logs for incoming requests
- Enable debug logging:
EMAIL_TRACKER_DEBUG=true - Verify signature validation secrets are correct
- Check Laravel logs for validation errors
Open tracking not working
- Open tracking requires HTML emails (not plain text)
- Many email clients block tracking pixels by default
- Gmail, Apple Mail, and others may proxy images
- Consider open tracking as approximate data only
Message IDs not matching
- Ensure you're storing the message ID from the send response
- Different providers format message IDs differently
- Check that the same message ID format is used in webhooks
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create a feature branch
- Make your changes with tests
- Run
composer testandcomposer analyse - Submit a pull request
For bugs and feature requests, please open an issue.
License
The MIT License (MIT). Please see License File for more information.
Credits
- Robert Pettique - Author and maintainer
- Based on the excellent work from juhasev/laravel-ses
All versions of laravel-email-tracker with dependencies
illuminate/contracts Version ^11.0|^12.0|^13.0
illuminate/mail Version ^11.0|^12.0|^13.0
illuminate/support Version ^11.0|^12.0|^13.0
illuminate/notifications Version ^11.0|^12.0|^13.0
illuminate/database Version ^11.0|^12.0|^13.0
aws/aws-sdk-php Version ^3.288
guzzlehttp/guzzle Version ^7.8
aws/aws-php-sns-message-validator Version ^1.7
nesbot/carbon Version ^3.0
voku/simple_html_dom Version ^4.8
ramsey/uuid Version ^4.7
symfony/psr-http-message-bridge Version ^7.0
nyholm/psr7 Version ^1.8