Ultimate Guide to Laravel SMS Integration (2026)

Ultimate Guide to Laravel SMS Integration (2026)
Laravel SMS Team
Laravel SMS Team
June 18, 2026

Ultimate Guide to Laravel SMS Integration (2026)

SMS remains one of the most reliable communication channels for web applications. Whether you’re sending OTP codes, order confirmations, marketing promotions, or two-factor authentication codes, integrating SMS into your Laravel application is a critical capability.

This guide covers everything you need to know about Laravel SMS integration in 2026 — from choosing the right gateway to advanced patterns like queueing, bulk messaging, and webhook handling.

Why Laravel for SMS Integration

Laravel provides a first-class notifications system that abstracts away the complexities of sending messages across multiple channels. With its built-in queue support, event system, and elegant API, Laravel makes SMS integration both powerful and maintainable.

Key advantages of using Laravel for SMS:

  • Unified notification API — send via SMS, email, Slack, or any channel with a single notification class
  • Queue integration — offload SMS sending to avoid blocking HTTP requests
  • Horizon monitoring — visual dashboard for queue workers and failed jobs
  • Rate limiting — built-in tools to prevent API throttling
  • Testing utilities — fake notifications for deterministic test suites

Choosing an SMS Gateway

Your choice of SMS gateway depends on your use case, budget, and target region. Here’s a comparison of the most popular providers for Laravel:

ProviderPrice (approx)Global CoverageLaravel PackageTwo-Way SMSDelivery Reports
Twilio$0.0079/msgExcellenttwilio/sdkYesYes
Vonage (Nexmo)$0.0061/msgVery Goodvonage/clientYesYes
AWS SNS$0.00645/msgGoodaws/aws-sdk-phpNoYes
Plivo$0.007/msgVery Goodplivo/plivo-phpYesYes
TextLocal$0.04/msgUK/EU FocusCustomYesYes
MessageBird$0.005/msgExcellentCustomYesYes

Twilio

Twilio is the most popular SMS API globally. It offers excellent documentation, a robust PHP SDK, and features like WhatsApp integration, verification services, and SIP calling.

Pros: Deep documentation, broad coverage, reliable infrastructure.
Cons: Slightly higher per-message pricing than some competitors.

Vonage (formerly Nexmo)

Vonage offers competitive pricing and a well-maintained PHP client. It excels in Europe and provides strong two-way SMS capabilities.

Pros: Good pricing, simple API, strong European coverage.
Cons: Smaller ecosystem than Twilio.

AWS SNS

If your infrastructure is already on AWS, SNS is a natural choice. It integrates tightly with other AWS services and offers pay-as-you-go pricing.

Pros: Low cost at scale, deep AWS integration.
Cons: No two-way SMS, requires AWS SDK setup, more complex delivery report handling.

Plivo

Plivo provides a strong alternative to Twilio with competitive pricing and a straightforward API.

Pros: Competitive pricing, good coverage, easy API.
Cons: Smaller community, fewer tutorials.

For a deeper comparison, check our guide on best SMS APIs for Laravel.

Setting Up Laravel for SMS

Start by installing the SDK for your chosen provider. Here’s a standard setup:

composer require twilio/sdk
---

Add your credentials to `.env`:

```env
TWILIO_SID=your_account_sid
TWILIO_AUTH_TOKEN=your_auth_token
TWILIO_FROM=+15551234567
---

Create a dedicated config file at `config/sms.php`:

```php
<?php

return [
    'default' => env('SMS_DRIVER', 'twilio'),

    'drivers' => [
        'twilio' => [
            'sid' => env('TWILIO_SID'),
            'token' => env('TWILIO_AUTH_TOKEN'),
            'from' => env('TWILIO_FROM'),
        ],

        'vonage' => [
            'api_key' => env('VONAGE_KEY'),
            'api_secret' => env('VONAGE_SECRET'),
            'from' => env('VONAGE_FROM'),
        ],
    ],
];
---

## Laravel Notifications System

Laravel's notification system is the recommended way to send SMS. Each notification is a dedicated class that defines one or more delivery channels.

### Creating an SMS Notification

```bash
php artisan make:notification OrderShipped
---

```php
<?php

namespace App\Notifications;

use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;

class OrderShipped extends Notification implements ShouldQueue
{
    use Queueable;

    public function __construct(
        protected string $orderNumber
    ) {}

    public function via(object $notifiable): array
    {
        return $notifiable->prefers_sms ? ['vonage'] : ['mail'];
    }

