Transactional SMS messages are automated, triggered communications sent to individual users based on specific actions or events. Unlike marketing blasts, transactional SMS is expected, anticipated, and often critical to the user experience. Think order confirmations, shipping updates, password resets, appointment reminders, and two-factor authentication codes.
This guide walks you through implementing transactional SMS in Laravel using notifications, queues, and event-driven architecture.
The distinction matters legally and practically:
| Aspect | Transactional SMS | Marketing SMS |
|---|---|---|
| Consent | Existing business relationship assumed | Requires explicit opt-in |
| Content | Account activity, order updates, security | Promotions, sales, newsletters |
| Frequency | Triggered by user action | Scheduled campaigns |
| Opt-out | Typically not required by law | Must include opt-out |
| Priority | High — delivered ASAP | Lower — batched delivery |
Transactional messages enjoy higher delivery rates and faster throughput because carriers recognize them as time-sensitive.
Laravel’s notification system is the ideal foundation for transactional SMS. Start by creating a notification:
php artisan make:notification OrderConfirmation
---
Configure it to send via SMS:
```php
<?php
namespace App\Notifications;
use App\Models\Order;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
class OrderConfirmation extends Notification
{
use Queueable;
public function __construct(
public Order $order
) {}
public function via(object $notifiable): array
{
// Prefer SMS, fall back to email
return $notifiable->phone ? ['sms'] : ['mail'];
}
public function toSms(object $notifiable): string
{
return sprintf(
'Order #%s confirmed! Your %s will ship to %s. Total: $%s. Track at %s/tracking/%s',
$this->order->order_number,
$this->order->items->count() . ' item(s)',
$this->order->shipping_address,
number_format($this->order->total, 2),
config('app.url'),
$this->order->tracking_number
);
}
}
---
Send it from your controller after order placement:
```php
<?php
namespace App\Http\Controllers;
use App\Models\Order;
use App\Notifications\OrderConfirmation;
use Illuminate\Http\Request;
class OrderController extends Controller
{
public function store(Request $request)
{
$order = Order::create($request->validated());
// Process payment...
// Send transactional SMS
$order->user->notify(new OrderConfirmation($order));
return redirect()->route('orders.show', $order);
}
}
---
## Shipping Status Updates
Keep customers informed as their order progresses. Create a dedicated notification:
```php
<?php
namespace App\Notifications;
use App\Models\Shipment;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
class ShippingUpdate extends Notification
{
use Queueable;
public function __construct(
public Shipment $shipment,
public string $status,
public ?string $location = null
) {}
public function via(object $notifiable): array
{
$preferences = $notifiable->notificationPreferences()
->forType('shipping_updates')
->first();
return $preferences?->channel === 'sms' || !$preferences
? ['sms']
: [$preferences->channel];
}
public function toSms(object $notifiable): string
{
$messages = [
'shipped' => "Your order has shipped! Tracking: {$this->shipment->tracking_number}. Estimated delivery: {$this->shipment->estimated_delivery->format('M d')}.",
'in_transit' => "Your package is in transit. Current location: {$this->location}. ETA: {$this->shipment->estimated_delivery->format('M d')}.",
'out_for_delivery' => "Your package is out for delivery today! Expect it by {$this->shipment->delivery_window}.",
'delivered' => "Your package was delivered! Thank you for your order.",
'exception' => "There's a delay with your shipment. Current status: {$this->shipment->exception_detail}. We'll update you soon.",
];
return $messages[$this->status] ?? "Shipping update: {$this->status} — {$this->shipment->tracking_number}";
}
}
---
Trigger updates from your shipping carrier webhook:
```php
<?php
namespace App\Http\Controllers\Webhooks;
use App\Models\Shipment;
use App\Notifications\ShippingUpdate;
use Illuminate\Http\Request;
class ShippingWebhookController extends Controller
{
public function __invoke(Request $request)
{
$event = $request->input('event');
$tracking = $request->input('tracking_number');
$shipment = Shipment::where('tracking_number', $tracking)->firstOrFail();
$shipment->order->user->notify(
new ShippingUpdate($shipment, $event, $request->input('location'))
);
return response('OK');
}
}
---
## Appointment Reminders
Transactional SMS drastically reduces no-shows for appointment-based businesses:
```php
<?php
namespace App\Notifications;
use App\Models\Appointment;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
class AppointmentReminder extends Notification
{
use Queueable;
public function __construct(
public Appointment $appointment,
public string $reminderType = 'upcoming' // 'upcoming', 'followup', 'rescheduled'
) {}
public function via(object $notifiable): array
{
return ['sms'];
}
public function toSms(object $notifiable): string
{
$timezone = $notifiable->timezone ?? 'UTC';
$localTime = $this->appointment->starts_at->setTimezone($timezone);
$messages = [
'upcoming' => "Reminder: Your appointment is {$localTime->diffForHumans()} on {$localTime->format('D, M j g:i A')}. Reply C to confirm or R to reschedule.",
'followup' => "How was your appointment? Reply with a rating (1-5) or visit " . config('app.url') . "/feedback/{$this->appointment->id} to leave feedback.",
'rescheduled' => "Your appointment has been rescheduled to {$localTime->format('D, M j g:i A')}. Reply C to confirm.",
];
return $messages[$this->reminderType];
}
}
---
## Using Laravel Notifications for Transactional Messages
Laravel's notification system provides everything you need for transactional SMS:
```php
<?php
namespace App\Notifications;
use Illuminate\Notifications\Notification;
use Illuminate\Notifications\Messages\VonageMessage;
class TwoFactorCode extends Notification
{
public function __construct(
public string $code
) {}
public function via(object $notifiable): array
{
return ['vonage'];
}
public function toVonage(object $notifiable): VonageMessage
{
return (new VonageMessage)
->content("Your verification code is: {$this->code}. It expires in 10 minutes.")
->from(config('services.vonage.sms_from'));
}
}
---
You can route to different providers based on the notification type:
```php
<?php
namespace App\Models;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
class User extends Authenticatable
{
use Notifiable;
public function routeNotificationForSms(): ?string
{
return $this->phone;
}
public function routeNotificationForVonage(): ?string
{
// Use Vonage specifically for security-critical messages
return $this->phone;
}
}
---
## Personalization with Order Data
Transactional messages perform best when they include specific, relevant data:
```php
<?php
namespace App\Notifications;
use App\Models\Order;
class OrderShipped extends Notification
{
public function __construct(
public Order $order
) {}
public function toSms(object $notifiable): string
{
return collect([
"📦 Your order #{$this->order->order_number} has shipped!",
"",
"Items:",
...$this->order->items->map(fn($item) => " • {$item->quantity}x {$item->name}"),
"",
"Tracking: {$this->order->tracking_url}",
"Est. delivery: {$this->order->estimated_delivery->format('D, M j')}",
"",
"Thank you for shopping with us!",
])->implode("\n");
}
}
---
## Delivery Guarantees for Transactional SMS
Transactional SMS requires high delivery reliability. Implement these strategies:
**Queue with high-priority channel:**
```php
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Notification;
class CriticalAlert extends Notification implements ShouldQueue
{
use Queueable;
public function __construct()
{
// High-priority queue for transactional messages
$this->onQueue('critical');
}
}
---
**Monitor delivery status:**
```php
<?php
namespace App\Console\Commands;
use App\Models\NotificationLog;
use Illuminate\Console\Command;
class RetryFailedTransactional extends Command
{
protected $signature = 'sms:retry-failed {--hours=1}';
protected $description = 'Retry failed transactional SMS messages';
public function handle()
{
$failed = NotificationLog::query()
->where('status', 'failed')
->where('type', 'transactional')
->where('created_at', '>=', now()->subHours($this->option('hours')))
->get();
foreach ($failed as $log) {
$log->retry();
$this->info("Retried notification #{$log->id}");
}
}
}
---
## Best Practices
1. **Always queue transactional notifications** — never send SMS synchronously in request lifecycle
2. **Include clear sender identification** — use your business name as the sender ID
3. **Respect quiet hours** — avoid sending between 9 PM and 8 AM unless critical
4. **Provide next-step context** — tracking links, appointment links, support numbers
5. **Log everything** — store message IDs, statuses, and timestamps for audit trails
## Testing Transactional SMS
```php
<?php
namespace Tests\Feature;
use App\Models\Order;
use App\Notifications\OrderConfirmation;
use Illuminate\Support\Facades\Notification;
use Tests\TestCase;
class TransactionalSmsTest extends TestCase
{
public function test_order_confirmation_sends_sms()
{
Notification::fake();
$order = Order::factory()->create();
$order->user->notify(new OrderConfirmation($order));
Notification::assertSentTo(
$order->user,
OrderConfirmation::class,
function ($notification) use ($order) {
return $notification->order->is($order);
}
);
}
}
---
## Conclusion
Transactional SMS is one of the highest-ROI features you can add to a Laravel application. Whether you're running an ecommerce store, a SaaS platform, or a service business, automated SMS notifications improve customer experience and reduce support load.
Use the [Laravel SMS API](/services/laravel-sms-api) to get started with transactional messaging, or explore our [enterprise SMS solution](/services/enterprise-sms-solution-laravel) for high-volume needs.
For more on customer notifications, see our guide on [building a notification software platform](/services/laravel-customer-notification-software).