agavesoft/mailflow
MailFlow is a Laravel/PHP package for building expressive email workflows. Define message sequences, conditions, and delivery steps with a clean API, then integrate them into your app to automate transactional or scheduled mail flow.
expired resolution via GET /api/send/{key}) is the classic "don't build your own retry table" argument. If a roadmap item says "guarantee invoice/legal notifications are delivered," this package is the cheaper path than a bespoke queue + reconciliation service.pending/sending/acked/expired/failed/superseded), stuck-row recovery at 10 min, and heartbeat every 5 min give you the observability needed to actually promise a delivery guarantee and to report on it.reportExternalSend() marks the outbox row superseded (and throws OutboxRowInFlight if the message is already in flight), so a manual fallback send does not produce a second email to the customer. This directly supports a documented incident runbook and a "no double-billing emails" quality bar.provisionPackage, validatePackage, smartmailto:provision --validate) plus the per-project variable catalog means message templates and workflows can live in Git, be reviewed in PRs, validated in CI, and applied all-or-nothing. That's a product-velocity decision: who can change customer-facing copy, and how it gets reviewed.POST {prefix}/smartmailto/pull, signed cursor, PullResolver) plus last-write-wins on updatedAt forces an explicit product decision: which system is the source of truth per attribute. Good support for "app is the system of record, marketing tool mirrors it."fromPath/fromData/fromUpload, plus fromDisk with a fresh 24 h signed URL per retry, fromUrl with sha256) unblocks e-invoicing and statement delivery; the 1 MB inline cap with outbox enabled is a product constraint to design around (force disk/URL for big PDFs).smartmailto:outbox:prune retention defaults (acked 7 days, closed 30 days), per-contact ARCO deletion, identify(..., consent: true) re-consent, and failure events deliberately carrying no personal data give legal/DPO a reviewable story.smartmailto_outbox, smartmailto_outbox_alert_state). That is a real platform decision: does a vendor SDK own tables in the app database, or do we dedicate outbox.connection? It also implies a supervised worker (smartmailto:outbox:work) as a new production dependency.alerts.mail_to + Teams adaptive card at 15 min, 1 h, 4 h, 12 h, 24 h, 48 h, 72 h digest, "recovered"; 422s immediately, grouped per template in 15 min windows) replaces "someone notices emails stopped." Supports a support-tier decision: is failed-notification triage L1 or engineering?SmartmailtoFake covering reportExternalSend(), assertLinked(), assertExternalSendReported(), fakePull() with assertPullContract(), and provisioning asserts determines whether we can keep CI hermetic — a gating criterion in any build-vs-buy scorecard.Notifier interface so switching costs stay bounded — a decision worth making now, not later.Consider it when:
illuminate/database, illuminate/encryption, illuminate/filesystem, illuminate/mail and symfony/yaml (verify the supported Laravel/PHP matrix against your runtime before committing).SMARTMAILTO_OUTBOX_ENABLED=false, SMARTMAILTO_PULL_ENABLED=false and no routes registered) mean you can adopt incrementally, but also that half-enabled deployments silently deliver none of the value.Look elsewhere when:
APP_KEY safely — outbox payloads are encrypted with APP_KEY, so key rotation makes in-flight rows unreadable.For executives (one paragraph): v2.5.0 upgrades this SDK from "a helper that sends email" into a delivery-reliability layer. It adds a transactional outbox — every message is written in the same database transaction as the business event, retried on a defined backoff for up to 72 hours, and only closed when the platform acknowledges it with a key — plus a documented emergency-send path that prevents customers receiving the same invoice twice during an outage. It also brings automated escalation alerts (email and Teams), attachment support (including disk/URL attachments regenerated per retry), and infrastructure-as-code for templates and workflows with CI validation. Cost: two new tables, one supervised worker, and a dependency on server-side feature flags. Net: we can credibly commit to "critical notifications are not lost and not duplicated," and we get an audit trail to prove it.
For engineering: the outbox is off by default and fully backward compatible, but enabling it changes contracts — send() requires sendBefore, a track without occurredAt stores commit time, identify transmits updated_at, and the package owns a migration you must publish with --tag=smartmailto-migrations. Plan a dedicated outbox.connection, add smartmailto:outbox:work to the supervisor, wire SmartmailtoOutboxFailed (needsEmergencySend()) into on-call, and schedule smartmailto:outbox:status, :retry, :prune. Wrap the SDK behind an internal interface; note the README no longer recommends sending directly off health(), and that install is VCS-only with "agavesoft/smartmailto": "^2.5".
For product/ops: templates and workflows become reviewable artifacts (smartmailto:provision --validate in CI, atomic apply, all-or-nothing with per-item error reporting), copy changes stop being a console-only activity, and retention/consent behaviour (prune --contact=, re-consent) is explicit enough to take to legal. Recommended sequencing: adopt with the outbox disabled, enable it for exactly one high-value flow (e.g., invoice delivery), measure ack rate and duplicate rate for a release cycle, then expand.
How can I help you explore Laravel packages today?