innoge/laravel-msgraph-mail
Send Laravel mail through Microsoft Graph (Office 365/Outlook) with a native mail transport. Configure your Azure app credentials, then use Laravel's Mail facade as usual—no SMTP needed. Supports attachments, HTML mail, and modern Graph authentication for reliable, scalable delivery.
save_to_sent_items additionally supports audit/retention requirements ("all outbound mail must be retained in the mailbox").cache:clear step are easy to forget.Mail.ReadWrite application permission. This is a genuine product decision: "do we support large attachments, and are we willing to ask the customer's admin for a broader permission scope?" For most products the answer is "no — send a download link," which keeps the permission request minimal and materially eases admin consent.from address must be a real, licensed mailbox (user or shared) in the tenant, and per-Mailable ->from() overrides retarget the Graph call. That makes "send as a shared mailbox like [email protected]" a shippable capability — but it also means each sending identity is an IT provisioning task, not a config change. Plan the onboarding flow accordingly.Adopt when most of these hold:
MAIL_MAILER switch, not an SDK you code against).sendMail per-mailbox (on the order of ~30 messages/minute and ~10k recipients/day — verify current published limits before committing to a volume assumption). This is fine for per-user notifications and wrong for fan-out.Mail.Send application permission and admin consent. That is a governance dependency, not a coding task, and it is the single largest schedule risk in adoption. Budget days-to-weeks for a customer's IT to approve, not hours.$tries, backoff) rather than send synchronously — Graph is transiently slow and will occasionally time out. If your architecture assumes a synchronous send in the request path, fix that first.Look elsewhere when:
Mail.Send application permission with admin consent. If the customer's security team won't grant tenant-wide mail-send to your app, this package cannot work; there is no delegated-user alternative here.Adoption notes worth writing into the ticket: pin to 2.x and read UPGRADE.md — 1.x differs on save_to_sent_items scoping, large-mail handling, and certificate auth. After rotating credentials or granting new Graph permissions, php artisan cache:clear is mandatory because tokens are cached per tenant+client for their lifetime. Custom headers only survive if prefixed X-; recipient display names are dropped by Graph. These are all small but they are the exact things that generate support tickets.
For executives (one paragraph): We can send our product's transactional email directly through the customer's own Microsoft 365 tenant using Microsoft's supported API, instead of SMTP or a third-party email vendor. That means: no new vendor contract, no per-email fees, no customer data passing through an additional processor, and it satisfies the "must originate from our tenant and be retained in our mailbox" requirement that shows up in most enterprise security reviews. Microsoft has already retired the old SMTP authentication path, so this is increasingly the only compliant option for Microsoft-centric customers. The cost is small — it's a configuration change in a Laravel app, not a new subsystem — and the main effort is a one-time Azure app registration that requires the customer's IT admin to click "approve."
For engineering (the summary): It's a Laravel mail transport, installed via auto-discovery, activated by setting MAIL_MAILER=microsoft-graph plus tenant/client credentials in a config/mail.php mailer block. There is no package-specific API — you keep using Mail::, Mailables, and Notifications exactly as today, which means the integration cost is configuration and credential setup, not code. It uses OAuth2 client credentials with Mail.Send application permission; prefer certificate auth over client secrets so we don't own a rotation runbook. Two things to design for up front: (1) queue all sends with retries and backoff — Graph is occasionally slow and we should never block a user request on it; (2) keep attachments small — anything over ~3 MB triggers chunked upload sessions and requires the broader Mail.ReadWrite permission, which is a harder ask of a customer's admin, so default to sending links. Watch three sharp edges: tokens are cached, so credential rotations and new permission grants don't take effect until php artisan cache:clear; only X--prefixed custom headers reach Graph; and the from address must be the mailbox's primary SMTP address (aliases fail with a 404). Finally, treat the dependency as thin and reversible — MIT, one maintainer, no commercial support — so isolate it behind the mailer config and assume we could fork or replace the transport in a day if we had to.
How can I help you explore Laravel packages today?