NewWe open-sourced 50+ Laravel packages
Custom AI apps, agents and automation — Roundly ConsultingRoundly
All packages
Notifications for Laravel

The Notifications facade

Everything goes through one facade, RoundlyConsulting\Notifications\Facades\Notifications, sugar over the NotificationsManager singleton. There is no global alias — it is one letter from Laravel’s own Notification facade — so always import it:

use RoundlyConsulting\Notifications\Facades\Notifications;   // no global alias — always import it

// Types — registered once, read for a settings screen
Notifications::define('comment.posted')->category('comments')->window('PT10M')->register();
Notifications::types()->all();

// Send — flat, fluent, or about one subject
Notifications::send('request.awaiting_approval', $request, $approvers, ['reference' => 'PR-9']);
Notifications::type('request.awaiting_approval')->about($request)->to($approvers)->once("request:{$request->id}")->send();
Notifications::for($contract)->send('contract.expiring', Recipients::owner());

// One recipient's inbox and preferences
Notifications::inbox($user)->in($company)->unread()->paginate();
Notifications::preferences($user)->mute('comment.posted');

// One subject's reminders, acknowledgement and revocation
Notifications::for($task)->remind('task.due', Reminder::before('deadline', 'PT1H'), to: $task->assignee);
Notifications::for($contract)->acknowledge('contract.expiring', by: $user);
Notifications::for($request)->revoke($formerMember);

// The time-driven work the scheduler runs
Notifications::sweep();
Notifications::prune();

Manager methods

MethodReturnsDoes
send($type, $subject, $recipients, $data = [], $actor = null, $context = null, $idempotencyKey = null)SendResultNotify recipients of one event — inbox, mail, preferences, de-dup, quiet hours, digests and access per recipient.
sendNotification(SendNotificationData $data)SendResultThe send as a DTO — what the builder and the raw action use.
type($type)PendingNotificationThe fluent send: about, to, by, with, in, once, escalate, send.
for(Model $subject)SubjectHandleEverything about one subject: sends, reminders, acknowledgement, revocation.
inbox(Model $recipient)InboxHandleOne recipient’s inbox — immutable filters, reads, counts, mutations.
preferences(Model $recipient)PreferencesHandleOne user’s channel, category, digest, quiet-hours and timezone preferences.
register(string ...$types) / registerType(NotificationType $type)voidRegister class types or a type instance.
define(string $key)TypeBuilderA type without a class.
types()TypesAccessorall(), categories(), find(), has(), get() — for a settings screen.
reminders()RemindersAccessorfireDue(), backfill($type), acknowledge($subject, $type, $by).
digests()DigestsAccessorsendDue(), preview($user).
deliveries()DeliveriesAccessordispatchDue().
interop()InteropAccessormap(), suppress(), mapped(), suppressed().
inboxMessage(Model $recipient, InboxMessage $message)?InboxItemWrite a Laravel inbox-channel message into a recipient’s inbox.
sweep(?array $only = null)SweepReportDue mail, digests, reminders, escalations, stale claims.
prune()PruneReportDelete old rows (see Commands).
Notifications::fake()NotificationsFakeFacade only — the record-only fake (see Testing).

perform() and applyInterop() are public on the manager but internal: perform() is the single funnel every handle, builder, channel and model trait runs its action through — the hook the fake records with — and applyInterop() is what the interop listener calls. Don’t call them from app code.

Notifications::for($subject)

MethodReturnsDoes
send($type, $recipients, $data = [], $actor = null, $context = null, $idempotencyKey = null)SendResultA send about this subject.
type($type)PendingNotificationThe fluent send, subject pre-filled.
remind($type, ReminderRule $rule, $to, array $data = [])list<NotificationReminder>An ad-hoc reminder.
reminders()SubjectRemindersschedule($type, $to), sync(?$type), cancel(?$type), pending(?$type), all(?$type).
acknowledge(?string $type = null, ?Model $by = null)intAcknowledge the subject’s open reminders and stop their escalations.
revoke(?Model $recipient = null)intRemove a recipient’s (or everyone’s) items about the subject and cancel their pending mail.
subject()ModelThe subject.

