CoreNotification, an abstract base notification class that gives every notification a consistent structure and multi-channel delivery out of the box: database storage, real-time broadcasting, email, and SMS.
Quick start
ExtendCoreNotification, implement the required getTitle() and getBody() methods (and configure channels in the constructor), and implement toMail() when using the mail channel.
Channels
CoreNotification implementsShouldBroadcast and ShouldQueue. The via() method automatically resolves channels based on the flags you set:
| Channel | Flag | Default | Description |
|---|---|---|---|
| Database | hiddenFromDatabase() | Enabled | Persists via CoreDatabaseChannel |
| Broadcast | useBroadcast() / withoutBroadcast() | Enabled | Real-time via Laravel Reverb |
useMail() / withoutMail() | Disabled | Brevo API when BREVO_KEY is set, otherwise Laravel mail | |
| SMS | useSms() / withoutSms() | Disabled | Brevo SMS or Twilio (automatic selection) |
| Inbox | useInbox() / withoutInbox() | null | Controls visibility in the notification list UI |
Channel selection flow
Abstract methods (required)
EveryCoreNotification subclass must implement:
getTitle(): string
Return the notification title. Used for database storage, broadcast payload, mail subject, and inbox display.
getBody(): ?string
Return the notification body. Used for database storage, broadcast payload, mail/SMS content, and inbox display.
When using the mail channel, also implement toMail().
toMail(object $notifiable): MailMessage
Returns the Laravel MailMessage for email delivery. Also used as the source for the Brevo email (rendered to HTML automatically).
Constructor configuration
Configure channels and metadata with fluentwith* methods in your constructor:
Fluent API reference
Content methods
| Method | Description |
|---|---|
getTitle(): string | Abstract. Return the notification title (required). |
getBody(): ?string | Abstract. Return the notification body (required). |
icon(?string $icon) | Set a Lucide icon name |
url(?string $url) | Set the click-through URL |
object(?ObjectContract $object) | Link to a domain object (auto-sets title, URL, and icon from the object) |
causer(?Model $causer) | Track the model that triggered this notification |
Channel toggle methods
| Method | Description |
|---|---|
useMail(bool $sendMail = true) | Enable/disable email delivery |
withoutMail() | Shortcut for useMail(false) |
useSms(bool $sendSms = true) | Enable/disable SMS delivery |
withoutSms() | Shortcut for useSms(false) |
useInbox(bool $useInbox = true) | Show/hide in the inbox UI |
withoutInbox() | Shortcut for useInbox(false) |
useBroadcast(bool $useBroadcast = true) | Enable/disable broadcasting |
withoutBroadcast() | Shortcut for useBroadcast(false) |
hiddenFromDatabase(bool $hidden = true) | Skip database storage entirely |
Exclusive channel methods
Use these to disable all other channels and enable only one:| Method | Description |
|---|---|
onlyMail() | Email only (no database, broadcast, inbox, or SMS) |
onlyInbox() | Inbox only (no email, broadcast, or SMS) |
onlyBroadcast() | Broadcast only (no email, inbox, or SMS) |
onlySms() | SMS only (no email, broadcast, or inbox) |
disableAllChannels() | Disable everything (call before selectively re-enabling) |
Domain objects
object() accepts any model implementing ObjectContract. It automatically populates:
- title from
getObjectTitle() - URL from
getObjectUrl()(if no URL is set) - icon from
getObjectIcon()(if no icon is set)
ObjectData and stored in the notification’s database payload, so the notification UI can link back to the related model.
Trackable URLs
UsegenerateTrackableUrl() in your toMail() to create signed URLs that mark the notification as read when clicked, then redirect to the destination:
url() or object()):
generateTrackableUrl() throws InvalidArgumentException. The generated URL is valid for 30 days and routes through notifications.track.
SimpleNotification
For ad-hoc notifications that don’t need a dedicated class, useSimpleNotification:
SimpleNotification extends CoreNotification and implements getTitle() / getBody() by storing values set via the fluent title() and body() setters. It also implements toMail() for you. Use SimpleNotification::make('Title') then optionally ->body('...'), mail subject, and SMS message via fluent setters.
Database storage
UnlesshiddenFromDatabase(true) is set, all notifications are persisted via CoreDatabaseChannel. The channel stores:
- Standard notification data (
title,body,icon,url,object) - Channel flags (
via_inbox,via_broadcast,via_mail,via_sms) - Causer information (
causer_type,causer_id) - Subject (domain object) information (
subject_type,subject_id) - Serialized constructor arguments (for replay/resend)
- Rendered mail HTML in a separate
DocumentBodyrecord
Broadcasting
Notifications broadcast on thenotification.received event type with this payload:
config('core.notifications.broadcast') and can be disabled per-notification with withoutBroadcast().
Real-world examples
Inbox + email notification with object
Email-only notification (auto-reply)
Verification code (no inbox, no broadcast)
Static helper methods
| Method | Returns | Description |
|---|---|---|
isBrevoEmailEnabled() | bool | true when BREVO_KEY is configured |
isBrevoSmsEnabled() | bool | true when BREVO_KEY and BREVO_SMS_SENDER are configured |
isTwilioSmsEnabled() | bool | true when Twilio is enabled and has an account SID |
isSmsEnabled() | bool | true when any SMS provider is available |
getSmsChannelClass() | ?string | Returns the active SMS channel class, or null |
Brevo integration
CoreNotification integrates with Brevo (formerly Sendinblue) for both email and SMS delivery. Brevo is used automatically when configured - no code changes required in your notification classes.Configuration
Add the following to your.env file:
config/brevo.php:
How email delivery works
WhenBREVO_KEY is set, useMail() routes email through BrevoEmailChannel instead of Laravel’s default mail channel. The default toBrevoEmail() implementation on CoreNotification:
- Calls your
toMail()method - Renders the
MailMessageto HTML - Builds a
BrevoEmailMessagewith the rendered content, subject, and sender
toMail() implementation works with Brevo with zero changes.
Customizing the Brevo email
OverridetoBrevoEmail() to use Brevo-specific features like templates, tags, or custom params:
BrevoEmailMessage methods
| Method | Description |
|---|---|
from($name, $email) | Set sender |
to($name, $email) | Add recipient (can be called multiple times) |
subject(string $subject) | Set email subject |
htmlContent(string $html) | Set HTML content |
textContent(string $text) | Set plain text content |
templateId(int $id) | Use a Brevo template |
params(array $params) | Template parameters |
cc($name, $email) | Add CC recipient |
bcc($name, $email) | Add BCC recipient |
replyTo($name, $email) | Set reply-to address |
attachment($name, $content) | Add attachment |
tags(array $tags) | Email tags |
headers(array $headers) | Custom headers |
How SMS delivery works
SMS channel is selected automatically:- Brevo SMS when both
BREVO_KEYandBREVO_SMS_SENDERare configured - Twilio as a fallback when Brevo SMS is not configured
toBrevoSms() and toTwilio() implementations use getBody() as SMS content. Override them for custom SMS text.
Troubleshooting
Emails not sending via Brevo:- Verify
BREVO_KEYis set in.env - Ensure
MAIL_FROM_ADDRESSis verified in your Brevo account - Check Laravel logs for
BrevoException
- Ensure both
BREVO_KEYandBREVO_SMS_SENDERare set (for Brevo) - Verify sender name is approved in Brevo
- Use international phone format (
+1234567890)