Getting started
Five minutes from composer require to "I can send a WhatsApp message from PHP." Pick Cloud API (faster setup, business use) or Web sidecar (personal numbers, groups, everything) — or both.
Prerequisites
- PHP 8.2+ with
ext-curl,ext-openssl - Laravel 11 or 12 or 13
- Composer
- Only for the Web sidecar: Node.js 18+, npm, and ~600 MB free disk (Puppeteer downloads Chromium)
- Only for the admin UI: Livewire 3.x + Flux UI
1. Install the package
composer require kstmostofa/laravel-whatsapp
php artisan vendor:publish --tag=laravel-whatsapp-config
php artisan vendor:publish --tag=laravel-whatsapp-migrations
php artisan migrateThe migrations create three tables: wa_sessions, wa_messages, wa_contacts. They're opt-in — the package works without them; persistence just won't kick in.
2. Pick a backend
Both backends share the same WhatsApp:: facade. You can install one now and add the other later.
Path A — Cloud API (Meta) — business use
Best for templated transactional messages at scale. Official, supported, no ban risk. Requires a Meta WhatsApp Business Account.
WHATSAPP_ACCESS_TOKEN=EAAG...permanent-system-user-token
WHATSAPP_PHONE_NUMBER_ID=123456789012345
WHATSAPP_BUSINESS_ACCOUNT_ID=987654321098765
WHATSAPP_APP_SECRET=your-meta-app-secret
WHATSAPP_VERIFY_TOKEN=any-random-string-you-make-upWhere to find these → Installation guide.
Send your first message:
use Kstmostofa\LaravelWhatsApp\Facades\WhatsApp;
WhatsApp::messages()->sendTemplate(
to: '+9665XXXXXXXX',
templateName: 'hello_world',
languageCode: 'en_US',
);Path B — Web sidecar — personal number, everything
Best when you need group chats, status, or free-form messages from a personal WhatsApp account.
composer require livewire/livewire livewire/flux # only if you want the admin UI
php artisan whatsapp:sidecar:install # ~600 MB Chromium download, one-time
php artisan whatsapp:sidecar:start # spawns Node sidecar detachedSet in .env:
WHATSAPP_WEB_ENABLED=true
WHATSAPP_WEB_TOKEN=long-random-shared-secretPair your phone — visit http://your-app.test/whatsapp/sessions and click Start → scan the QR with your phone (WhatsApp → Settings → Linked Devices → Link a Device).
In a separate terminal (Supervisor in prod), start the event bridge so inbound messages reach Laravel:
php artisan whatsapp:web:listen mainSend a message:
use Kstmostofa\LaravelWhatsApp\Facades\WhatsApp;
WhatsApp::web('main')->messages()->sendText('+9665XXXXXXXX', 'Hello from PHP');
// ^ phone or 9665…@c.us — both work3. Receive messages
Both backends emit Laravel events. Add a listener:
use Kstmostofa\LaravelWhatsApp\Events\Web\MessageReceived; // sidecar
use Kstmostofa\LaravelWhatsApp\Events\MessageReceived as CloudInbound; // Cloud API
Event::listen(MessageReceived::class, function ($event) {
Log::info('Web inbound', [
'from' => $event->from(),
'body' => $event->body(),
'session' => $event->sessionId,
]);
});To persist inbound messages to the wa_messages table automatically:
WHATSAPP_PERSIST_INCOMING=trueThat enables a built-in listener — no extra code.
4. The one-line shortcut
Don't want to think about which backend? Use WhatsApp::send() — it routes based on recipient shape:
WhatsApp::send('+9665XXXXXXXX', 'Hello'); // → Cloud API (E.164 phone)
WhatsApp::send('966512345678@c.us', 'Hello'); // → Web sidecar (WA chat ID)
WhatsApp::send('+9665XXXXXXXX', 'Hi', backend: 'web', sessionId: 'main'); // Override5. (Optional) The admin UI
If you installed Livewire + Flux, open http://your-app.test/whatsapp. You get:
- Dashboard — sidecar status, message counts, recent activity
- Sessions — start/stop, QR display
- Compose — pick backend, type (text/image/document/template), send
- Conversations — full chat UI with bubbles, search, file attachments, edit/delete, sound on inbound
- Groups, Contacts, Webhooks log, Status
Three install paths depending on whether you use Tailwind → UI docs.
What's next
- Production? → Deployment checklist
- Don't want the UI? → Use the package headless. Just don't install
livewire/flux. The package detects it and skips registering UI routes. - Want to isolate WA data in a separate DB? → Configuration
- Troubleshooting? → Common issues