usamamuneerchaudhary/filament-notifier

A powerful notification system for FilamentPHP that handles multi-channel notifications with template management, scheduling, and real-time delivery. Built for developers who need enterprise-grade notifications without the complexity.
3,588 36
Install
composer require usamamuneerchaudhary/filament-notifier
Latest Version:v2.0
PHP:^8.3
Laravel:11 · 12 · 13
Older releases:4 – 10
License:MIT
Last Updated:Oct 11, 2026
Links: GitHub  ·  Packagist
Maintainer: usamamuneerchaudhary

Filament Notifier

Latest Version on Packagist Scrutinizer Code Quality CodeFactor Build Status Code Intelligence Status Total Downloads Licence

A powerful notification system for FilamentPHP that handles multi-channel notifications with template management, scheduling, and real-time delivery. Built for developers who need enterprise-grade notifications without the complexity.

See UPGRADE.md when moving from 1.x, and CHANGELOG.md for what shipped in 2.0.

Support matrix

Package PHP Laravel Filament
2.x 8.3–8.5 11, 12, 13 4, 5
1.5 8.2+ 11, 12, 13 4, 5
1.4 8.2+ 11, 12 4, 5

Features

  • Multi-channel: Email, in-app (Filament bell), SMS (Twilio), Slack, Discord, and Push (FCM HTTP v1)
  • Template management: {{variable}} substitution (HTML-escaped in emails; {{{variable}}} for trusted HTML)
  • Scheduled notifications: Queue a send for a specific time (dispatched once)
  • Event-driven: Trigger notifications from application events stored in the admin panel
  • User preferences: Filament page, <livewire:notifier-preferences />, and an authenticated REST API
  • Unsubscribe: Signed one-click links and List-Unsubscribe headers
  • Retries and webhooks: Backoff, fallback channels, and inbound Mailgun / Postmark / SES / Twilio delivery status
  • Analytics dashboard: Opens, clicks, channel performance. Treat click rate as the primary metric — Apple Mail Privacy Protection inflates opens
  • Signed click tracking: Tracking redirects are signed; unsigned links only follow the app host or allowed_hosts
  • Rate limiting: Global, optional per-user / per-channel, with drop or defer when a limit is hit
  • Notifier::fake(): Record sends in host-app tests
  • CI: GitHub Actions generated by laravel-slipway

Installation

1. Install the Package

composer require usamamuneerchaudhary/filament-notifier

2. Run the Installation Command

php artisan notifier:install

This command will:

  • Publish the configuration file
  • Run the necessary migrations
  • Create sample notification channels and templates
  • Set up the basic structure

3. Register the Plugin

Add the plugin to your Filament panel configuration:

use Usamamuneerchaudhary\Notifier\FilamentNotifierPlugin;

public function panel(Panel $panel): Panel
{
    return $panel
        ->plugins([
            FilamentNotifierPlugin::make(),
        ]);
}

After registration, you'll have access to:

  • Notifier Dashboard - Comprehensive analytics and metrics
  • Notification Settings - Configure preferences, analytics, and rate limiting
  • Notification Preferences - Let panel users manage their own channels (hide with ->preferencesPage(false))
  • Notification Resources - Manage channels, events, templates, and notifications

In-app (Filament bell) channel

The in_app driver writes to Laravel's notifications table via Filament\Notifications\Notification::sendToDatabase(). Before using it:

php artisan make:notifications-table
php artisan migrate

Enable database notifications on the panel:

return $panel
    ->databaseNotifications()
    ->plugins([
        FilamentNotifierPlugin::make(),
    ]);

Pass optional template data: action_url, action_label, status (info|success|warning|danger), and icon. Set broadcast on the channel to also push through Echo.

4. Configure Channels

Update your .env file with your notification channel settings:

# Email Configuration
MAIL_MAILER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=your_username
MAIL_PASSWORD=your_password
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=noreply@example.com
MAIL_FROM_NAME="Your App"

# Notifier Package Settings
NOTIFIER_EMAIL_ENABLED=true
NOTIFIER_SLACK_ENABLED=false
NOTIFIER_SMS_ENABLED=false

# Slack Configuration
SLACK_WEBHOOK_URL=https://hooks.slack.com/services/YOUR/WEBHOOK/URL
SLACK_CHANNEL=#notifications

# SMS Configuration (Twilio)
TWILIO_ACCOUNT_SID=your_account_sid
TWILIO_AUTH_TOKEN=your_auth_token
TWILIO_PHONE_NUMBER=+1234567890

Usage

Basic Notification Sending

