laravel-notification-channels/facebook
Laravel Notification Channels Facebook adds a Facebook channel to Laravel’s native notification system, letting you deliver notifications through Facebook from your app using the familiar Laravel notification API with simple setup and integration.
Install the channel with Composer. Laravel’s package auto-discovery registers FacebookServiceProvider, so no manual config/app.php edit is needed on modern Laravel:
composer require laravel-notification-channels/facebook
Version 0.3.0 is the first release to declare Laravel 9 support. It is still not tested against Laravel 10/11 or PHP 8.2+, so check composer.json constraints before adding it to a current app (see Gotchas).
Add the credentials used by the Graph API to config/services.php and .env:
// config/services.php
'facebook' => [
'client_id' => env('FACEBOOK_CLIENT_ID'),
'client_secret' => env('FACEBOOK_CLIENT_SECRET'),
'access_token' => env('FACEBOOK_ACCESS_TOKEN'),
],
FACEBOOK_CLIENT_ID=
FACEBOOK_CLIENT_SECRET=
FACEBOOK_ACCESS_TOKEN=
The thing to look at first is src/FacebookChannel.php and src/FacebookMessage.php. Those two classes define the whole surface area of the package: the channel handles the route resolution and the HTTP call to Graph, and the message class builds the recipient / template / href payload plus the optional buttons and image elements. There is very little else in the package, so reading both takes five minutes — and it is the only reliable way to know exactly which payload keys 0.3.0 emits, since the README lags behind the button/image additions.
Tell the notifiable how to resolve a Facebook user id:
// app/Models/User.php
public function routeNotificationForFacebook()
{
return $this->facebook_id;
}
Then create a notification whose via() returns the channel and whose toFacebook() returns a FacebookMessage:
use Illuminate\Notifications\Notification;
use NotificationChannels\Facebook\FacebookChannel;
use NotificationChannels\Facebook\FacebookMessage;
class InvoicePaid extends Notification
{
public function __construct(public Invoice $invoice) {}
public function via($notifiable)
{
return [FacebookChannel::class];
}
public function toFacebook($notifiable)
{
return FacebookMessage::create()
->to($notifiable->facebook_id)
->template("Invoice #{$this->invoice->number} has been paid")
->href(url("/invoices/{$this->invoice->id}"));
}
}
A good first use case is exactly this shape: a short plain-text template plus a deep link back into your app (an invoice, a comment reply, an approval request). Facebook notifications are best treated as a lightweight "come back to the app" nudge, not as a rich messaging surface.
Always queue the notification. The channel makes a synchronous Graph API call inside send(), so implement ShouldQueue on the notification or call notify() through a queued job. A slow or failing Facebook endpoint should never block a web request.
class InvoicePaid extends Notification implements ShouldQueue
{
public $tries = 3;
public $backoff = [10, 60];
}
Use on-demand notifications for one-offs. When you don't have a notifiable model with a stored id (e.g. an admin alert triggered from a command), use an anonymous notifiable:
Notification::route('facebook', $facebookUserId)
->notify(new SystemAlert($message));
The channel falls back to routeNotificationFor('facebook'), so the route key must be facebook and must match what routeNotificationForFacebook() returns for models — keep those two names aligned or you'll get "no recipient" exceptions only in one code path.
Compose the channel conditionally. Don't hard-code FacebookChannel::class in via() if only some users have linked their account. Filter the channel list:
public function via($notifiable)
{
return array_filter([
FacebookChannel::class,
$notifiable->facebook_id ? null : MailChannel::class,
DatabaseChannel::class,
]);
}
This lets you add Facebook as an extra delivery path alongside mail/database without duplicating notification classes, and gives you a natural fallback when a user hasn't connected Facebook.
Buttons are simpler as of 0.3.0. The button element now accepts a plain string payload, so a single call-to-action no longer needs the verbose array form:
return FacebookMessage::create()
->to($notifiable->facebook_id)
->template(Str::limit($text, 120))
->href(URL::signedRoute('invoices.show', $this->invoice))
->button('View invoice', url("/invoices/{$this->invoice->id}"));
Keep using the array form when you need more than a title plus one target — but check FacebookMessage::button() in the source first, because the exact key names are not documented in the release notes and differ from the Messenger button schema.
Images: set the aspect ratio explicitly. 0.3.0 adds a helper for the image aspect ratio. Facebook validates that field and rejects images without a supported value, so always pair the image call with the helper rather than relying on a default:
->image(url('/img/receipt.png'))
->imageAspectRatio('horizontal') // or 'vertical'
The image must be a publicly reachable absolute URL — a signed, auth-gated route will be fetched by Facebook's servers and fail.
Centralise message construction. Because the payload is small, wrap the template formatting in a helper or base notification so truncation, signing and aspect-ratio defaults happen in one place:
protected function facebookMessage(string $text, string $href): FacebookMessage
{
return FacebookMessage::create()
->template(Str::limit($text, 120))
->href(URL::signedRoute('invoices.show', $this->invoice));
}
Test without hitting Graph. Notification::fake() asserts that the notification was sent on the channel, but it does not exercise the message payload. For the payload, test the channel directly with a fake notifiable and Http::fake():
Http::fake(['graph.facebook.com/*' => Http::response(['success' => true])]);
(new FacebookChannel)->send($user, new InvoicePaid($invoice));
Http::assertSent(fn ($request) =>
str_contains($request->url(), "/{$user->facebook_id}/notifications")
&& $request['template'] === 'Invoice #123 has been paid'
);
This is also the fastest way to discover the exact request shape the package sends — including how the new button and image-aspect-ratio fields are serialised — since the code is thin and the tests in the repo do the same thing.
The biggest one: Facebook's notification API is heavily restricted. The POST /{user-id}/notifications edge is only available to apps that live on Facebook.com (Canvas/games-style apps). Mobile-only and web-only apps will get permission errors ((#200)) or an outright "notifications are not supported" response even with a perfectly valid token. Validate this against your app type before designing a feature around the package — it is the single reason this channel sees little real-world use. Buttons and images do not change this; they only affect payload shape, not eligibility.
Tokens and permissions are the second most common failure. The access token must belong to a user who has authorised the app with the relevant permission, and it expires. There is no refresh flow in the package; you have to store and rotate tokens yourself. Graph errors come back as JSON in the response body but the channel surfaces them as a CouldNotSendNotification exception, so log the exception message (and the response body) rather than the stack trace alone.
The package is still effectively stale, but less so. 0.3.0 ends a long gap (previous release was 0.2.1, February 2022) and adds Laravel 9 support — welcome, but it still predates Laravel 10/11 and PHP 8.2+. Before adopting, check the constraint, and be prepared to fork or pin the Graph API version, since the default version baked into the endpoint may be retired regardless of the Laravel support matrix. If you need this long-term, budget for vendoring the two source files rather than relying on upstream. Note that the community interest here is thin: two small PRs and one new contributor.
href must point somewhere Facebook accepts. It has to be a URL on your app's domain or canvas URL, not an arbitrary external link, and it should be absolute. Deep links into a mobile app won't work through this channel — you'd need the Messenger Send API for that, which this package does not implement.
Templates are plain text; the new richness is cosmetic. The template string itself still has no markdown and is subject to length limits — truncate defensively on your side. Buttons added in 0.3.0 are a title plus a target, not arbitrary interactive cards, and the image element is a single image with a validated aspect ratio (horizontal / vertical). If you were previously told this package could not do buttons or images at all, that has changed; if you need Messenger templates, quick replies, or carousels, it still isn't the right base.
Watch for double-sending. Because there's no idempotency key in the payload, a retried queued notification can result in duplicate notifications. If duplicate nudges are a problem, gate the send with a ShouldSend-style check or a small cache key keyed on the notifiable and notification type.
Extension points are minimal but workable. The message class is plain and extendable — subclass FacebookMessage if you need a custom payload field, and the channel will send whatever array it exposes. Button-payload handling in 0.3.0 is a good example of a small change that flows straight through. If you need Messenger, per-message API versions, or richer templates, write a small custom channel around Http::withToken(...)->post("https://graph.facebook.com/v19.0/me/messages", ...) instead.
How can I help you explore Laravel packages today?