    public function toVonage(object $notifiable): \Illuminate\Notifications\Messages\VonageMessage
    {
        return (new \Illuminate\Notifications\Messages\VonageMessage)
            ->content("Your order #{$this->orderNumber} has shipped!");
    }
}
---

### Custom SMS Channel

If your provider isn't supported out of the box, create a custom channel:

```php
<?php

namespace App\Notifications\Channels;

use App\Services\SmsService;
use Illuminate\Notifications\Notification;

class SmsChannel
{
    public function __construct(
        protected SmsService $sms
    ) {}

    public function send(object $notifiable, Notification $notification): void
    {
        $message = $notification->toSms($notifiable);

        $this->sms->send(
            $notifiable->phone_number,
            $message->content
        );
    }
}
---

For a full walkthrough, see our tutorial on [how to send SMS in Laravel](/blog/how-to-send-sms-in-laravel).

## Queueing SMS for Performance

Sending SMS synchronously during an HTTP request increases response time and creates a poor user experience. Always queue SMS notifications.

### Making Notifications Queueable

```php
<?php

namespace App\Notifications;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Notification;

class WelcomeSms extends Notification implements ShouldQueue
{
    use Queueable;

    public function __construct(
        public string $message
    ) {
        $this->onQueue('sms');
    }
}
---

Dispatch the notification as usual — Laravel automatically pushes it to the queue:

```php
$user->notify(new WelcomeSms('Welcome to our platform!'));
---

### Configuring Horizon for SMS

Create a dedicated queue for SMS in `config/horizon.php`:

```php
'environments' => [
    'production' => [
        'sms' => [
            'connection' => 'redis',
            'queue' => ['sms'],
            'balance' => 'auto',
            'minProcesses' => 1,
            'maxProcesses' => 5,
            'tries' => 3,
            'timeout' => 30,
        ],
    ],
],
---

### Handling Failed Jobs

Failed SMS jobs should be logged and optionally retried via a fallback provider:

```php
<?php

namespace App\Jobs;

use App\Notifications\SmsFailed;
use Illuminate\Support\Facades\Log;

class HandleFailedSms
{
    public function handle(object $event): void
    {
        Log::error('SMS failed to send', [
            'notification' => get_class($event->notification),
            'notifiable' => $event->notifiable->id ?? null,
            'exception' => $event->exception->getMessage(),
        ]);

        // Optionally retry via fallback gateway
    }
}
---

Register the handler in `AppServiceProvider`:

```php
use Illuminate\Notifications\Events\NotificationFailed;

public function boot(): void
{
    Event::listen(
        NotificationFailed::class,
        HandleFailedSms::class
    );
}
---

For more details, read our guide on [Laravel SMS notifications using queues](/blog/laravel-sms-notifications-queues).

## OTP Verification via SMS

One-time passwords sent via SMS remain the most common phone verification method.

### Generating and Sending OTP

```php
<?php

namespace App\Services;

use Illuminate\Support\Facades\Cache;

class OtpService
{
    public function generate(string $identifier, int $length = 6, int $ttl = 300): string
    {
        $code = (string) random_int(10 ** ($length - 1), (10 ** $length) - 1);

        Cache::put("otp:{$identifier}", [
            'code' => $code,
            'attempts' => 0,
        ], $ttl);

        return $code;
    }

    public function verify(string $identifier, string $code): bool
    {
        $stored = Cache::get("otp:{$identifier}");

        if (! $stored || $stored['attempts'] >= 5) {
            return false;
        }

        Cache::increment("otp:{$identifier}.attempts");

        if (! hash_equals($stored['code'], $code)) {
            return false;
        }

        Cache::forget("otp:{$identifier}");

        return true;
    }
}
---

### Rate Limiting OTP Requests

```php
use Illuminate\Support\Facades\RateLimiter;

$key = "otp-request:{$user->id}";

if (RateLimiter::tooManyAttempts($key, 3)) {
    $seconds = RateLimiter::availableIn($key);
    return back()->withErrors(['phone' => "Try again in {$seconds} seconds."]);
}

RateLimiter::hit($key, 60);
---

See the complete implementation in our [Laravel OTP verification via SMS guide](/blog/laravel-otp-verification-sms).

## Bulk SMS Sending

Sending SMS to thousands of recipients requires careful batching and rate limiting to avoid API throttling.

### Chunked Dispatch