use Usamamuneerchaudhary\Notifier\Facades\Notifier;

// Send a notification using the facade
Notifier::send($user, 'user.registered', [
    'name' => $user->name,
    'email' => $user->email,
]);

// Or using the service directly
$notifier = app('notifier');
$notifier->send($user, 'user.registered', [
    'name' => $user->name,
    'email' => $user->email,
]);

Using Service Facades

The package provides convenient facades for accessing services:

use Usamamuneerchaudhary\Notifier\Facades\Preference;
use Usamamuneerchaudhary\Notifier\Facades\Analytics;
use Usamamuneerchaudhary\Notifier\Facades\UrlTracking;
use Usamamuneerchaudhary\Notifier\Facades\NotificationRepo;

// Get user preferences
$preferences = Preference::getUserPreferences($user, 'user.registered');

// Check analytics status
if (Analytics::isEnabled()) {
    Analytics::trackOpen($notification);
}

// Safely redirect URLs
return UrlTracking::safeRedirect('https://example.com');

// Find notification by token
$notification = NotificationRepo::findByToken($token);

Note: After installing the package, run composer dump-autoload to register the facades.

Dependency Injection (Recommended)

For better testability, use dependency injection in your controllers and services:

use Usamamuneerchaudhary\Notifier\Services\PreferenceService;
use Usamamuneerchaudhary\Notifier\Services\AnalyticsService;

class MyController extends Controller
{
    public function __construct(
        protected PreferenceService $preferenceService,
        protected AnalyticsService $analyticsService
    ) {}

    public function index()
    {
        $preferences = $this->preferenceService->getUserPreferences($user, 'event.key');
        
        if ($this->analyticsService->isEnabled()) {
            // Do something
        }
    }
}

Scheduled Notifications

use Carbon\Carbon;

// Schedule a notification for later
$notifier->schedule($user, 'reminder.email', Carbon::now()->addDays(7), [
    'task_name' => 'Complete project review',
]);

Event-Based Notifications

Create events, templates and their default channels in the Filament admin (Notifier → Events). You can also register an event in code:

app('notifier')->registerEvent('user.registered', [
    'channels' => ['email', 'in_app'],
    'template' => 'welcome-email',
    'fallbacks' => ['email' => 'sms'],
]);

send(), sendToChannel() and schedule() all honour the recipient's preferences.

Testing with Notifier::fake()

use Usamamuneerchaudhary\Notifier\Facades\Notifier;

$fake = Notifier::fake();

// code that calls Notifier::send($user, 'user.registered', [...])

$fake->assertSent('user.registered');
$fake->assertSentTo($user, 'user.registered');
$fake->assertSentTimes('user.registered', 1);
$fake->assertNotSent('order.shipped');
$fake->assertSentCount(1);

Unsubscribe

Emails include a signed {{unsubscribe_url}} and List-Unsubscribe / List-Unsubscribe-Post headers (RFC 8058). GET /notifier/unsubscribe/{user} shows a confirmation page; POST performs the unsubscribe. Embed the same preference grid anywhere with:

<livewire:notifier-preferences />

Delivery webhooks

POST /notifier/webhooks/{mailgun|postmark|ses|twilio} updates delivered, bounced and complained. Each provider verifies its own signature. Configure keys in config/notifier.php under webhooks. Failed notifications can be retried from the notifications table (row action or Retry failed bulk action). Configure per-event fallbacks as fallbacks => ['email' => 'sms'].

Continuous integration

This package's GitHub Actions workflow is generated by laravel-slipway. Edit slipway.php, then:

composer slipway:compile

Commit both slipway.php and .github/workflows/slipway.yml. Do not edit the YAML by hand; the slipway-guard job fails if they drift.

Creating Templates

Templates can be created through the Filament admin panel or programmatically:

use Usamamuneerchaudhary\Notifier\Models\NotificationTemplate;

NotificationTemplate::create([
    'title' => 'Welcome Email',
    'key' => 'welcome-email',
    'type' => 'email',
    'subject' => 'Welcome to {{app_name}}, {{name}}!',
    'content' => 'Hi {{name}},\n\nWelcome to {{app_name}}! We\'re excited to have you on board.',
    'variables' => [
        'name' => 'User\'s full name',
        'app_name' => 'Application name',
    ],
]);

Custom Channel Drivers

Create custom channel drivers by implementing the ChannelDriverInterface:

use Usamamuneerchaudhary\Notifier\Services\ChannelDrivers\ChannelDriverInterface;
use Usamamuneerchaudhary\Notifier\Models\Notification;

