laravel-websms maintained by websms-nz
WebSMS for Laravel
Laravel package for the WebSMS Connexus API — send SMS, OTP / 2FA codes, and templated appointment reminders from any Laravel 10/11/12 application. Includes a notification channel so any Notifiable can receive SMS via ->via('websms').
NZ and AU phone numbers are normalised automatically (027 123 4567 → 64271234567, 0412 345 678 → 61412345678).
Installation
composer require websms-nz/laravel-websms
Publish the config file:
php artisan vendor:publish --tag=websms-config
Add credentials to your .env:
WEBSMS_CLIENT_ID=your_client_id
WEBSMS_CLIENT_SECRET=your_client_secret
# Optional. If omitted, WebSMS routes via a shared shortcode.
WEBSMS_FROM=2190
# Optional. Defaults to your APP_NAME.
WEBSMS_OTP_COMPANY=YourApp
Get credentials at https://websms.co.nz/members/api-keys.php.
Usage
Send an SMS
use WebSms;
WebSms::send('+64211234567', 'Hello from Laravel.');
Send an OTP / 2FA code
WebSMS generates a code if you don't supply one. The response includes the code so you can persist it for verification.
$response = WebSms::otp()->send('+64211234567');
// => ['success' => true, 'code' => '482917', 'message_id' => '...']
session(['otp_code' => $response['code']]);
// Or supply your own:
WebSms::otp()->send('+64211234567', code: '123456', company: 'Acme');
Send a templated appointment reminder
Templated server-side at $0.08 + GST per SMS part (or your custom rate
if lower). Only the fields you supply are included in the message body.
WebSms::appointment()->send(
to: '+64211234567',
company: 'Sunrise Wellness Clinic',
name: 'Barry',
date: '28/10/25',
time: '11:15am',
replyY: true,
callUs: '09-555-1234',
address: '42 Main Street, Auckland',
);
// => "Hi Barry, your next appointment with Sunrise Wellness Clinic is on
// 28/10/25 at 11:15am. Reply Y to confirm or Pls call us on 09-555-1234
// to re-book. Our Address: 42 Main Street, Auckland. Std SMS charges apply."
// Minimal call (company falls back to config('websms.appointment.company') / otp.company):
WebSms::appointment()->send(to: '+64211234567', company: 'Acme Trades');
// Sandbox (no send, no charge — for integration testing):
WebSms::appointment()->send(to: '+64211234567', company: 'Acme', sandbox: true);
Check balance / number lookup
WebSms::balance();
WebSms::lookup('+64211234567'); // returns carrier + porting info for NZ numbers
Receive inbound replies (MO) and delivery reports (DLR)
WebSMS posts inbound SMS replies and delivery reports as JSON to a single URL configured in your WebSMS members area. The package ships a controller that parses the payload and dispatches Laravel events you can listen for.
1. Mount the controller in routes/web.php:
use WebSmsNz\Laravel\Http\WebhookController;
Route::post('/webhooks/websms', [WebhookController::class, 'handle'])
->withoutMiddleware([\App\Http\Middleware\VerifyCsrfToken::class]);
2. Paste the URL into the Webhook URL field on
https://websms.co.nz/members/api-keys.php. Recommended: append a secret —
https://yourapp.com/webhooks/websms?secret=... — and set the same value
as WEBSMS_WEBHOOK_SECRET in your .env (the controller will reject
mismatched requests with 401).
3. Listen for events anywhere in your app:
use Illuminate\Support\Facades\Event;
use WebSmsNz\Laravel\Events\WebSmsMessageReceived;
use WebSmsNz\Laravel\Events\WebSmsDeliveryReportReceived;
Event::listen(WebSmsMessageReceived::class, function ($event) {
// $event->from, ->to, ->body, ->messageId, ->timestamp, ->encoding, ->network, ->raw
Log::info("Reply from {$event->from}: {$event->body}");
});
Event::listen(WebSmsDeliveryReportReceived::class, function ($event) {
// $event->messageId, ->status, ->statusCode, ->timestamp, ->details, ->raw
if ($event->isFailed()) {
Log::warning("DLR failed for {$event->messageId}: {$event->status}");
}
});
Or generate proper queued listeners with php artisan make:listener so
heavy work runs on the queue rather than blocking the webhook response.
Notification channel
class AppointmentReminder extends Notification
{
public function via($notifiable): array
{
return ['websms'];
}
public function toWebSms($notifiable): \WebSmsNz\Laravel\Notifications\WebSmsMessage
{
return \WebSmsNz\Laravel\Notifications\WebSmsMessage::make(
"Kia ora {$notifiable->first_name}, your appointment is tomorrow at 1:00pm."
);
}
}
The notifiable should expose the recipient phone number via either:
public function routeNotificationForWebSms($notification)
{
return $this->mobile_number;
}
…or simply have a phone_number attribute.
CLI sanity check
php artisan websms:test +64211234567 --message="Hello from Laravel"
Configuration reference
See config/websms.php after publishing. The two required keys are
client_id and client_secret; everything else has sensible defaults.
| Key | Env | Required | Default |
|---|---|---|---|
client_id |
WEBSMS_CLIENT_ID |
yes | — |
client_secret |
WEBSMS_CLIENT_SECRET |
yes | — |
base_url |
WEBSMS_BASE_URL |
no | https://websms.co.nz/api/connexus |
from |
WEBSMS_FROM |
no | (uses shared shortcode) |
otp.company |
WEBSMS_OTP_COMPANY |
no | APP_NAME |
otp.comment |
WEBSMS_OTP_COMMENT |
no | Valid for 5 minutes. |
appointment.company |
WEBSMS_APPOINTMENT_COMPANY |
no | falls back to otp.company |
timeout |
WEBSMS_TIMEOUT |
no | 10 seconds |
webhook_secret |
WEBSMS_WEBHOOK_SECRET |
no | (no auth on webhook controller) |
cache_store |
WEBSMS_CACHE_STORE |
no | application's default cache store (used for token cache) |
Errors
All API and network failures are wrapped in WebSmsNz\Laravel\Exceptions\WebSmsException. The original API response (where available) is accessible via $exception->getApiResponse().
License
MIT