```php
User::where('subscribed', true)->chunk(200, function ($users) {
    foreach ($users as $user) {
        $user->notify(new PromotionalSms($user, 'Big sale this weekend!'));
    }
});
---

### Using Laravel Batches

```php
use Illuminate\Bus\Batch;
use Illuminate\Support\Facades\Bus;

$batch = Bus::batch(
    User::where('subscribed', true)->map(fn ($user) => new SendPromoSms($user))
)->then(function (Batch $batch) {
    Log::info('Bulk SMS campaign completed: ' . $batch->totalJobs);
})->catch(function (Batch $batch) {
    Log::error('Bulk SMS campaign had failures', [
        'failed' => $batch->failedJobs,
    ]);
})->dispatch();
---

### Rate Limiting API Calls

Wrap your provider calls with Laravel's rate limiter:

```php
use Illuminate\Support\Facades\RateLimiter;

$executed = RateLimiter::attempt(
    'sms-api',
    $perSecond = 10,
    function () use ($user, $message) {
        // Send SMS via provider
    }
);
---

## Two-Factor Authentication with SMS

Laravel Fortify and Jetstream provide two-factor authentication, but you can build a custom 2FA flow with SMS:

```php
<?php

namespace App\Actions;

use App\Notifications\TwoFactorCode;
use Illuminate\Support\Facades\Cache;

class SendTwoFactorCode
{
    public function __invoke($user): void
    {
        $code = (string) random_int(100000, 999999);

        Cache::put("2fa:{$user->id}", [
            'code' => $code,
            'expires_at' => now()->addMinutes(10),
        ], 600);

        $user->notify(new TwoFactorCode($code));
    }
}
---

## Webhook Handling for Delivery Reports

Most SMS providers send delivery receipts via webhook. Handle them with a dedicated route:

```php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\SmsWebhookController;

Route::post('/webhooks/twilio/status', [SmsWebhookController::class, 'twilioStatus'])
    ->name('webhooks.twilio.status');
---

Controller example:

```php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;

class SmsWebhookController extends Controller
{
    public function twilioStatus(Request $request): void
    {
        $request->validate([
            'MessageSid' => 'required|string',
            'MessageStatus' => 'required|string',
            'To' => 'required|string',
        ]);

        Log::info('SMS delivery update', $request->only([
            'MessageSid', 'MessageStatus', 'To', 'ErrorCode',
        ]));

        // Update your database delivery status
    }
}
---

## Best Practices & Error Handling

### Logging

Always log SMS sent/failed events for debugging and auditing:

```php
use Illuminate\Support\Facades\Log;

Log::channel('sms')->info('SMS sent', [
    'to' => $phone,
    'message_id' => $response->sid,
    'provider' => 'twilio',
]);
---

### Retry Logic with Exponential Backoff

```php
public function retryUntil(): \DateTime
{
    return now()->addMinutes(5);
}

public function backoff(): array
{
    return [2, 5, 10, 30];
}
---

### Fallback Gateway

Switch providers on failure:

```php
try {
    $this->twilio->send($to, $message);
} catch (TwilioException $e) {
    Log::warning('Twilio failed, falling back to Vonage', [
        'error' => $e->getMessage(),
    ]);
    $this->vonage->send($to, $message);
}
---

### Environment-Specific Configuration

Use different SMS drivers per environment:

```env
# Production
SMS_DRIVER=twilio

# Staging
SMS_DRIVER=log

# Testing
SMS_DRIVER=null
---

## Conclusion

Integrating SMS into Laravel applications is straightforward thanks to the framework's notification system, queue infrastructure, and extensive package ecosystem. By following the patterns outlined in this guide, you can build a reliable, scalable SMS system that handles everything from simple alerts to complex bulk campaigns.

Start with a single [notification class](/blog/how-to-send-sms-in-laravel), add [queue support](/blog/laravel-sms-notifications-queues) for performance, implement [OTP verification](/blog/laravel-otp-verification-sms) for security flows, and choose the right [SMS API](/blog/best-sms-apis-for-laravel) for your needs.

## Related Articles

- [How to Send SMS in Laravel (Step-by-Step Guide)](/blog/how-to-send-sms-in-laravel)
- [Laravel SMS Notifications Using Queues (Performance Guide)](/blog/laravel-sms-notifications-queues)
- [Laravel OTP Verification via SMS (Complete Guide)](/blog/laravel-otp-verification-sms)
- [Best SMS APIs for Laravel in 2026](/blog/best-sms-apis-for-laravel)