laravel-notification-channels/sms-broadcast
Send SMS notifications from Laravel through the SMS Broadcast API using a dedicated notification channel. Install the package, configure your SMS Broadcast credentials, and start delivering messages via Laravel's familiar notification system.
Install and wire up credentials (5 minutes)
composer require laravel-notification-channels/sms-broadcast
php artisan vendor:publish --tag=sms-broadcast-config # check the tag in the README
Add the provider credentials to .env (names below follow the convention of the laravel-notification-channels/* family — confirm against the package's config/sms-broadcast.php):
SMSBROADCAST_USERNAME=
SMSBROADCAST_PASSWORD=
SMSBROADCAST_FROM=MyApp
Laravel's package discovery registers the service provider; there is nothing to add to config/app.php on modern Laravel versions.
Where to look first
vendor/laravel-notification-channels/sms-broadcast/src/ — SmsBroadcastChannel.php and SmsBroadcastMessage.php are the whole package. Read both before you build on top of them; at this size the source is the documentation.First use case: transactional SMS to Australian mobiles
// app/Notifications/OrderShipped.php
public function via($notifiable): array
{
return $notifiable->wantsSms() ? [SmsBroadcastChannel::class] : [];
}
public function toSmsBroadcast($notifiable): SmsBroadcastMessage
{
return SmsBroadcastMessage::create()
->content("Order {$this->order->number} is on its way.");
}
// on the notifiable model
public function routeNotificationForSmsBroadcast(): ?string
{
return $this->phone; // normalise to +614xxxxxxxx before you get here
}
Trigger it like any other notification:
$user->notify(new OrderShipped($order));
That's the whole happy path. If that works end-to-end in staging, you've validated the package; everything below is about making it survive production.
On-demand notifications for unauthenticated recipients (OTP to a number you only have in the request):
Notification::route(SmsBroadcastChannel::class, $phone)
->notify(new SendOtp($code));
This avoids modelling a throwaway user and is the most common way people use these channels.
Gate the channel on consent, not on config. Build the decision into via() so opt-outs, DND windows, and channel preference live next to the business rule:
public function via($notifiable): array
{
return collect([
$notifiable->sms_opt_in && ! $notifiable->isOutsideQuietHours()
? SmsBroadcastChannel::class : null,
$notifiable->email_opt_in ? 'mail' : null,
])->filter()->values()->all();
}
Queue it, then throttle it. SMS gateways rate-limit per account and will reject bursts. Put ShouldQueue on the notification and add a rate-limited queue or a Redis::throttle() middleware. Because each channel in via() runs within one queued job, a slow HTTP call to the gateway also delays the mail channel — if that matters, split SMS into its own notification.
Persist the provider reference. The gateway returns an identifier for the send; if the message class exposes it, capture it in a sms_logs table alongside the notification class and recipient. You will need this when a customer says "I never got it" and you have no other audit trail.
Wrap it behind your own interface if SMS is core to the product.
interface SmsSender { public function send(string $to, string $body): void; }
One adapter calls this package; another calls Twilio or a local aggregator. This package has 2 stars and 0 dependents — a thin abstraction costs you an hour now and saves a migration later if the package dies or the provider changes its API.
Testing. Two complementary levels:
Notification::fake();
$user->notify(new OrderShipped($order));
Notification::assertSentTo($user, OrderShipped::class, function ($n, $channels) {
return in_array(SmsBroadcastChannel::class, $channels);
});
and, for the wire format, Http::fake() around a direct channel invocation so you can assert the outgoing payload (to, sender, message) — Notification::fake() never exercises the message builder or the HTTP client, so subtle bugs in toSmsBroadcast() slip through.
Multi-environment sender IDs. Use a distinct alphanumeric sender per environment (MyApp-Stg vs MyApp) so a staging test can never be mistaken for production traffic, and so you can revoke staging credentials independently.
composer.json, read src/ once, and be prepared to fork. Budget for the possibility that the provider's API has moved on and the package hasn't.laravel-notification-channels family is consistent in spirit but not in naming — SmsBroadcastChannel vs SmsBroadcastChannelClass, toSmsBroadcast vs toSms, ->content() vs ->body(). The README is the contract; the source is the truth. A five-minute grep -rn "function to" vendor/laravel-notification-channels/sms-broadcast/src saves an hour of guessing.0412 345 678, +61412345678, 61412345678, and with stray spaces. Pick E.164 (+61412345678) and enforce it in routeNotificationForSmsBroadcast() or a cast, not inside the message builder. Silent drops with no exception are the worst failure mode.mb_strlen($renderedBody) <= 160 and rejects non-GSM-7 characters in templates.null return from toSmsBroadcast() silently skips the send. That's a feature (lets you suppress a message conditionally) and a debugging trap. If messages "disappear," check the builder before you check the credentials.routeNotificationForSmsBroadcast() that returns null and let the channel skip, or validate phone presence at the point of opt-in.SmsBroadcastChannel::send() to see whether it calls ->throw() on the HTTP response. If it doesn't, wrap your calls and listen for Laravel's Illuminate\Notifications\Events\NotificationFailed to get alerting.SMSBROADCAST_*, run php artisan config:cache in the deploy step; in local dev run config:clear. Half the "credentials aren't working" issues in this family of packages are a stale cached config.vendor/. If you need retry logic, logging, or a different endpoint, bind your own implementation into the container and resolve it from your notification, or copy the two source files into app/Sms/ and take ownership outright. The package is small enough that owning it is a legitimate option.How can I help you explore Laravel packages today?