Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Whatsapi Laravel Package

xaamin/whatsapi

xaamin/whatsapi is a Laravel-friendly wrapper for the WhatsApp Cloud API, making it easy to send text, media, template and interactive messages, verify webhooks, and manage WhatsApp Business resources.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Install the package with Composer:

    composer require xaamin/whatsapi
    

    Pull in a PSR-18 HTTP client and a logger if your app doesn't already provide one (guzzlehttp/guzzle, psr/log).

  2. Read the README first, then the tests. The package is a thin PHP client around a Node-based WhatsApp Web bridge, so the two things you actually need to learn are (a) how the PHP side is configured (base URL / port / token of the bridge) and (b) how the bridge is launched and authenticated. The tests/ directory and any examples/ folder show the real method signatures faster than the docs.

  3. Stand up the bridge. The wrapper does not speak the WhatsApp protocol itself — you run the bundled Node service (check the package's bin/ or server.js script for the exact entrypoint), it prints a QR code, and you scan it from WhatsApp on a phone dedicated to your app (never your personal number). Persist the resulting session folder to a durable path (absolute path in config) so restarts don't force a re-scan.

  4. Wire it into Laravel. Create config/whatsapi.php (or publish the package config), bind the client as a singleton in a service provider, and expose it via a small facade or a WhatsAppService wrapper you control. Keep the package's raw class out of your controllers.

  5. First use case — send a message, in a queued job, not a controller:

    public function handle(WhatsAppService $wa): void
    {
        $wa->sendText($this->to, $this->body);
    }
    

    Dispatch it, confirm delivery on the phone, then build out from there (media, templates, inbound handling).

Implementation Patterns

Wrap, don't sprinkle. Never call xaamin\whatsapi\* classes directly from controllers or jobs. Create an interface (WhatsAppGateway) with a concrete implementation that adapts the package, so you can swap in Twilio/Meta Cloud API later and fake it in tests. Bind it in a WhatsAppServiceProvider.

Always send from queues. WhatsApp Web-style bridges are slow and rate-sensitive. A SendWhatsAppMessage job with $tries = 3, backoff, and a rateLimiter middleware gives you retries on bridge hiccups and keeps HTTP requests fast. Group by recipient to avoid self-throttling.

Separate outbound and inbound paths.

  • Outbound: your app → PHP wrapper → bridge → WhatsApp. Model this as jobs + events (WhatsAppMessageSent, WhatsAppMessageFailed).
  • Inbound: the bridge pushes events to a webhook endpoint you define. Register a route (e.g. POST /webhooks/whatsapp), verify it's from the bridge (shared secret header, IP allowlist, or HMAC — check what the bridge supports), then dispatch a domain event like InboundWhatsAppMessage for listeners to route to conversations, tickets, notifications, etc.

Treat the session as infrastructure. One WhatsApp session = one identity = one bridge process. Put the session directory on a persistent volume, back it up, and run the bridge under Supervisor/systemd with auto-restart. In multi-tenant apps, spin up a bridge + session per tenant, keyed by tenant ID, and manage them via an artisan command or process manager — a single session cannot serve two WhatsApp numbers.

Test with a fake gateway. Implement a FakeWhatsAppGateway that records sent messages for assertions, and bind it in tests/. Never hit the real bridge in CI. For inbound webhook tests, Http::fake() isn't relevant — use $this->postJson() with a signed payload against your controller and assert events were dispatched.

Observability from day one. Wrap every send in a log line (to, message id, duration, status) and track success/failure metrics. Message IDs returned by the bridge are your correlation key into WhatsApp-side debugging.

Compose with notifications. A WhatsAppChannel for Illuminate\Notifications gives you a clean interface: via() returns ['whatsapp'], and the channel renders a templated message and pushes it onto the queue. This is the highest-leverage integration point in a Laravel app.

Gotchas and Tips

  • Session loss is the #1 failure mode. Ephemeral filesystems (Docker without a volume, Forge deploys that wipe storage/) mean a QR re-scan on every deploy. Pin the session path in config to a persistent, backed-up location, and make the bridge's startup emit a clear log line if no existing session was found.

  • One process, one session. Don't try to horizontally scale the bridge behind a load balancer or run two instances against the same session folder — you will corrupt the session or get disconnected. Scale outbound throughput via your queue, not by adding bridge replicas.

  • You are on WhatsApp Web's terms, not the official API. Unofficial wrappers can get numbers banned, especially with bulk sending, cold contacts, or messages that look automated. For customer-facing production traffic, budget for migrating to the official Cloud API; keep this wrapper for internal notifications, ops alerts, or low-volume use cases where the ban risk is acceptable and the number is disposable.

  • Rate-limit aggressively and humanize. Add jitter between messages, cap messages-per-minute per number in config, and avoid sending to numbers that haven't messaged you first. A RateLimiter::for('whatsapp') middleware on your job is the easiest guardrail.

  • Media is the common source of "message sent but not delivered." Check whether the bridge expects a local path, a public URL, or base64 for images/documents/audio — this differs from the text path and is usually where first-time integrations break. If using URLs, they must be publicly reachable from the bridge host, not just from your app.

  • Config quirks: the base URL/port of the bridge is often hardcoded in examples. Move it to .env (WHATSAPP_BRIDGE_URL, WHATSAPP_BRIDGE_TOKEN), and ensure the client's timeout is longer than your queue's job timeout, otherwise you'll get zombie jobs.

  • Debugging workflow: when a message silently fails, (1) curl the bridge's health/status endpoint directly, (2) tail the bridge's Node stdout — the PHP exception is often just "500", while the real reason ("not connected", "invalid wid", "media too large") lives in Node logs, (3) inspect the raw request/response by enabling the wrapper's HTTP client logging. Reproduce with a single script outside the queue before blaming the queue.

  • Extension points: the package is thin, so don't patch vendor code. Subclass or decorate the client (composition), and if you need retries, reconnection, or event normalization, do it in your WhatsAppServiceProvider binding — a RetryGateway decorator around the real client is the cleanest place. Keep your own WhatsAppMessage value object so payload shapes from the wrapper never leak into domain code.

  • Pin the version. These wrappers track an unstable external protocol; minor bumps have changed method names and payload shapes. Commit the lockfile and add a smoke test that sends to a test number on deploy, gated behind an env flag.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
unabomber/timezone
delatbabel/contacts
astritzeqiri/g-recaptcha
kevinongko/laravel-genode
developeruz/laravel-analytics
robbiep/afterthedeadline
frankkessler/incontact-laravel-oauth2-rest
jlxh/laravel-userlog
davidmgilo/timetables
qylinfly/role-permission
reflect-digital/conduit
takashato/laravel-vietqr
aitor24/laralang
kraenzle-ritter/entity-relations
dindigital/din-image
pomirleanu/image-colors
sawmainek/laravel-api-generator
delatbabel/jsonresponses
mouhamedfd/permissions-generator
eduplus/qr