spatie/mailcoach
Self-hosted email marketing for Laravel: manage audiences, send campaigns with segmentation and A/B testing, track analytics, build automation workflows, and handle transactional emails—all in one Mailcoach app integrated with your project.
Installation
composer require spatie/mailcoach
php artisan vendor:publish --provider="Spatie\Mailcoach\MailcoachServiceProvider" --tag="mailcoach-migrations"
php artisan migrate
php artisan vendor:publish --provider="Spatie\Mailcoach\MailcoachServiceProvider" --tag="mailcoach-config"
php artisan vendor:publish --provider="Spatie\Mailcoach\MailcoachServiceProvider" --tag="mailcoach-assets"
Configure Mail Driver
Update .env to use mailcoach as your default mail driver:
MAIL_MAILER=mailcoach
Create a Campaign
php artisan mailcoach:campaign
Follow prompts to define subject, body, and recipient list.
Send Test Email
use Spatie\Mailcoach\Facades\Mailcoach;
Mailcoach::send('campaign-name', ['user@example.com']);
database/migrations/ for schema changesconfig/mailcoach.php for customizationresources/views/vendor/mailcoach/ for UI overridesCreate/Edit Campaigns:
Use the mailcoach:campaign Artisan command or the built-in UI to design campaigns with:
// Programmatically create a campaign
$campaign = \Spatie\Mailcoach\Models\Campaign::create([
'name' => 'Welcome Series',
'subject' => 'Welcome to our platform!',
'body' => '<h1>Hello!</h1><p>Thanks for joining.</p>',
]);
Schedule & Send:
$campaign->schedule->update(['sent_at' => now()->addHours(1)]);
$campaign->send();
Trigger-Based Emails: Use events to trigger workflows (e.g., user signup, order completion).
// In EventServiceProvider
protected $listen = [
'user.registered' => ['Spatie\Mailcoach\Listeners\TriggerWorkflow', 'handle'],
];
$user->tags()->sync(['vip', 'new-2023']);
Delayed Actions:
// Delay a workflow step by 3 days
$workflowStep->delay = 3 * 24 * 60; // in minutes
$workflowStep->save();
Override Default Mailer:
// In a controller
Mail::to('user@example.com')
->mailcoach('emails.welcome')
->send();
resources/views/vendor/mailcoach/emails/.Track Opens/Click:
Automatically logs analytics via mailcoach:track-opens and mailcoach:track-clicks middleware.
Sync Contacts:
// Add a contact
$contact = \Spatie\Mailcoach\Models\Contact::create([
'email' => 'user@example.com',
'name' => 'John Doe',
'custom_fields' => ['plan' => 'premium'],
]);
// Bulk import via CSV
php artisan mailcoach:import-contacts path/to/file.csv
Segmentation: Use tags or custom fields for filtering:
$contacts = \Spatie\Mailcoach\Models\Contact::whereHas('tags', function($q) {
$q->where('name', 'active-users');
})->get();
Laravel Events:
Listen for Mailcoach\Events\CampaignSent or Mailcoach\Events\ContactSubscribed to extend functionality.
event(new \Spatie\Mailcoach\Events\CampaignSent($campaign));
API Access: Use the built-in API for headless setups:
$response = Http::post('http://your-app.test/api/mailcoach/campaigns', [
'name' => 'API Campaign',
'subject' => 'Hello via API',
'body' => '<p>API-powered email</p>',
]);
Queue Workers: Run the queue worker for async processing:
php artisan queue:work --queue=mailcoach
Mail Driver Misconfiguration:
MAIL_MAILER=mailcoach is set in .env. Forgetting this causes emails to fail silently.php artisan config:get mail.mailer.Queue Stuck on "Processing":
mailcoach_schedules table).storage/logs/laravel.log).php artisan mailcoach:retry-failed
Tracking Pixel Blocking:
mailcoach:track-opens middleware with a fallback:
if (!Mailcoach::isTrackingPixelBlocked()) {
Mailcoach::trackOpen($email);
}
Custom Fields Not Syncing:
custom_fields column is defined in the contacts table.php artisan migrate:fresh --path=/vendor/spatie/mailcoach/database/migrations
Memory Limits:
mailcoach:import-contacts) may hit memory limits.memory_limit in php.ini or chunk imports:
$chunkSize = 100;
$contacts->chunk($chunkSize, function ($chunk) {
// Process chunk
});
Log Emails:
Enable debug mode in config/mailcoach.php:
'debug' => env('MAILCOACH_DEBUG', false),
Logs appear in storage/logs/mailcoach.log.
SQL Queries: Use Laravel Debugbar to inspect queries:
composer require barryvdh/laravel-debugbar
Test Mode:
Disable sending in config/mailcoach.php:
'test_mode' => env('MAILCOACH_TEST_MODE', false),
Emails are stored in the database but not sent.
Default From Address:
Override in config/mailcoach.php:
'from' => [
'address' => 'noreply@yourdomain.com',
'name' => 'Your Brand',
],
Asset Paths: Customize asset paths if using a CDN:
'assets' => [
'path' => 'cdn/yourdomain.com/assets/mailcoach',
],
Queue Connections: Specify a custom queue for Mailcoach:
'queue' => 'mailcoach',
Then configure the queue in .env:
QUEUE_CONNECTION=database
Custom Email Templates: Override default templates by publishing assets:
php artisan vendor:publish --tag=mailcoach-assets
Edit files in resources/views/vendor/mailcoach/.
API Extensions:
Add custom endpoints by extending the Spatie\Mailcoach\Http\Controllers namespace.
Webhook Listeners: Trigger external actions via webhooks:
// In config/mailcoach.php
'webhooks' => [
'campaign_sent' => 'https://your-app.com/webhooks/mailcoach',
],
Custom Fields:
Extend the contacts table schema:
// In a migration
Schema::table('mailcoach_contacts', function (Blueprint $table) {
$table->string('custom_field')->nullable();
});
Update the UI via JavaScript hooks in resources/js/mailcoach.js.
How can I help you explore Laravel packages today?