Search by

Pure PHP SDK for the go-whatsapp-web-multidevice (GOWA) server

Package info

github.com/Gowa-PHP/sdk

pkg:composer/gowa-php/sdk

Statistics

Installs: 612

Dependents: 2

Suggesters: 0

Stars: 0

Open Issues: 0

v1.7.0 2026-09-25 22:09 UTC

README

gowa-php Banner

gowa-php

Pure PHP SDK for GOWA (go-whatsapp-web-multidevice) & WhatsApp Web, powered by whatsmeow

Latest Version Total Downloads Plumb score License PHP Version

🇧🇷 Para ler a documentação em Português, acesse README.pt.md.

⚡ Acknowledgments & Dependencies

This SDK interacts with the Go backend ecosystem created by the open-source community:

Installation

composer require gowa-php/sdk

Requirements

Usage Example

1. Initialize Configuration and Client

use Gowa\Sdk\Config;
use Gowa\Sdk\GowaClient;

$config = new Config(
    baseUrl: 'https://gowa.yourcompany.com',
    username: 'admin',
    password: 'secretpassword',
    timeout: 15
);

$client = new GowaClient($config);

// Or inject a custom Guzzle handler (e.g. for testing or Laravel Http::fake() HandlerStack) without losing Config options:
// $client = new GowaClient($config, handler: $customHandler);

2. Device Management & Pairing

// Register device and webhook
$device = $client->createDevice(
    deviceId: 'my-instance-uuid',
    webhookUrl: 'https://myapi.com/webhooks/gowa/my-instance-uuid',
    webhookSecret: 'my_hmac_secret_48_chars',
    events: ['message', 'message.ack', 'message.reaction', 'message.edited', 'message.revoked']
);

// Start pairing via QR Code
$pairing = $client->startQrPairing('my-instance-uuid');
echo $pairing->qrLink; // QR Code URL

// Or request 8-digit code for manual typing on phone
$codePairing = $client->startCodePairing('my-instance-uuid', '5511999998888');
echo $codePairing->pairCode; // e.g. ABCD-1234

// List all registered devices on the server
$devices = $client->devices();

// Reconnect or permanently delete/purge a device slot
$client->reconnectDevice('my-instance-uuid');
$client->deleteDevice('my-instance-uuid');

// Check if a number is registered on WhatsApp before messaging
if ($client->checkUser('my-instance-uuid', '5511999998888')) {
    echo "Phone is on WhatsApp!";
}

3. Sending Messages and Media

Text, Links, Polls & Mentions

// Send text with mentions (@everyone or phone numbers) and disappearing messages duration (24h = 86400s)
$client->sendText(
    deviceId: 'my-instance-uuid',
    to: '120363xxxx@g.us',
    text: 'Hello @everyone and @5511999998888!',
    mentions: ['5511999998888', '@everyone'],
    duration: 86400,
);

// Send URL link with preview
$client->sendLink('my-instance-uuid', '5511999998888', 'https://fazz.ai', 'Check our website');

// Send interactive poll
$client->sendPoll('my-instance-uuid', '5511999998888', 'What is your preferred time?', ['Morning', 'Afternoon', 'Evening']);

Media Uploads (Files, URLs, Mentions & View Once)

use Gowa\Sdk\Dto\MediaType;
use Gowa\Sdk\Dto\MediaUpload;
use Gowa\Sdk\Dto\MediaPayload;

// Send voice note (PTT) from external URL
$upload = MediaUpload::fromUrl('https://mycompany.com/storage/voicenote.m4a');
$media = new MediaPayload(type: MediaType::Audio, upload: $upload, voice: true);
$client->sendMedia('my-instance-uuid', '5511999998888', $media);

// Send local image with caption mentions and view-once enabled
$imgUpload = MediaUpload::fromPath('/path/to/invoice.jpg');
$imgMedia = new MediaPayload(
    type: MediaType::Image,
    upload: $imgUpload,
    caption: 'Invoice for @5511999998888',
    mentions: ['5511999998888'],
    viewOnce: true,
);
$client->sendMedia('my-instance-uuid', '5511999998888', $imgMedia);

Scheduled & Recurring Message Sends (GOWA v9.5+)

use Gowa\Sdk\Dto\ScheduleOptions;

// Schedule a message for future or recurring delivery
$schedule = new ScheduleOptions(
    scheduledAt: '2026-10-01T09:00:00Z',
    timezone: 'America/Sao_Paulo',
    recurrence: 'weekly',
    weekdays: [1, 3, 5], // Monday, Wednesday, Friday
    endAt: '2026-12-31T23:59:59Z',
    occurrenceLimit: 20,
);

$sent = $client->sendText(
    deviceId: 'my-instance-uuid',
    to: '5511999998888',
    text: 'Weekly reminder!',
    schedule: $schedule,
);

echo $sent->scheduleId; // e.g. '0b5c3a8e-7f5d-4d6f-9d1e-2f8c1a7b9e10'

// Manage schedules
$schedules = $client->listSchedules('my-instance-uuid');
$client->pauseSchedule('my-instance-uuid', $sent->scheduleId);
$client->resumeSchedule('my-instance-uuid', $sent->scheduleId);
$client->cancelSchedule('my-instance-uuid', $sent->scheduleId);