class CustomChannelDriver implements ChannelDriverInterface
{
    public function send(Notification $notification): bool
    {
        // Your custom sending logic here
        return true;
    }

    public function validateSettings(array $settings): bool
    {
        // Validate your channel settings
        return !empty($settings['api_key'] ?? null);
    }
}

User Preferences API

The package provides a simple REST API for users to manage their notification preferences. All endpoints require authentication and respect the allow_override setting configured in the admin panel.

Get All User Preferences
GET /api/notifier/preferences
Authorization: Bearer {token}

Response:

{
  "data": [
    {
      "event_key": "user.registered",
      "event_name": "User Registered",
      "event_group": "User",
      "description": "Sent when a new user registers",
      "channels": {
        "email": true,
        "sms": false,
        "push": true
      }
    }
  ]
}
Get Available Events and Channels
GET /api/notifier/preferences/available
Authorization: Bearer {token}

Response:

{
  "data": {
    "events": [
      {
        "key": "user.registered",
        "name": "User Registered",
        "group": "User",
        "description": "Sent when a new user registers",
        "default_channels": ["email"]
      }
    ],
    "channels": [
      {
        "type": "email",
        "title": "Email",
        "icon": "heroicon-o-envelope"
      },
      {
        "type": "sms",
        "title": "SMS",
        "icon": "heroicon-o-device-phone-mobile"
      }
    ]
  }
}
Get Preference for Specific Event
GET /api/notifier/preferences/{eventKey}
Authorization: Bearer {token}

Example:

GET /api/notifier/preferences/user.registered

Response:

{
  "data": {
    "event_key": "user.registered",
    "event_name": "User Registered",
    "event_group": "User",
    "description": "Sent when a new user registers",
    "channels": {
      "email": true,
      "sms": false
    }
  }
}
Update Preference for Event
PUT /api/notifier/preferences/{eventKey}
Authorization: Bearer {token}
Content-Type: application/json

Request Body:

{
  "channels": {
    "email": true,
    "sms": true,
    "push": false
  },
  "settings": {}
}

Response:

{
  "data": {
    "event_key": "user.registered",
    "event_name": "User Registered",
    "channels": {
      "email": true,
      "sms": true,
      "push": false
    }
  },
  "message": "Preferences updated successfully."
}

Error Responses:

  • 403 Forbidden - User preference override is disabled by administrator
  • 422 Unprocessable Entity - Invalid channel type or validation error
  • 404 Not Found - Event not found or inactive

Note: The API will return a 403 error if the admin has disabled allow_override in the notification settings.

Analytics & Tracking

The package includes comprehensive analytics tracking for email notifications, allowing you to measure engagement and optimize your notification strategy.

Email Open Tracking

When track_opens is enabled in analytics settings, a 1x1 transparent tracking pixel is automatically injected into all email notifications. When a user opens the email, the pixel loads and tracks the open event.

Tracking Endpoint:

GET /notifier/track/open/{token}

This endpoint:

  • Returns a 1x1 transparent PNG pixel
  • Records the first open time in opened_at
  • Increments the opens_count counter
  • Respects analytics enabled/disabled settings

Link Click Tracking

When track_clicks is enabled, all URLs in email content are rewritten to signed tracking URLs. When users click links, the system tracks the click and redirects to the original URL.

Apple Mail Privacy Protection prefetches tracking pixels, so open rate is inflated. Use click-through rate as the main engagement metric. Unsigned (legacy) tracking links only redirect to the application host, hosts in notifier.tracking.allowed_hosts, or a URL that already appeared in that notification.

Tracking Endpoint:

GET /notifier/track/click/{token}?url={encoded_url}

This endpoint:

  • Records the first click time in clicked_at
  • Increments the clicks_count counter
  • Safely redirects to the original URL
  • Validates URLs to prevent open redirect vulnerabilities

Analytics Dashboard

Access the comprehensive analytics dashboard in your Filament admin panel:

Location: Navigate to Notifier → Dashboard in your Filament admin panel.

Dashboard Features:

  1. Overview Stats Widget

    • Total notifications with trend chart
    • Success rate percentage
    • Pending notifications count
    • Failed notifications count
    • Active channels count
  2. Engagement Stats Widget

    • Total opens
    • Open rate percentage
    • Total clicks
    • Click rate percentage
    • Click-through rate
  3. Time Series Chart

    • 30-day line chart showing sent, opened, and clicked notifications
    • Full-width visualization
    • Auto-refreshes every 30 seconds
  4. Engagement Analytics Chart

    • Combined bar and line chart
    • Shows opens and clicks
    • Overlays open rate % and click rate %
    • Dual Y-axis for counts and percentages
    • Last 7 days view
  5. Channel Performance Chart

    • Bar chart comparing all active channels
    • Shows sent, opened, and clicked per channel
    • Helps identify best-performing channels
  6. Rate Limiting Status Widget

    • Real-time usage for minute, hour, and day limits
    • Color-coded warnings
    • Percentage usage indicators
    • Auto-refreshes every 10 seconds

