mettle/sendportal-core
Core Laravel package powering SendPortal, a self-hosted email marketing platform. It provides models, services, and backend logic for managing subscribers, lists, campaigns, templates, and sending email through multiple providers.
Architecture fit
mettle/sendportal-core is the domain kernel of SendPortal, a self-hosted email marketing platform. It is not a thin utility library: it ships models, migrations, jobs, routes, views, and provider/webhook integrations for campaigns, lists, subscribers, templates, sending, and tracking. Architecture fit should therefore be judged as "embedding a marketing platform inside a Laravel estate," not "adding a Composer dependency." It fits teams that already run Laravel with queue workers and a scheduler and want first-party control of subscriber data and sending infrastructure. It is a poor fit for teams wanting a drop-in, versioned SaaS SDK.
Integration feasibility The intended path is the SendPortal application skeleton with this package as core; grafting it into an unrelated Laravel app is possible but requires reconciling auth, queue, scheduler, public webhook routes, and tracking endpoints. Every provider (SES, Postmark, Postal, SMTP) implies separate credential, DNS, and webhook plumbing. v4.0.1 is a patch over v4.0.0 and does not change the integration surface.
Technical risk This release is unusually dense with behavior changes for a patch:
{{email}}, {{first_name}}, {{last_name}} are no longer replaced under the reverted behavior. Any template or content pipeline relying on the interim behavior will regress silently. This is the highest-risk item.Key questions
SubscriptionChange) as they evolve?Stack fit
Laravel with MySQL/Postgres, Redis-backed queues, and a reliably running schedule:run are hard prerequisites — campaign dispatch is scheduler- and queue-driven, not request-driven. Provider fit spans SES (with SNS webhooks), Postmark (including the newly added SubscriptionChange event), Postal, and generic SMTP. Confirm PHP and Laravel constraints from composer.json before scoping; do not assume 4.0.x matches an older pinned runtime.
Migration path
^4.0.1 explicitly. Do not allow a floating constraint that could resolve to 4.0.0.Compatibility
Inventory any local forks or overrides of PostalAdapter, HandleSesWebhook, message-ID extraction, and the merge/render pipeline — all four areas were touched. If we have patches on those files, rebase deliberately rather than fast-forwarding. Verify that custom webhook consumers tolerate the Postmark SubscriptionChange payload and any changed SES handling.
Sequencing
SubscriptionChange and dead subscription links (expect 404, not 500).Maintenance Expect a frequent patch cadence with occasional in-release reversions. Pin versions, stage every bump, and read the changelog for behavioral items rather than only features. Local forks are a maintenance liability here — every upstream touch to the sending or rendering path becomes a rebase.
Support Self-hosted and community-supported: no vendor SLA, no escalation path, and issue resolution depends on contributor availability. The nine first-time contributors are a healthy signal for bus factor but also mean variable familiarity with the sending pipeline, so review latency and fix durability should be watched over the next few releases.
Scaling The slow-campaign-query fix (#204) lands directly on the send path and is the most operationally valuable change in this release for large lists. The SMTP false-success fix (#223) is equally important at scale: silent drops previously masked as successes distort deliverability decisions. Scaling also depends on queue depth and worker concurrency for campaign fan-out and for inbound webhook/tracking ingestion — size those explicitly rather than inheriting defaults.
Failure modes
Ramp-up Requires Laravel fluency plus operational competence in queues, scheduler reliability, and ESP-specific webhook models (SNS topics for SES, Postmark webhook configuration). Budget onboarding time for the provider and webhook layer specifically; that is where new engineers are most likely to misconfigure and where failures are least visible.
How can I help you explore Laravel packages today?