Message Actions (Forward, Edit, Revoke, Reactions, Star & Chat History)

// Forward message (also supports scheduling via ScheduleOptions)
$client->forwardMessage('my-instance-uuid', '5511999998888', 'WAMID_ORIGINAL_123');

// Edit sent text
$client->editMessage('my-instance-uuid', '5511999998888', 'WAMID_ORIGINAL_123', 'Updated message text');

// Send emoji reaction
$client->sendReaction('my-instance-uuid', '5511999998888', 'WAMID_ORIGINAL_123', '👍');

// Revoke (Delete for everyone)
$client->revokeMessage('my-instance-uuid', '5511999998888', 'WAMID_ORIGINAL_123');

// Star or unstar message
$client->starMessage('my-instance-uuid', '5511999998888', 'WAMID_ORIGINAL_123', true);

// Request older chat history from the phone on-demand (e.g. 50 messages)
$client->requestChatHistory('my-instance-uuid', '5511999998888', count: 50);

Media Download (Echo Candidate Phones, Timeout, and Cleanup)

use Gowa\Sdk\Exceptions\MediaUnavailableException;

// 1. Query media with candidate phones (e.g. echo outbound vs inbound recipient)
try {
    $remoteMedia = $client->describeMedia(
        deviceId: 'my-instance-uuid',
        phones: ['5511888888888', '5511999999999'], // tries candidate phones in order
        providerMessageId: 'WAMID_ORIGINAL_123'
    );
} catch (MediaUnavailableException $e) {
    // Media was permanently refused (e.g. text-only message or expired)
    $remoteMedia = null;
}

// 2. Download decrypted media with custom timeout (default is Config timeout)
if ($remoteMedia !== null) {
    $client->downloadMedia(
        mediaUrl: $remoteMedia->url,
        destinationPath: '/path/to/downloaded.mp4',
        timeout: 120 // dedicated timeout for large media downloads
    );
}

4. Webhook Verification & Event Parsing

use Gowa\Sdk\Dto\EventPayload;
use Gowa\Sdk\Dto\LiveLocationPayload;
use Gowa\Sdk\Dto\PollPayload;
use Gowa\Sdk\Security\WebhookSignature;
use Gowa\Sdk\Webhook\Dto\IncomingAck;
use Gowa\Sdk\Webhook\Dto\IncomingMessage;
use Gowa\Sdk\Webhook\Event;
use Gowa\Sdk\Webhook\WebhookParser;

$payload = file_get_contents('php://input');
// GOWA sends the signature header formatted as "sha256=<hex>"
$signature = $_SERVER['HTTP_X_HUB_SIGNATURE_256'] ?? '';
$secret = 'my_hmac_secret_48_chars';

// 1. Verify HMAC SHA-256 signature (requires the "sha256=" prefix)
if (!WebhookSignature::verify($payload, $signature, $secret)) {
    http_response_code(401);
    exit('Invalid signature');
}

// 2. Parse incoming webhook payload with fluent routing
WebhookParser::parse($payload)
    ->onMessage(function (IncomingMessage $msg) {
        $msg
            ->whenLiveLocation(function (LiveLocationPayload $loc) {
                echo "Coordinates: {$loc->latitude}, {$loc->longitude}\n";
            })
            ->whenPoll(function (PollPayload $poll) {
                echo "Poll question: {$poll->question}\n";
            })
            ->whenEvent(function (EventPayload $event) {
                echo "Event title: {$event->name}\n";
            })
            ->whenText(function (string $text) {
                echo "Text: {$text}\n";
            })
            ->otherwise(function (IncomingMessage $msg) {
                echo "Other message type: {$msg->type}\n";
            });
    })
    ->onAck(function (IncomingAck $ack) {
        echo "Receipt: {$ack->receiptType}\n";
    })
    ->otherwise(function (mixed $data, Event $event) {
        echo "Unhandled or unknown event: {$event->value}\n";
    });

// Traditional array access and helper methods remain fully supported:
// $event = WebhookParser::parse($payload);
// if ($event->isMessage()) { $msg = $event->message(); ... }
// or $event['event'] === Event::Message

Available Features Summary

Device Management

Feature Method Endpoint
Register Device & Webhook createDevice() POST /devices
List Registered Devices devices(), listDevices() GET /devices
Update Webhook Config updateWebhook() PATCH /devices/:id/webhook
Start QR Pairing startQrPairing() GET /devices/:id/login
Start 8-Digit Code Pairing startCodePairing() POST /devices/:id/login/code
Query Device Info & Status device() GET /devices/:id
Reconnect Device reconnectDevice() POST /devices/:id/reconnect
Logout Device (Keep Slot) logout() POST /devices/:id/logout
Purge / Delete Device Slot deleteDevice() DELETE /devices/:id

Messages & Interactions