Notifications::inbox($recipient)

Handles are immutable — each filter returns a copy:

MethodReturnsDoes
unread() / read() / archived() / withArchived()InboxHandleStatus filters; the default is active (not archived).
in(Model|string $context) / withoutContext() / allContexts()InboxHandleOne context (a model or a key like company:3), only items without one, or every context (the default).
category($category) / type($type) / since(CarbonInterface)InboxHandleNarrow by category, type or recent activity.
latest()InboxHandleNewest activity first — the default order.
paginate(?int $perPage) / cursorPaginate(?int $perPage)LengthAwarePaginator / CursorPaginatorPages that eager-load actors and subjects; perPage clamped to inbox.max_per_page.
get(?int $limit) / first()Collection / ?InboxItemA capped list; the newest item.
find($item)InboxItemBy model or id, inside this inbox only — anything else throws InboxItemNotFoundException.
unreadCount() / counts()int / InboxCountsUnread count; unread, total and per-context counts.
markRead($item) / markUnread($item)InboxHandleRead state; markRead() also acknowledges an acknowledgedOnRead() reminder.
markAllRead(in: null, category: null)intOne UPDATE within every filter of the handle; an explicit argument replaces the filter of its kind.
archive($item) / unarchive($item) / delete($item)InboxHandleArchive, restore from the archive, soft-delete.
recipient()ModelThe inbox owner.

Notifications::preferences($user)

MethodReturnsDoes
mute($type, ?Channel $channel = null)PreferencesHandleMail only — or every mutable channel with inbox muting on, or one channel.
unmute($type, ?Channel $channel = null)PreferencesHandleClear the override: back to the category and type default.
set($type, Channel $channel, bool $enabled)PreferencesHandleAn explicit on or off.
muteCategory($category, ?Channel) / unmuteCategory($category, ?Channel)PreferencesHandleA whole category; an unused category throws.
digest(DigestFrequency $frequency, ?string $time = null)PreferencesHandleOff, daily or weekly, at an optional local time.
quietHours(?string $start, ?string $end = null)PreferencesHandleLocal HH:MM; null clears them.
timezone(?string $timezone)PreferencesHandleAn IANA id; null clears it.
update(PreferencesUpdate $update)PreferencesHandleSeveral changes, all or nothing.
reset()PreferencesHandleEvery type and category override forgotten (digest, quiet hours and timezone kept).
matrix() / overview()list<PreferenceRow> / PreferencesOverviewThe settings-screen data, translated.
enabled($type, Channel) / muted($type)boolThe effective state of a channel; whether mail is off.
digestPreference() / quietHoursPreference() / resolvedTimezone()DigestPreferenceData / ?QuietHours / stringThe effective digest, quiet hours and timezone.
user()ModelThe user.

Builders, DTOs and enums

  • Builders: PendingNotification (about, to, by, with, in, once, escalate, data, send), TypeBuilder (see Notification types), the reminder rules Reminder, ReminderRule and EscalationRule, and Context (of, key).
  • DTOs (final readonly): SendNotificationData, SendResult, RecipientOutcome, TypeDefinition, CategoryDefinition, PreferenceRow, ChannelPreference, PreferencesOverview, PreferencesUpdate, DigestPreferenceData, QuietHours, InboxCounts, ContextCount, ContextReference, ReminderReference, SweepReport, PruneReport, FiredReminders.
  • Enums: Channel (inbox, mail), Priority (low, normal, urgent), DigestFrequency (off, daily, weekly), MailStrategy (leading, trailing), DeliveryStatus, DigestRunStatus, ReminderStatus (pending, firing, sent, cancelled, skipped), ReminderKind, InboxOutcome, MailOutcome, CancelReason and NotificationOperation. The user-facing ones carry translated labels.

Show your open-source love

This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.

More ways to support, including crypto

By donating, you agree to our donation terms.

Want this built into your product?

We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.