bahricanli / whatsapp-business
Laravel notification channel for WhatsApp Business Cloud API (Meta)
Requires
- php: >=7.4
- guzzlehttp/guzzle: >=6.5
- illuminate/notifications: >=5.5
- illuminate/support: >=5.5
Requires (Dev)
- mockery/mockery: ^1.5
- orchestra/testbench: ^6.0|^7.0|^8.0
- phpunit/phpunit: ^9.0|^10.0
README
Laravel notification channel for sending template messages via Meta's official WhatsApp Business Cloud API — no self-hosted bridge, no unofficial WhatsApp Web automation.
Designed as a parallel channel alongside bahricanli/corvass (SMS) — the API is intentionally similar for easy integration.
Requirements
- PHP 7.4+
- Laravel 5.5+
- A WhatsApp Business Platform phone number set up in Meta Business Manager
- At least one pre-approved message template (business-initiated messages outside the 24h customer service window require an approved template — free-form text is not allowed)
Installation
composer require bahricanli/whatsapp-business
Laravel's auto-discovery registers the service provider automatically.
Publish the config file:
php artisan vendor:publish --tag=whatsapp-business-config
Configuration
Add the following to your .env:
WHATSAPP_BUSINESS_TOKEN= # permanent system-user access token WHATSAPP_BUSINESS_PHONE_NUMBER_ID= # from Meta Business Manager > WhatsApp > API Setup WHATSAPP_BUSINESS_API_VERSION=v21.0 WHATSAPP_BUSINESS_TIMEOUT=10
Or edit config/whatsapp-business.php directly.
Usage
1. Direct usage (no notification system)
use NotificationChannels\WhatsAppBusiness\WhatsAppFacade as WhatsAppBusiness; use NotificationChannels\WhatsAppBusiness\WhatsAppMessage; WhatsAppBusiness::sendMessage( WhatsAppMessage::create('appointment_created') ->language('tr') ->parameters(['Ahmet Uzman', '2026-08-01', '14:00']) ->to('905551234567') );
2. As a Laravel notification channel
In your notification class:
use Illuminate\Notifications\Notification; use NotificationChannels\WhatsAppBusiness\WhatsAppChannel; use NotificationChannels\WhatsAppBusiness\WhatsAppMessage; class AppointmentCreatedNotification extends Notification { public function __construct(private array $appointment) {} public function via($notifiable): array { return [WhatsAppChannel::class]; } public function toWhatsAppBusiness($notifiable): WhatsAppMessage { return WhatsAppMessage::create('appointment_created') ->parameters([ $this->appointment['specialist'], $this->appointment['date'], $this->appointment['time'], ]); // Recipient is pulled from routeNotificationForWhatsAppBusiness() // if ->to() isn't called explicitly. } }
In your notifiable model (e.g. Member):
public function routeNotificationForWhatsAppBusiness(): string { return $this->phone_number; // e.g. 905551234567 }
3. Parallel with SMS (Corvass)
Send via both SMS and WhatsApp, falling back to SMS-only when WhatsApp is off:
public function via($notifiable): array { if (! filter_var(env('WHATSAPP_ENABLED', false), FILTER_VALIDATE_BOOLEAN)) { return [\NotificationChannels\Corvass\CorvassChannel::class]; } return [ \NotificationChannels\WhatsAppBusiness\WhatsAppChannel::class, \NotificationChannels\Corvass\CorvassChannel::class, ]; }
Dynamic URL Button Templates
Some templates put their only variable in a button's URL instead of the
body (e.g. a button registered as https://example.com/{{1}}). This is a
separate placeholder from the body's, sent as its own component:
WhatsAppMessage::create('belge_dogrulama') ->buttonParameter('AL845') // fills https://tarti.me/{{1}} -> just the suffix, not the full URL ->to('905551234567');
You can combine parameters() (body) and buttonParameter() (button) on
the same message if a template has both.
Free-Form Replies (24h Window)
If a contact has messaged you within the last 24 hours, you may reply with plain text — no approved template required:
use NotificationChannels\WhatsAppBusiness\WhatsAppFacade as WhatsAppBusiness; WhatsAppBusiness::sendText('905551234567', 'Teşekkürler, hemen bakıyorum.');
Outside that window, Meta rejects free-form text — fall back to sendMessage()
with an approved template.
Message Templates
Unlike a plain-text bridge, the official Cloud API requires every business-initiated message to use a pre-approved template. Submit templates for approval in Meta Business Manager, then reference the exact approved name/language in your notification:
WhatsAppMessage::create('phone_confirmation_code') ->language('tr') ->parameters(['482917']); // fills the template's {{1}}
The parameters() array maps positionally to {{1}}, {{2}}, ... in the
template body — order matters and must match what was approved.
Phone Number Formats
The library normalizes phone numbers automatically:
| Input | Stored as |
|---|---|
905551234567 |
905551234567 |
+905551234567 |
905551234567 |
0905551234567 |
905551234567 |
05551234567 |
905551234567 |
5551234567 |
905551234567 |
Events
| Event | Fired |
|---|---|
SendingMessage |
Before a message is sent |
MessageWasSent |
After successful delivery |
use NotificationChannels\WhatsAppBusiness\Events\MessageWasSent; Event::listen(MessageWasSent::class, function (MessageWasSent $event) { Log::info('WhatsApp Business sent', [ 'to' => $event->message->to, 'template' => $event->message->template, 'wamid' => $event->response['messages'][0]['id'] ?? null, ]); });
License
MIT — see LICENSE.md.