Feature Method Endpoint
Text Message (Mentions, Duration, Schedule) sendText() POST /send/message
Image (Caption Mentions, View-Once, Schedule) sendMedia() POST /send/image
Video (Caption Mentions, View-Once, Schedule) sendMedia() POST /send/video
Audio / PTT Voice Note (Schedule) sendMedia() (voice: true) POST /send/audio
Document / File (Caption Mentions, Schedule) sendMedia() POST /send/file
WebP Sticker sendSticker() POST /send/sticker
Location sendLocation() POST /send/location
Contact Card sendContacts() POST /send/contact
URL Link Preview sendLink() POST /send/link
Interactive Poll sendPoll() POST /send/poll
Emoji Reaction sendReaction() POST /message/:id/reaction
Forward Message (Schedule) forwardMessage() POST /message/:id/forward
Edit Message editMessage() POST /message/:id/update
Revoke Message (Delete for All) revokeMessage() POST /message/:id/revoke
Delete Message (Local) deleteMessage() POST /message/:id/delete
Star / Unstar Message starMessage() POST /message/:id/star, POST /message/:id/unstar
Mark Audio Played markPlayed() POST /message/:id/played
Mark Read / Typing markRead() POST /message/:id/read
On-Demand Chat History requestChatHistory() POST /chat/:jid/history

Scheduled & Recurring Sends (GOWA v9.5+)

Feature Method Endpoint
List Scheduled Sends listSchedules() GET /send/schedules
Get Scheduled Send getSchedule() GET /send/schedules/:id
Pause Scheduled Send pauseSchedule() POST /send/schedules/:id/pause
Resume Scheduled Send resumeSchedule() POST /send/schedules/:id/resume
Cancel Scheduled Send cancelSchedule() POST /send/schedules/:id/cancel

Contacts, Presence & Media Download

Feature Method Endpoint
Check User on WhatsApp checkUser() GET /user/check
Contact Profile Picture avatar() GET /user/avatar
Prepare Media Download describeMedia() GET /message/:id/download
Download Decrypted Media downloadMedia() GET media URL

Error Handling

The SDK provides structured exceptions to clearly differentiate between network/transport unreachability, permanent media refusals, and server validation/refusal responses:

use Gowa\Sdk\Exceptions\GowaRequestException;
use Gowa\Sdk\Exceptions\GowaUnreachableException;
use Gowa\Sdk\Exceptions\MediaUnavailableException;

try {
    $client->sendText('my-device-id', '5511999998888', 'Hello');
} catch (GowaUnreachableException $e) {
    // Network failure, connection timeout, DNS failure, or server unreachable
    // Reconcile delivery status before retrying non-idempotent operations (such as sending messages)
} catch (MediaUnavailableException $e) {
    // Permanent media download refusal (e.g. message does not contain media or unsupported format)
} catch (GowaRequestException $e) {
    // Server responded with an error (4xx/5xx or code != SUCCESS)
    $status = $e->statusCode;    // e.g. 400
    $code = $e->gowaCode;        // e.g. "VALIDATION_ERROR"
    $message = $e->gowaMessage;  // e.g. "your audio type is not allowed..."
}

🔒 Security & Multi-Tenancy Considerations

Tenant Isolation & Device Scoping

  • GOWA does not isolate tenants at the API level: A single Basic Auth credential provides access to all paired devices on the server.
  • deviceId scoping: Every device-specific operation must be explicitly scoped by $deviceId. An invalid, empty, or missing deviceId would cause requests to execute on whichever device the server chooses.
  • Validation: All methods accepting $deviceId strictly reject empty or whitespace-only strings with InvalidArgumentException before any network request is issued.
  • Caller responsibility: The $deviceId must always originate from trusted server storage (e.g. your database model) and never directly from unvalidated user input or request parameters.
  • Un-scoped endpoints: Broad reading endpoints (/chats, /user/my/contacts, /user/my/groups) are not scoped per device on the GOWA server and return mixed data across all paired numbers. Avoid relying on them for tenant-isolated data.

Anti-SSRF Validation & Redirect Protection

Any media or QR image URL fetched through downloadMedia() or fetchQrImage() is strictly validated via GowaHost::assertBelongsToServer() to ensure requests only target the configured GOWA server. Furthermore, automatic HTTP redirects are explicitly disabled (allow_redirects: false) to prevent unvalidated redirects from escaping the configured host, protecting against SSRF attacks and credential leaks.

Running Tests (Pest PHP)

vendor/bin/pest

⚠️ Disclaimer & Terms of Use

This software is an open-source library created for educational, research, and testing laboratory purposes.

  • Third-Party Terms of Service: Users of this library are solely responsible for complying with WhatsApp's Terms of Service, Meta's Platform Policies, and the terms of any third-party services utilized.
  • Automated Messaging & Policy Compliance: Automated or unauthorized messaging may violate platform terms. Users must ensure strict compliance with applicable privacy laws (e.g., GDPR, LGPD), user consent requirements, and platform guidelines.
  • No Warranty & Liability: This software is provided "as is", without warranty of any kind, express or implied. The authors and contributors assume no liability for any account bans, data loss, service interruptions, or misuse of this library.

Contributing

Please see CONTRIBUTING.md and CODE_OF_CONDUCT.md for details.

License

This package is open-source software licensed under the MIT License.