pushinbr / pam-whatsapp-web
Persistent WhatsApp Web client for PHP on PAM, without Node.js or Puppeteer.
Requires
- php: ^8.5
- ext-mbstring: *
- chillerlan/php-qrcode: ^6.0
- pushinbr/pam-browser: ^1.0
Requires (Dev)
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^11.5
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-11-09 16:13:36 UTC
README
The whatsapp-web.js experience, now in persistent, pure PHP.
Terminal QR · persistent sessions · real-time events · media · groups · channels · calls. PAM keeps the process alive, PAM Browser speaks the Chrome DevTools Protocol, and this library provides the typed API: no Node.js, npm, Puppeteer or Playwright in production.
Install · Quick example · Features · Compatibility · Safety · Cookbook
Install
Create a PAM project and add the library:
pam init my-whatsapp --template raw
cd my-whatsapp
pam composer require pushinbr/pam-whatsapp-web
Chrome or Chromium must be installed on the host. No JavaScript runtime is required. New to PAM? Install it from push-in/pam and run pam doctor first.
Quick example
Create listen.php:
<?php declare(strict_types=1); require __DIR__.'/vendor/autoload.php'; use Pam\WhatsApp\Auth\LocalAuth; use Pam\WhatsApp\Auth\LocalAuthOptions; use Pam\WhatsApp\Client; use Pam\WhatsApp\ClientOptions; use Pam\WhatsApp\Event\MessageReceived; use Pam\WhatsApp\Event\QrCodeReceived; use Pam\WhatsApp\Event\Ready; use Pam\WhatsApp\TerminalQrCode; $client = new Client(new ClientOptions( authStrategy: new LocalAuth(new LocalAuthOptions( clientId: 'main', dataPath: __DIR__.'/.sessions', )), )); $client->onQrCode(static function (QrCodeReceived $event): void { echo "\n\nOpen WhatsApp > Linked devices > Link a device\n\n"; echo TerminalQrCode::render($event->code), "\n"; }); $client->onReady(static function (Ready $event): void { echo "\n\n✓ WhatsApp connected. Waiting for messages...\n"; }); $client->onMessage(static function (MessageReceived $event): void { $message = $event->message; $body = $message->body !== '' ? $message->body : '['.$message->type->name.']'; printf("[%s] %s: %s\n", date('H:i:s', $message->timestamp), $message->from, $body); }); $client->initialize(); $client->run();
Run it with pam listen.php. On the first run, a scannable QR code appears in the terminal. After scanning, the client connects automatically and starts printing inbound messages. Authentication is stored in .sessions/; subsequent runs restore the session without another QR code. Never publish this directory or share its contents.
The complete runnable program is available at
examples/listen.php.
Platform support
| Platform | Support | Notes |
|---|---|---|
| PAM runtime host | Chrome or Chromium installed | Driven through PAM Browser (pushinbr/pam-browser ^1.0) |
| PHP | 8.5+ | ext-mbstring; from composer.json |
| whatsapp-web.js | 1.34.7 | Reference commit 942d236, from api-matrix.json |
What's included
| Area | Features |
|---|---|
| Session | QR, pairing code, LocalAuth, RemoteAuth, reconnection, and session conflict handling |
| Messages | text, replies, editing, deletion, forwarding, reactions, mentions, and read receipts |
| Media | images, audio, video, documents, stickers, thumbnails, and streaming downloads |
| Conversations | contacts, private chats, groups, participants, invites, and membership requests |
| Content | locations, polls, contacts, contact lists, scheduled events, and legacy buttons/lists |
| presence, connection state, calls, channels, labels, business profiles, products, and orders | |
| API | typed objects, integer-backed enums, immutable events, PHPStan level 9, and an automated parity matrix |
Common tasks
Reply to a message
$client->onMessage(static function (MessageReceived $event): void { if ($event->message->body === '!ping') { $event->message->reply('pong 🟢'); } });
Send messages and manage chats
$client->sendMessage('15551234567@c.us', 'Hello directly from PHP!'); $client->sendPresenceAvailable(); $client->archiveChat($chatId); $client->muteChat($chatId, new DateTimeImmutable('+1 hour')); $contact = $client->getNumberId('5511999999999');
For unsaved contacts, pass any human-readable international number. The client normalizes it, verifies that it is registered, resolves its current WhatsApp ID, and sends the message:
use Pam\WhatsApp\RetryOptions; $message = $client->sendMessageToNumber( '+55 (11) 99999-9999', "Hello!\nThis message has two lines.", retry: new RetryOptions(maxAttempts: 3), );
Retries are opt-in because retrying an ambiguous send failure can produce a duplicate. The default is one attempt.
Send media with one call
$client->sendImageToNumber('+55 11 99999-9999', __DIR__.'/photo.jpg', 'Photo caption'); $client->sendAudioToNumber('+55 11 99999-9999', __DIR__.'/voice.ogg'); $client->sendDocumentToNumber('+55 11 99999-9999', __DIR__.'/invoice.pdf'); $client->sendStickerToNumber('+55 11 99999-9999', __DIR__.'/sticker.webp', 'Pack name', 'Author');
Observe delivery
$client->onMessageSent(fn ($event) => printf("Sent: %s\n", $event->message->id)); $client->onMessageDelivered(fn ($event) => printf("Delivered: %s\n", $event->message->id)); $client->onMessageRead(fn ($event) => printf("Read: %s\n", $event->message->id)); $client->onMessageFailed(fn ($event) => printf("Failed: %s\n", $event->message->id));
These convenience events are derived from the typed upstream acknowledgement
event. on(EventType::MessageAcknowledged, ...) remains available unchanged.
Reconnection, diagnostics, and logs
use Pam\WhatsApp\ClientOptions; use Pam\WhatsApp\LogLevel; $client = new Client(new ClientOptions( autoReconnect: true, reconnectMaxAttempts: 5, reconnectDelayMs: 1_000, logger: static function (LogLevel $level, string $message, array $context): void { fwrite(STDERR, sprintf("[%s] %s %s\n", $level->name, $message, json_encode($context))); }, )); $diagnostics = $client->diagnoseSession(); if (!$diagnostics->healthy()) { var_dump($diagnostics); }
Automatic reconnect is disabled by default to preserve existing production
behavior. Logout and QR retry exhaustion are never reconnected automatically.
Client::reconnect() is available after a closed or failed session.
Download large media without exhausting memory
use Pam\WhatsApp\MediaStreamOptions; $media = $message->downloadMediaStream(new MediaStreamOptions( chunkSize: 1024 * 1024, )); if ($media !== null) { foreach ($media->stream as $chunk) { // Persist or forward each binary chunk. } }
Upstream supported-feature status
Every feature currently marked ✅ in the upstream whatsapp-web.js README is
represented by the typed PHP API: Multi Device, send/receive messages, all
listed media formats, stickers, contact cards, locations, replies, group
administration, mentions, chat mute, contact block, contact/profile operations,
status, reactions, polls, and channels.
Buttons and list messages are deprecated and marked ❌ upstream. Their PHP types remain for source compatibility, but successful delivery is not guaranteed. Communities are marked 🔜 upstream and are not advertised as a completed feature here.
A working PHP example for every row of the upstream Supported features table is in the supported-feature cookbook; the machine-readable mapping is supported-features.json.
Compatibility
The current release tracks whatsapp-web.js 1.34.7 at reference commit
942d236a11ad68807308b058303ba5256915979c. Coverage is auditable in
api-matrix.json: 81 symbols + 670 members, 751/751 strict
contracts.
pam composer parity:gate
pam composer test
pam composer analyse
Live certification is split between an unauthenticated QR smoke test and an
authenticated suite with explicit mutation guards. See
CERTIFICATION.md.
Architecture
your PHP code
│
▼
PAM WhatsApp Web ── typed events and objects
│
▼
PAM Browser ─────── Chrome DevTools Protocol
│
▼
Chrome / Chromium ─ WhatsApp Web
Responsible use
This is an unofficial, community-maintained library. WhatsApp may change its internal modules without notice and may restrict or block accounts that violate its terms. Avoid spam and abusive automation. For officially supported integrations and critical workloads, use Meta's WhatsApp Business Platform.
Docs
Part of the PAM ecosystem
PAM runs PHP natively on servers, desktops and phones. This repository is one piece of it.
| PAM | PAM Desktop | PAM Native | Ecosystem |
|---|---|---|---|
| the runtime and CLI | native desktop apps in PHP | native Android and iOS apps in PHP | every official package and plugin |
Docs: push-in.github.io/pam-docs · Issues
Open source under the Apache License 2.0: use, modify and distribute it, including commercially · Made in Brazil by Pushin