PHPackages                             redberry/mailbox-for-laravel - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Mail &amp; Notifications](/categories/mail)
4. /
5. redberry/mailbox-for-laravel

ActiveLibrary[Mail &amp; Notifications](/categories/mail)

redberry/mailbox-for-laravel
============================

This is my package mailbox-for-laravel

2.3.0(1mo ago)4115.7k↓53.1%4[4 PRs](https://github.com/RedberryProducts/mailbox-for-laravel/pulls)MITPHPPHP ^8.3CI passing

Since Aug 26Pushed 1w ago1 watchersCompare

[ Source](https://github.com/RedberryProducts/mailbox-for-laravel)[ Packagist](https://packagist.org/packages/redberry/mailbox-for-laravel)[ Docs](https://github.com/redberryproducts/mailbox-for-laravel)[ GitHub Sponsors](https://github.com/Redberry)[ RSS](/packages/redberry-mailbox-for-laravel/feed)WikiDiscussions main Synced 2d ago

READMEChangelog (10)Dependencies (56)Versions (37)Used By (0)

Mailbox for Laravel
===================

[](#mailbox-for-laravel)

[![Latest Version on Packagist](https://camo.githubusercontent.com/53e3eb67a3666a714660ffbd0d6b44d3b67da450cd1f0b411a8b30e372b9328c/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f72656462657272792f6d61696c626f782d666f722d6c61726176656c2e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/redberry/mailbox-for-laravel)[![GitHub Tests Action Status](https://camo.githubusercontent.com/b6ba83a8eb9b1d958f1a26f7b7c709f3c0eb42576d722585afb7ffe406a9f13d/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f526564626572727950726f64756374732f6d61696c626f782d666f722d6c61726176656c2f72756e2d74657374732e796d6c3f6272616e63683d6d61696e266c6162656c3d7465737473267374796c653d666c61742d737175617265)](https://github.com/RedberryProducts/mailbox-for-laravel/actions?query=workflow%3Arun-tests+branch%3Amain)[![GitHub Code Style Action Status](https://camo.githubusercontent.com/d256224bd49f018a77ac2bd657832ee30cceb37dde015c700db311ecf6b0a29e/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f526564626572727950726f64756374732f6d61696c626f782d666f722d6c61726176656c2f6669782d7068702d636f64652d7374796c652d6973737565732e796d6c3f6272616e63683d6d61696e266c6162656c3d636f64652532307374796c65267374796c653d666c61742d737175617265)](https://github.com/RedberryProducts/mailbox-for-laravel/actions?query=workflow%3A%22Fix+PHP+code+style+issues%22+branch%3Amain)[![Total Downloads](https://camo.githubusercontent.com/91972a51938c1c9cd77fb91c0921bcacc7e26d2c2e723657e8fe4c83f167e758/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f72656462657272792f6d61696c626f782d666f722d6c61726176656c2e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/redberry/mailbox-for-laravel)

Mailbox for Laravel captures your application's outgoing mail and serves it through a local, self-hosted dashboard — like Mailtrap or Mailhog, but without an external service or a second process to run. It ships with a fluent testing API that, unlike `Mail::fake()`, asserts against the fully rendered message: real HTML, real recipients, real attachments.

Installation
------------

[](#installation)

Require the package via Composer. In most cases you only want this in development:

```
composer require redberry/mailbox-for-laravel --dev
```

Run the install command to publish assets, publish the config file, and run migrations:

```
php artisan mailbox:install
```

Point your mailer at the `mailbox` transport in `.env`:

```
MAIL_MAILER=mailbox
```

The dashboard is then available at `/mailbox` (or whatever path you configure). The package is auto-discovered — no manual provider registration needed.

**Requirements:** PHP 8.3+, Laravel 10 / 11 / 12 / 13.

Capturing Mail
--------------

[](#capturing-mail)

Everything your app sends through Laravel's `Mail` facade is intercepted by the `mailbox` transport and stored before delivery. Visit the dashboard to see captured messages:

- Sorted newest-first with a live-updating list
- HTML, plain-text, and raw RFC 822 views per message
- Attachment preview and download
- Read/unread tracking, single-message delete, and clear-all
- Recipient filtering and search
- A "Send test email" button for smoke tests

Internally, the pipeline is: transport → normalizer → `CaptureService` → paired message/attachment store. The architectural details are in [ARCHITECTURE.md](ARCHITECTURE.md).

### Transport decoration (capture + real delivery)

[](#transport-decoration-capture--real-delivery)

By default, the `mailbox` transport captures mail without sending it. If you want emails to appear in the dashboard **and** be delivered for real (useful in staging or for monitoring production mail), set the `MAILBOX_DECORATE` env variable to any mailer name your app already knows:

```
MAIL_MAILER=mailbox
MAILBOX_DECORATE=smtp
```

This tells the service provider to resolve the `smtp` mailer's Symfony transport and wrap it with `MailboxTransport`. Every outgoing email is captured locally first, then forwarded to the decorated transport for actual delivery. Any mailer registered in `config/mail.php` works — `smtp`, `ses`, `postmark`, `log`, etc.

To go back to capture-only mode, remove `MAILBOX_DECORATE` (or set it to empty).

Testing your emails
-------------------

[](#testing-your-emails)

`Mail::fake()` only tells you a Mailable was queued; it can't tell you whether the rendered email would actually contain what you expect. This package's assertions run against the captured message after Laravel renders it, so you can assert on subject lines, recipients, HTML content, and attachments as the recipient would see them.

Add the `InteractsWithMailbox` trait to your test. It clears the mailbox before every test, and exposes `$this->mailbox()` for assertions.

**Pest:**

```
use Redberry\MailboxForLaravel\Testing\InteractsWithMailbox;

uses(InteractsWithMailbox::class);
```

**PHPUnit:**

```
class OrderEmailTest extends TestCase
{
    use InteractsWithMailbox;
}
```

### Collection-level assertions

[](#collection-level-assertions)

```
use Redberry\MailboxForLaravel\Facades\Mailbox;
use Redberry\MailboxForLaravel\DTO\MailboxMessageData;

Mailbox::assertSentCount(2);
Mailbox::assertNothingSent();
Mailbox::assertSentTo('user@example.com');
Mailbox::assertNotSentTo('admin@example.com');

Mailbox::assertSent(fn (MailboxMessageData $m) => $m->subject === 'Welcome');

Mailbox::assertSent(
    fn (MailboxMessageData $m) => str_contains($m->subject, 'Newsletter'),
    expectedCount: 3,
);
```

### Per-message fluent assertions

[](#per-message-fluent-assertions)

Call `firstSent()` to chain assertions against a single captured message:

```
Mailbox::firstSent()
    ->assertHasSubject('Order Confirmation')
    ->assertFrom('noreply@shop.com')
    ->assertHasTo('buyer@example.com')
    ->assertSeeInHtml('Order #12345')
    ->assertDontSeeInHtml('error')
    ->assertHasAttachment('invoice.pdf', 'application/pdf')
    ->assertAttachmentCount(1);
```

`firstSent()` also accepts a filter callback:

```
Mailbox::firstSent(fn (MailboxMessageData $m) => $m->subject === 'Password Reset')
    ->assertHasTo('user@example.com')
    ->assertSeeInHtml('Reset your password');
```

### Reference — per-message assertions

[](#reference--per-message-assertions)

MethodDescription`assertFrom($email, $name?)`Assert the sender email (and optionally name)`assertHasTo($email, $name?)`Assert a "to" recipient exists`assertHasCc($email, $name?)`Assert a "cc" recipient exists`assertHasBcc($email, $name?)`Assert a "bcc" recipient exists`assertHasReplyTo($email, $name?)`Assert a "reply-to" address exists`assertHasSubject($subject)`Assert exact subject match`assertSubjectContains($substring)`Assert subject contains a substring`assertSeeInHtml($string)`Assert HTML body contains string`assertDontSeeInHtml($string)`Assert HTML body does not contain string`assertSeeInText($string)`Assert text body contains string`assertDontSeeInText($string)`Assert text body does not contain string`assertSeeInOrderInHtml($strings)`Assert strings appear in order in HTML`assertSeeInOrderInText($strings)`Assert strings appear in order in text`assertHasAttachment($filename, $mimeType?)`Assert attachment exists`assertHasNoAttachments()`Assert no attachments`assertAttachmentCount($count)`Assert number of attachments`assertHasHeader($name, $value?)`Assert header exists (optionally with value)### End-to-end example

[](#end-to-end-example)

```
it('sends welcome email with getting started guide', function () {
    $this->post('/register', [
        'name' => 'John Doe',
        'email' => 'john@example.com',
        'password' => 'secret123',
    ]);

    Mailbox::assertSentCount(1);
    Mailbox::assertSentTo('john@example.com');

    Mailbox::firstSent()
        ->assertHasSubject('Welcome, John!')
        ->assertFrom('noreply@myapp.com')
        ->assertSeeInOrderInHtml(['Welcome', 'Getting Started', 'Support'])
        ->assertHasAttachment('getting-started.pdf');
});
```

Configuration
-------------

[](#configuration)

Defaults live in [config/mailbox.php](config/mailbox.php) and work without modification. Publish the config file only — without touching views, assets, or migrations — if you need to customize:

```
php artisan vendor:publish --tag=mailbox-config
```

Add `--force` to overwrite an existing `config/mailbox.php` after a package upgrade.

The keys you're most likely to touch:

- `decorate` — a mailer name (e.g. `smtp`) to forward captured mail to for real delivery. Default `null` (capture-only). See [Transport decoration](#transport-decoration-capture--real-delivery).
- `path` — the URI prefix the dashboard lives at (default `mailbox`).
- `middleware` — routes run under the `web` group by default; add your own guards here (e.g. `auth`) for staging access control.
- `gate` — the Gate ability checked before dashboard access (default `viewMailbox`). See [Authorization](#authorization).
- `store.driver` — `sqlite` (default), `database`, or `file`. See [Storage](#storage).
- `store.database.connection` — the connection the DB driver uses. Defaults to an auto-created `mailbox` SQLite file isolated from your app's database.
- `retention` — seconds before `mailbox:clear --outdated` prunes a message (default 24 h).
- `retention_schedule` — when `true`, the package auto-registers a daily `mailbox:clear --outdated` on Laravel's scheduler. Disabled by default (`false`); enable it to let the package prune automatically, or wire the purge yourself.
- `per_page` — dashboard pagination size (default 20, clamped 1–100).
- `attachments.disk` — the filesystem disk attachment content is written to (default `mailbox` local disk).

### Environment variables

[](#environment-variables)

VariableDefaultDescription`MAILBOX_ENABLED``true` (non-production)Master on/off switch — routes and transport only register when true`MAILBOX_DECORATE``null`Mailer name to decorate — capture + forward for real delivery (e.g. `smtp`, `ses`)`MAILBOX_PATH``mailbox`URL prefix for the dashboard`MAILBOX_GATE``viewMailbox`Gate ability checked by the authorize middleware`MAILBOX_UNAUTHORIZED_REDIRECT``null`Redirect target on gate denial (null = 403 response)`MAILBOX_STORE_DRIVER``sqlite``sqlite`, `database`, or `file``MAILBOX_STORE_DATABASE_CONNECTION``mailbox`Connection name for the DB driver`MAILBOX_STORE_DATABASE_TABLE``mailbox_messages`Messages table name`MAILBOX_STORE_FILE_PATH``storage/app/mailbox`Path for the file driver`MAILBOX_RETENTION``86400`Retention period in seconds`MAILBOX_RETENTION_SCHEDULE``false`Auto-register a daily `mailbox:clear --outdated` on the scheduler`MAILBOX_PER_PAGE``20`Messages per dashboard page`MAILBOX_ATTACHMENTS_DISK``mailbox`Disk for attachment contentStorage
-------

[](#storage)

### SQLite driver (default)

[](#sqlite-driver-default)

Messages are stored in a dedicated SQLite database at `storage/app/mailbox/mailbox.sqlite`, fully isolated from your app's main database. This is the zero-config default — no setup required.

### Database driver (bring-your-own-connection)

[](#database-driver-bring-your-own-connection)

If you'd rather store captured mail in MySQL, Postgres, or another existing connection, switch the driver to `database` and define the connection in `config/database.php`:

```
MAILBOX_STORE_DRIVER=database
```

```
'connections' => [
    'mailbox' => [
        'driver' => 'mysql',
        'host' => env('MAILBOX_DB_HOST', '127.0.0.1'),
        'database' => env('MAILBOX_DB_DATABASE', 'mailbox'),
        'username' => env('MAILBOX_DB_USERNAME', 'root'),
        'password' => env('MAILBOX_DB_PASSWORD', ''),
    ],
],
```

Or point `MAILBOX_STORE_DATABASE_CONNECTION` at an existing connection such as `mysql`. Run `php artisan mailbox:install` again to create the `mailbox_messages` and `mailbox_attachments` tables there. Both `sqlite` and `database` use the same Eloquent-backed store internally — the difference is only in how the connection is configured.

### File driver

[](#file-driver)

Captures each message to a JSON file under `storage/app/mailbox/`. Use it when you can't write to a database at all. It's slower for listing and paginates in-memory.

```
MAILBOX_STORE_DRIVER=file
```

### Attachment disks

[](#attachment-disks)

Attachment content lives on the `mailbox` filesystem disk, independent of the message driver. By default the package registers a local disk at `storage/app/mailbox/`, but — like the database connection — it won't overwrite a disk of the same name you've already defined. To store attachments on S3:

```
// config/filesystems.php
'disks' => [
    'mailbox' => [
        'driver' => 's3',
        'bucket' => env('MAILBOX_S3_BUCKET'),
        'region' => env('MAILBOX_S3_REGION', 'us-east-1'),
    ],
],
```

### Custom drivers

[](#custom-drivers)

Implement [`Contracts\MessageStore`](src/Contracts/MessageStore.php) for messages, and [`Contracts\AttachmentStore`](src/Contracts/AttachmentStore.php) for their attachments. Register the pair in your service provider and point `mailbox.store.driver` at your resolver key. See [DRIVERS.md](DRIVERS.md) for the full driver author guide.

```
'store' => [
    'driver' => 'redis',
    'resolvers' => [
        'redis' => fn () => new \App\Storage\RedisMessageStore,
    ],
],
```

Drivers are always resolved as a pair — if you ship a custom `MessageStore`, also bind a matching `AttachmentStore` so attachment reads don't fall through to the database driver.

Authorization
-------------

[](#authorization)

Dashboard access is gated through Laravel's `Gate::allows()` using the `viewMailbox` ability. The package defines a default gate that allows access in local environments or whenever `mailbox.enabled` is true; if you define your own `viewMailbox` gate, the package will not overwrite it.

```
use Illuminate\Support\Facades\Gate;

public function boot(): void
{
    Gate::define('viewMailbox', fn ($user) => $user?->isAdmin());
}
```

For staging servers, this gives you authenticated-only access without any extra middleware. You can also point the config's `unauthorized_redirect` at a login URL if you'd rather redirect than serve a 403.

Captured messages can include passwords, tokens, and personal data, so leave `MAILBOX_ENABLED=false` in production unless you deliberately want the inbox running there. If you do, define a strict gate and make sure the dashboard sits behind authentication.

Artisan Commands
----------------

[](#artisan-commands)

```
# Publish assets + config, then run package migrations.
# Flags: --force (overwrite published files), --refresh (drop and rebuild tables),
#        --dev (symlink assets for hot reload).
php artisan mailbox:install

# Clear captured mail. With --outdated, only remove messages older than `retention`.
# Auto-pruning is off by default; set MAILBOX_RETENTION_SCHEDULE=true to have the
# --outdated variant run daily on Laravel's scheduler.
php artisan mailbox:clear
php artisan mailbox:clear --outdated

# Upgrade from v1.x to v2.0 — detects stale config, refreshes schema.
# Use --fresh to skip prompts.
php artisan mailbox:upgrade

# Recreate the dev-mode asset symlink (rarely needed directly; --dev on install uses it).
php artisan mailbox:dev-link
```

Upgrading
---------

[](#upgrading)

See [UPGRADE.md](UPGRADE.md) for the full v1.x → v2.0.0 migration guide. The quickest path:

```
composer update redberry/mailbox-for-laravel
php artisan mailbox:upgrade
```

Breaking changes between major versions are also documented in [CHANGELOG.md](CHANGELOG.md). Re-publish the config after any upgrade to pick up new keys:

```
php artisan vendor:publish --tag=mailbox-config --force
```

When a release changes the storage schema — v2.0.0 switched message ids from auto-increment integers to ULIDs — run `php artisan mailbox:install --refresh` to drop and recreate the mailbox tables.

Contributing
------------

[](#contributing)

See [CONTRIBUTING.md](CONTRIBUTING.md) for local setup, tests, and the coding standards we enforce.

Security Vulnerabilities
------------------------

[](#security-vulnerabilities)

If you discover a security vulnerability within this package, please email **** instead of using the issue tracker. All security vulnerabilities will be promptly addressed.

Credits
-------

[](#credits)

- [Nika Jorjoliani](https://github.com/nikajorjoliani) — Creator &amp; maintainer
- [Redberry](https://redberry.international)
- [All contributors](https://github.com/RedberryProducts/mailbox-for-laravel/graphs/contributors)

License
-------

[](#license)

The MIT License (MIT). See [LICENSE.md](LICENSE.md).

###  Health Score

57

—

FairBetter than 98% of packages

Maintenance96

Actively maintained with recent releases

Popularity39

Limited adoption so far

Community15

Small or concentrated contributor base

Maturity63

Established project with proven stability

 Bus Factor1

Top contributor holds 77% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~9 days

Total

30

Last Release

30d ago

Major Versions

v0.0.5 → v1.0.02025-09-19

v1.2.0 → v2.0.0-rc12026-04-16

### Community

Maintainers

![](https://www.gravatar.com/avatar/d2ceddc163a8c821d012238fe8f5439bd018bcd3036368c373eb397a19acfe7b?d=identicon)[Redberry LTD](/maintainers/Redberry%20LTD)

---

Top Contributors

[![nikajorjika](https://avatars.githubusercontent.com/u/4212052?v=4)](https://github.com/nikajorjika "nikajorjika (207 commits)")[![Copilot](https://avatars.githubusercontent.com/in/1143301?v=4)](https://github.com/Copilot "Copilot (48 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (10 commits)")[![Domianidze](https://avatars.githubusercontent.com/u/51382381?v=4)](https://github.com/Domianidze "Domianidze (2 commits)")[![github-actions[bot]](https://avatars.githubusercontent.com/in/15368?v=4)](https://github.com/github-actions[bot] "github-actions[bot] (1 commits)")[![nezaboravi](https://avatars.githubusercontent.com/u/1515172?v=4)](https://github.com/nezaboravi "nezaboravi (1 commits)")

---

Tags

laravelredberrymailbox-for-laravel

###  Code Quality

TestsPest

Static AnalysisPHPStan

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/redberry-mailbox-for-laravel/health.svg)

```
[![Health](https://phpackages.com/badges/redberry-mailbox-for-laravel/health.svg)](https://phpackages.com/packages/redberry-mailbox-for-laravel)
```

###  Alternatives

[spatie/laravel-pdf

Create PDFs in Laravel apps

1.0k4.8M47](/packages/spatie-laravel-pdf)[vormkracht10/laravel-mails

Laravel Mails can collect everything you might want to track about the mails that has been sent by your Laravel app.

24857.5k](/packages/vormkracht10-laravel-mails)[rawilk/profile-filament-plugin

Profile &amp; MFA starter kit for filament.

3914.6k](/packages/rawilk-profile-filament-plugin)[backstage/laravel-mails

Laravel Mails can collect everything you might want to track about the mails that has been sent by your Laravel app.

24884.4k13](/packages/backstage-laravel-mails)[harris21/laravel-fuse

Circuit breaker for Laravel queue jobs. Protect your workers from cascading failures.

44855.7k](/packages/harris21-laravel-fuse)[danestves/laravel-polar

A package to easily integrate your Laravel application with Polar.sh

8120.4k](/packages/danestves-laravel-polar)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
