laravel-notification-channels/plivo
Send SMS notifications through Plivo in Laravel. This channel plugs into Laravel's notification system so you can deliver text messages via the Plivo API using a simple toPlivo method on your notifications, with support for configuring the sender ID and recipient numbers.
The channel is a thin wrapper around plivo/plivo-php. Minimum viable setup:
composer require laravel-notification-channels/plivo
php artisan vendor:publish --provider="NotificationChannels\Plivo\PlivoServiceProvider"
config/plivo.php via .env:PLIVO_AUTH_ID=MAXXXXXXXXXXXX
PLIVO_AUTH_TOKEN=xxxxxxxxxxxxxxxxxxxx
PLIVO_FROM=+15551234567
Notification class:use NotificationChannels\Plivo\PlivoChannel;
use NotificationChannels\Plivo\PlivoMessage;
class OrderShipped extends Notification
{
public function via($notifiable)
{
return [PlivoChannel::class]; // or 'plivo' if the alias is registered
}
public function toPlivo($notifiable)
{
return (new PlivoMessage())
->content('Your order has shipped.');
}
}
Then $user->notify(new OrderShipped());.
The recipient is resolved by routeNotificationForPlivo() on the notifiable, falling back to the package's default attribute (usually phone_number). Add an explicit method on your model so you are never guessing which column wins:
public function routeNotificationForPlivo()
{
return $this->phone_e164;
}
Where to look first: the package's src/ for PlivoChannel, PlivoMessage, and the service provider to confirm the config keys and the notification alias. Given the low activity, read the source rather than relying on the README.
On-demand notifications — for one-off messages where the recipient isn't a notifiable model (admin alerts, smoke tests):
Notification::route('plivo', '+15551234567')
->notify(new ServiceDownAlert($service));
Queued delivery — SMS is an outbound HTTP call; always queue it. Implement ShouldQueue on the notification and keep it off the request path:
class OrderShipped extends Notification implements ShouldQueue
{
public $tries = 3;
public $backoff = [10, 60, 300];
}
Reusable message builders — keep PlivoMessage construction in dedicated notification classes rather than inline in controllers. One notification class per business event (OtpCode, PaymentFailed, AppointmentReminder) makes Notification::fake() assertions readable and keeps templates testable.
Per-message sender overrides — useful when you maintain separate numbers for marketing vs. transactional traffic:
return (new PlivoMessage())
->from(config('plivo.from_transactional'))
->content($body);
Testing — use Laravel's notification fake and assert on the notifiable and channel:
Notification::fake();
$user->notify(new OrderShipped($order));
Notification::assertSentTo($user, OrderShipped::class, function ($notification, $channels) {
return in_array(PlivoChannel::class, $channels);
});
The fake intercepts before the channel runs, so this tests routing and via() logic; it does not test payload shape. For payload tests, instantiate the notification and assert on toPlivo($user) directly.
Integration tip — centralize the SMS body generation in a small service or Blade-backed template so message length (see gotchas) can be enforced in one place before the channel ever sees it.
The package is effectively unmaintained. Last release June 2021, no dependents. It targets Laravel 8-era contracts and pins an old plivo/plivo-php. Before committing:
NotificationChannel contract on your Laravel version.vendor/. Fork it, or drop the dependency and write a ~40-line custom channel around plivo/plivo-php — the surface area is tiny and you control the SDK version.Phone numbers must be E.164. Plivo rejects anything without the + country code, and it will not infer a default country. Normalize on save or in an accessor; do not rely on the channel to sanitize.
The sender ID must exist on your Plivo account for that destination country. Alphanumeric sender IDs work in some countries and not others; a US destination requires a long code, short code, or toll-free number. A from that works in staging may fail in production for a different destination.
Config caching is the classic silent failure. If you add PLIVO_* to .env and the config is cached, the old values persist. Run php artisan config:clear (and re-cache) after any credential change.
Message length and encoding. GSM-7 is 160 chars per segment; Unicode (emoji, accents) drops to 70. Every segment is billed. Concatenation happens transparently, so a long body returns success while costing 3–4× what you expected. Truncate or split deliberately rather than letting Plivo decide.
Error handling is easy to miss. The channel wraps an SDK call that can throw or return a non-2xx. Decide explicitly whether failures should retry (transient 5xx, rate limits) or fail permanently (invalid number, blocked destination) — a blanket $tries = 3 on a bad number wastes quota and may trip spam controls. Log the Plivo request_uuid on success and the full error body on failure; the SDK's exceptions are not always descriptive.
Rate limiting. Plivo enforces per-number throughput. Bulk sends from a single notification loop will hit 429s. Use Bus::batch() with a rate limiter on the queue connection, or chunk the dispatch with sleep() between batches.
Route caching. If your notifiable relies on a dynamic routeNotificationForPlivo(), confirm it is not being bypassed by a cached route. Also check that the Notification facade is imported (Illuminate\Support\Facades\Notification) — a common source of "class not found" in on-demand examples.
Extension points are limited. There is no event system of its own. If you need delivery receipts or spend tracking, either listen at the Plivo webhook level with your own controller, or wrap PlivoChannel in a custom channel that delegates to it and emits an application event. Given the package's age, wrapping is usually cheaper than patching.
How can I help you explore Laravel packages today?