All widgets respect the analytics and rate limiting enabled/disabled settings.

Data Retention

Configure data retention in the analytics settings. Use the cleanup command to remove old analytics data:

# Clean up analytics data older than retention period
php artisan notifier:cleanup-analytics

# Dry run to see what would be cleaned
php artisan notifier:cleanup-analytics --dry-run

The cleanup command:

  • Respects the retention_days setting
  • Anonymizes analytics data
  • Can be scheduled via Laravel's task scheduler

Rate Limiting

The package includes built-in rate limiting to prevent notification abuse and ensure system stability.

Configuration

Configure rate limits in the Filament admin panel under Notifier → Settings → Rate Limiting:

  • Max Per Minute / Hour / Day: Global caps (defaults: 60 / 1000 / 10000)
  • When a limit is reached: drop (default) skips the notification; defer queues it until the window resets
  • Max Per User Per Hour / Day and Max Per Channel Per Minute: optional extra caps (0 disables them)

How It Works

Rate limiting is enforced atomically before notification creation:

  • Global limits, then optional per-user and per-channel limits, are reserved together
  • drop skips the notification; defer still creates it and delays the job
  • Rate limit violations are logged
  • Counters live in Laravel Cache with a TTL that outlasts the window

Monitoring

View real-time rate limiting status in the Notifier Dashboard:

  • Current usage vs limits for each time period
  • Color-coded warnings (red >90%, yellow >75%, green otherwise)
  • Percentage usage indicators

Configuration

Channels, events and templates are stored in the database and edited in Filament. config/notifier.php holds package defaults (queue, retries, tracking hosts, API middleware, webhook secrets, and fallback settings). Publish it with php artisan vendor:publish --tag=notifier-config.

Database Tables

The package creates the following database tables with the notifier_ prefix to avoid conflicts with Laravel's built-in tables:

  • notifier_channels - Stores notification channel configurations
  • notifier_events - Stores notification event definitions
  • notifier_templates - Stores notification templates
  • notifier_preferences - Stores user notification preferences
  • notifier_notifications - Stores sent notifications
  • notifier_settings - Stores global notification settings

Note: These tables are separate from Laravel's built-in notifications table, which is used for database notifications. Our package provides a comprehensive notification management system that works alongside Laravel's native notification system.

Publishing Migrations

To customize migrations (e.g., for UUID support, different indexes, or schema changes):

php artisan vendor:publish --tag=notifier-migrations

After publishing, edit the migration files in database/migrations/ and run:

php artisan migrate

API Reference

NotifierManager

Methods
  • send($user, string $eventKey, array $data = []): Send a notification
  • sendNow($user, string $eventKey, array $data = []): Send immediately without queuing
  • sendToChannel($user, string $eventKey, string $channelType, array $data = []): Send to a specific channel
  • schedule($user, string $eventKey, Carbon $scheduledAt, array $data = []): Schedule a notification
  • registerChannel(string $type, $handler): Register a custom channel driver
  • registerEvent(string $key, array $config): Register an event configuration
  • getRegisteredChannels(): Get all registered channel types
  • getRegisteredEvents(): Get all registered event keys

Available Facades

Preference Facade
use Usamamuneerchaudhary\Notifier\Facades\Preference;

Preference::getUserPreferences($user, string $eventKey): array
Preference::getChannelsForEvent(NotificationEvent $event, ?NotificationPreference $preference): array
Preference::shouldSendToChannel($user, string $channelType, array $preferences): bool
Analytics Facade
use Usamamuneerchaudhary\Notifier\Facades\Analytics;

Analytics::isEnabled(): bool
Analytics::isOpenTrackingEnabled(): bool
Analytics::isClickTrackingEnabled(): bool
Analytics::generateTrackingPixel(string $trackingToken): string
Analytics::trackOpen(Notification $notification): void
Analytics::trackClick(Notification $notification): void
UrlTracking Facade
use Usamamuneerchaudhary\Notifier\Facades\UrlTracking;

UrlTracking::safeRedirect(string $url): RedirectResponse
UrlTracking::rewriteUrlsForTracking(string $content, string $token): string
NotificationRepo Facade
use Usamamuneerchaudhary\Notifier\Facades\NotificationRepo;

