amjadiqbal/laravel-notification-inbox
amjadiqbal/laravel-notification-inbox is a database-backed notification inbox for Laravel. It helps you store, retrieve, mark as read, and organize user notifications, making it easier to build in-app notification centers with a clean, simple API and read/unread status handling.
composer require <vendor>/laravel-notification-inbox
php artisan vendor:publish --tag="notification-inbox-config"
php artisan vendor:publish --tag="notification-inbox-migrations"
php artisan migrate
config/notification-inbox.php first. The three things you will touch day to day are the gate name (manageNotificationInbox), the redaction lists (redact, redact_containing), and the pruning knobs (prune_lottery).manageNotificationInbox ability before rendering delete/clear actions, and that ability is local-only by default. In a non-local environment nothing is deletable until you define it:
// AppServiceProvider::boot()
Gate::define('manageNotificationInbox', fn (User $user) => $user->isAdmin());
Viewing the inbox does not grant deletion; plan for two audiences (support reading, admins clearing).php artisan tinker, then open the inbox route. The notification is captured instead of delivered on non-mail channels, so you can iterate on formatting and copy without touching a real webhook.Use it as a local channel sandbox. Point your notification's via() at the channels you actually use in production and let the package catch them locally. You keep the real toSlack(), toVonage(), etc. code paths under test, and inspect the rendered payload in the inbox instead of a third-party dashboard. Google Chat was added to the channel table in v0.1.2, so if you thought a channel was unsupported, re-check the README table.
Assert against the inbox in tests rather than faking channels. Capture a notification by dispatching it in a feature test and asserting on what the inbox stored. This exercises your toArray()/payload builders and catches redaction regressions in one place.
Tune redaction in two layers. Keep redact for exact key names you know are sensitive, and put the broad families in redact_containing — v0.1.2 already masks keys containing password, passcode, secret, token, otp, code, private_key, and webhook. Add your domain-specific terms (e.g. api_key, session_id, card) to the substring list rather than enumerating every variant.
Turn on code masking only where it makes sense. mask_codes_in_text (NOTIFICATION_INBOX_MASK_CODES) is opt-in and masks 4–8 digit codes inside message text. Enable it in shared or screenshot-friendly environments; leave it off locally when the OTP is the thing you are debugging.
Schedule pruning explicitly. Pruning is opportunistic — roughly one capture in fifty triggers it — so it is not a substitute for a scheduled run. Add the command to your schedule (and to any environment where the inbox is not local-only):
$schedule->command('notification-inbox:prune')->daily();
Then set prune_lottery deliberately: 0 (or a very high denominator) on development machines where you want every capture retained, a low value in shared staging.
Place your own NotificationSending listeners in boot(). The package registers its interception listener in register(), which now runs ahead of listeners added in a provider's boot() and the app's own. Treat the package listener as the first link in the chain and put logging/telemetry listeners after it.
toX() code still runs. Do not use the inbox as a guarantee that nothing left the machine — an HTTP call you make inside toX() still fires.false still blocks delivery. v0.1.2 fixed ordering so an earlier listener can no longer silently pre-empt interception, but if your own NotificationSending listener returns false the notification stops before capture. Debug "missing from inbox" by logging listener order first, config second.code matches country_code, status_code, promo_code, error_code; token matches tokenized. Payloads that used to be readable for debugging will now show ***. If a field disappears, check redact_containing before assuming a bug — and remember config:clear after editing, since a cached config silently keeps old lists.mask_codes_in_text will leave it visible. Sanitise URLs in the notification itself if they are sensitive.manageNotificationInbox, but hidden UI is not authorization. If you build your own delete endpoint or call the package's action from a controller, verify the gate server-side. Local-only default means staging/production inboxes become read-only until you define the gate.prune_lottery is not set to 0.NOTIFICATION_INBOX_MASK_CODES and friends are documented, but every new env key is another thing to remember in CI; set defaults in the published config and override per environment.redact, redact_containing, mask_codes_in_text, prune_lottery), the notification-inbox:prune command, and the README's channel table — those four are where behaviour changed in v0.1.2 and where your day-to-day tuning will happen.How can I help you explore Laravel packages today?