a2zwebltd / laravel-email-sequences
A portable Laravel drip / lifecycle email-sequence engine — enrol users on registration, schedule time-delayed emails (onboarding, trial nurture, win-back), deliver them on a cron with pluggable eligibility rules, and manage everything from Nova.
Package info
github.com/a2zwebltd/laravel-email-sequences
pkg:composer/a2zwebltd/laravel-email-sequences
Requires
- php: ^8.2
- laravel/framework: ^11.0|^12.0|^13.0
Requires (Dev)
- laravel/pint: ^1.25
- orchestra/testbench: ^9.0|^10.0|^11.0
- pestphp/pest: ^3.0|^4.0
- pestphp/pest-plugin-laravel: ^3.0|^4.0
Suggests
- laravel/nova: Auto-registers Nova resources (EmailSequence, EmailSequenceDelivery) when present.
Provides
None
Conflicts
None
Replaces
None
README
A portable Laravel drip / lifecycle email engine. Enrol users into time-delayed email sequences (onboarding, trial nurture, win-back, feedback), deliver them on a cron, and stop a user's sequence the moment they convert — all driven by config and pluggable eligibility rules, with ready-made Nova admin resources.
Designed to drop into any Laravel app while leaving the host in control of its user model, eligibility rules, branding and notifications.
Requirements
- PHP 8.2+
- Laravel 11 / 12 / 13
laravel/nova^5 (optional — auto-registers Nova resources when present)
Installation
composer require a2zwebltd/laravel-email-sequences php artisan migrate php artisan vendor:publish --tag=email-sequences-config # optional php artisan vendor:publish --tag=email-sequences-views # optional, to rebrand emails
Add the trait to your User model:
use A2ZWeb\EmailSequences\Concerns\HasEmailSequences; class User extends Authenticatable { use HasEmailSequences; }
That's it — with auto_enroll on (the default), every newly registered user is
enrolled into all sequences and delivery runs hourly.
How it works
- Configure your sequences in
config/email-sequences.php(code, delay, subject, view). They seed theemail_sequencestable on the first migrate; after that, runphp artisan email-sequences:syncwhenever you change them. - Enrolment — new users are auto-enrolled (or call
$user->enrollInEmailSequences()yourself). Each sequence is scheduled relative to the user's registration date. - Delivery — the
emails:deliver-sequencescommand (scheduled hourly) sends every due message, firesSequenceDelivered, and marks it sent. - Eligibility —
should_enrollgates who gets enrolled;should_continuestops a user's remaining deliveries (e.g. once they upgrade to a paid plan). A sequence entry may also define its ownshould_sendgate — when it returns false at delivery time, that single message is skipped (marked sent, never retried) while the rest of the user's sequence continues (e.g. skip a "make your first call" nudge for users who already called the API).
Use emails:backfill-enrollments to enrol users that pre-date installation. It
only picks up users with no deliveries at all, and schedules from each user's
created_at, so long-standing users get every overdue email on the next run.
Changing sequences after the first migrate
Enrolment reads name and days_delay from the email_sequences table, not
from config. After adding, renaming, re-timing or removing a sequence, push the
config into the table:
php artisan email-sequences:sync --dry-run # preview php artisan email-sequences:sync # add missing codes, update name/days_delay php artisan email-sequences:sync --prune # also soft-delete codes removed from config
The command is idempotent: it matches rows by sequence_code, updates only the
oldest live row per code (the one enrolment uses), revives a soft-deleted row
instead of inserting a new one, and never creates duplicates. By default it
leaves already-scheduled deliveries alone; add --reschedule to shift pending
deliveries by the change in days_delay. Existing users are not enrolled into a
newly added sequence; schedule those deliveries yourself if you want them.
Configuration
See config/email-sequences.php:
| Key | Purpose |
|---|---|
user_model |
The model enrolled into sequences |
sequences |
The sequence set: code => [name, days_delay, subject, view, should_send?] |
sequences.*.should_send |
fn ($user) => bool — skip just this message when false (invokable class-string recommended so the config stays cacheable) |
auto_enroll |
Enrol every newly-created user automatically |
should_enroll |
fn ($user) => bool — gate enrolment |
should_continue |
fn ($user) => bool — stop a user's sequence when false |
schedule.deliver / schedule.cron |
Self-scheduling of the delivery command |
schedule.without_overlapping |
Skip a tick while the previous delivery run is still going (default true) |
schedule.on_one_server |
Run the delivery on one server only; needs a cache store with atomic locks (default false) |
nova.group / nova.user_resource |
Nova menu group and User resource for relations |
Events
| Event | When |
|---|---|
SequenceDelivered |
A sequence email was delivered (in-app notifications, analytics) |
Commands
| Command | Purpose |
|---|---|
emails:deliver-sequences |
Deliver all due sequence emails (self-scheduled hourly) |
emails:backfill-enrollments |
Enrol users that have no deliveries yet |
email-sequences:sync |
Sync email_sequences rows from config (--prune, --reschedule, --dry-run) |
AI agents (Laravel Boost)
The package ships Laravel Boost resources: a
short guideline that is always loaded, and an email-sequences-development
skill loaded on demand. Boost 2 or newer is required. In the host app:
composer require laravel/boost --dev php artisan boost:install # first time php artisan boost:update --discover # already using Boost
Select a2zwebltd/laravel-email-sequences when prompted.
Testing
composer test
License
MIT — see LICENSE file.