NotificationRepo::findByToken(string $token): ?Notification

Models

NotificationChannel
  • title: Channel display name
  • type: Channel type (email, sms, slack, etc.)
  • icon: Icon for the channel
  • is_active: Whether the channel is active
  • settings: Channel-specific settings
NotificationTemplate
  • title: Template display name
  • key: Unique template identifier
  • type: Template type
  • subject: Email subject line
  • content: Template content with variable placeholders
  • variables: Available variables for the template
Notification
  • notification_template_id: Associated template
  • user_id: Target user
  • channel: Channel type
  • subject: Rendered subject
  • content: Rendered content
  • status: Notification status (pending, sending, sent, delivered, bounced, complained, failed)
  • scheduled_at: Scheduled send time
  • sent_at: Actual send time
  • opened_at: First time notification was opened (analytics)
  • clicked_at: First time a link was clicked (analytics)
  • opens_count: Total number of opens (analytics)
  • clicks_count: Total number of clicks (analytics)

Testing

Running Tests

composer test

The package includes comprehensive tests covering:

  • Notification sending, scheduling, retries and fallbacks
  • User preferences API (authenticated) and unsubscribe
  • Analytics tracking (opens, signed clicks)
  • Rate limiting (drop and defer)
  • Email, Discord, Push (FCM v1) and in-app drivers
  • Delivery webhooks
  • Notifier::fake()
  • Analytics cleanup command

Roadmap (not in 2.0)

Polymorphic / on-demand recipients, localised templates, live preview and test send, template versioning and A/B tests, Blade or Markdown syntax, digests and quiet hours, a Laravel Notification bridge, multi-tenancy, per-link click events, and CSV export. See UPGRADE.md for 1.5 → 2.0 notes.

Test Coverage

Feature Tests:

  • NotifierManagerTest - Core notification functionality
  • AnalyticsTrackingTest - Email open and click tracking
  • RateLimitingTest - Rate limit enforcement
  • AnalyticsCleanupCommandTest - Data retention cleanup

Unit Tests:

  • RateLimitingServiceTest - Rate limiting service logic
  • EmailDriverTest - Email sending and tracking pixel injection

Sending Test Notifications

You can send test notifications using the provided command:

# Send a test notification
php artisan notifier:test user.registered --user=1

# Send with custom data
php artisan notifier:test user.registered --user=1 --data="name=John Doe" --data="app_name=Test App"

# Send to specific channel
php artisan notifier:test user.registered --user=1 --channel=email

Available Commands

  • php artisan notifier:install - Install the package and create sample data
  • php artisan notifier:test {event} [options] - Send test notifications
  • php artisan notifier:cleanup-analytics [--dry-run] - Clean up old analytics data based on retention settings

Cleanup Analytics Command

The cleanup command removes or anonymizes analytics data older than the configured retention period:

# Clean up old analytics data
php artisan notifier:cleanup-analytics

# Preview what would be cleaned (dry run)
php artisan notifier:cleanup-analytics --dry-run

Options:

  • --dry-run: Show what would be cleaned without actually deleting data

What it does:

  • Finds notifications with analytics data older than retention_days
  • Anonymizes the data (sets opens/clicks to 0, clears timestamps)
  • Preserves notification records for historical reference
  • Respects the analytics.enabled setting

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Add tests for new functionality
  5. Submit a pull request

License

This package is open-sourced software licensed under the MIT license.

Architecture & Design

The package follows Laravel best practices with a clean, service-oriented architecture:

  • Service Classes: Regular classes that can be dependency injected or accessed via facades
  • Facades: Convenient static access to services (registered in composer.json)
  • Dependency Injection: Preferred approach for controllers and testable code
  • Service Container: All services are registered as singletons for optimal performance

Screenshots

Dashboard Overview

Analytics & Metrics

Notification Management

Templates & Settings

Support

For support, please open an issue on GitHub or contact me at hello@usamamuneer.me.

Related Packages

usamamuneerchaudhary/filament-command-palette

A Spotlight/CMD+K style command palette for Filament: fuzzy search across naviga...

5,983 16
tomatophp/filament-alerts

Send notification to users using notification templates and multi notification c...

12,788 82

Version History

Version Released PHP Laravel License
v2.0 ^8.3 ^11.0|^12.0|^13.0 MIT
v1.5 ^8.2 ^11.0|^12.0|^13.0 MIT
1.4 ^8.2 ^11.0|^12.0 MIT
v1.2 ^8.2 * MIT
v1.1 ^8.2 * MIT