amsaid/larawhat

A Laravel package for the WhatsApp Cloud API — send messages, manage templates, handle webhooks, and more.

Maintainers

Package info

github.com/amsaid/larawhat

pkg:composer/amsaid/larawhat

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

dev-master 2026-04-24 10:15 UTC

This package is auto-updated.

Last update: 2026-04-24 10:17:32 UTC


README

Latest Version PHP Version Laravel

A Laravel package for the WhatsApp Cloud API (Meta). Send messages, manage templates, handle incoming webhooks, and control your business profile — all with a clean, fluent Laravel interface.

Requirements

  • PHP 8.2+
  • Laravel 11.x, 12.x, or 13.x

Installation

composer require amsaid/larawhat

Publish the configuration file:

php artisan vendor:publish --tag=larawhat-config

Configuration

Set these environment variables in your .env file:

WHATSAPP_ACCESS_TOKEN=EAAT...
WHATSAPP_PHONE_NUMBER_ID=123456789
WHATSAPP_BUSINESS_ACCOUNT_ID=987654321
WHATSAPP_WEBHOOK_VERIFY_TOKEN=my-verify-token
WHATSAPP_API_VERSION=v22.0
Variable Description Required
WHATSAPP_ACCESS_TOKEN Permanent or temporary access token from Meta Developer Console
WHATSAPP_PHONE_NUMBER_ID ID of the phone number sending messages
WHATSAPP_BUSINESS_ACCOUNT_ID WABA ID (needed for templates & phone number management) For some endpoints
WHATSAPP_WEBHOOK_VERIFY_TOKEN Token to verify your webhook in Meta Console For webhooks
WHATSAPP_API_VERSION API version (defaults to v22.0)
WHATSAPP_WEBHOOK_PATH Webhook URI path (defaults to /api/whatsapp/webhook)

Webhook setup

In the Meta Developer Console, configure your webhook callback URL:

https://your-app.com/api/whatsapp/webhook

Use the same WHATSAPP_WEBHOOK_VERIFY_TOKEN value. The route is auto-registered and handles both the GET verification challenge and POST event delivery.

Usage

Facade

use Larawhat\Facades\Larawhat;

Send a text message

Larawhat::sendText('+1234567890', 'Hello from Laravel!');

With URL preview:

Larawhat::sendText('+1234567890', 'Check this out: https://example.com', previewUrl: true);

Using message objects

use Larawhat\Messages\TextMessage;

$message = new TextMessage('Hello via message object!');
$response = Larawhat::sendMessage($message, '+1234567890');

Send media

use Larawhat\Messages\MediaMessage;

// By media ID (upload first)
Larawhat::sendMessage(
    MediaMessage::fromId('media-id-here', 'image', 'Nice photo!'),
    '+1234567890',
);

// By URL
Larawhat::sendMessage(
    MediaMessage::fromUrl('https://example.com/photo.jpg', 'image', 'Check this out'),
    '+1234567890',
);

Send a template

use Larawhat\Messages\TemplateMessage;

$message = (new TemplateMessage('hello_world', 'en_US'))
    ->addBodyParameter('John');

Larawhat::sendMessage($message, '+1234567890');

Interactive messages

Buttons:

use Larawhat\Messages\InteractiveMessage;

$message = InteractiveMessage::buttons(
    body: 'Would you like to proceed?',
    buttons: [
        ['id' => 'yes', 'title' => '✅ Yes'],
        ['id' => 'no', 'title' => '❌ No'],
    ],
    header: 'Confirm Action',
    footer: 'Powered by Larawhat',
);

Larawhat::sendMessage($message, '+1234567890');

List:

$message = InteractiveMessage::list(
    body: 'Select an option:',
    buttonText: 'View Options',
    sections: [
        [
            'title' => 'Categories',
            'rows' => [
                ['id' => 'support', 'title' => 'Support', 'description' => 'Get help'],
                ['id' => 'sales', 'title' => 'Sales', 'description' => 'Talk to sales'],
            ],
        ],
    ],
    header: 'Menu',
    footer: 'Choose wisely',
);

Larawhat::sendMessage($message, '+1234567890');

Send location

use Larawhat\Messages\LocationMessage;

$message = new LocationMessage(48.8566, 2.3522, 'Paris', 'France');
Larawhat::sendMessage($message, '+1234567890');

Send contact

use Larawhat\Messages\ContactsMessage;

$message = (new ContactsMessage)
    ->addPerson('John Doe', '+11234567890', '1234567890');

Larawhat::sendMessage($message, '+1234567890');

Mark message as read

Larawhat::markAsRead('wamid.message-id-here');

Media management

// Upload a file
$response = Larawhat::uploadMedia(storage_path('app/photos/photo.jpg'), 'image/jpeg');
$mediaId = $response->json('id');

// Get media metadata
$media = Larawhat::getMedia($mediaId);

// Download media
$file = Larawhat::downloadMedia($mediaId);
Storage::put('downloads/photo.jpg', $file->body());

// Delete media
Larawhat::deleteMedia($mediaId);

Business profile

// Get profile
$profile = Larawhat::businessProfile();

// Update profile
Larawhat::updateBusinessProfile([
    'about' => 'We are open 9-5.',
    'description' => 'Your friendly neighbourhood business.',
    'websites' => ['https://example.com'],
]);

Phone numbers

// List all phone numbers
$numbers = Larawhat::phoneNumbers();

// Get specific phone number
$number = Larawhat::phoneNumber('123456789');

Message templates

// List templates
$templates = Larawhat::templates();

// Create a template
Larawhat::createTemplate([
    'name' => 'order_confirmation',
    'language' => 'en_US',
    'category' => 'UTILITY',
    'components' => [
        [
            'type' => 'BODY',
            'text' => 'Your order {{1}} has been confirmed!',
        ],
    ],
]);

// Delete a template
Larawhat::deleteTemplate('order_confirmation');

Webhook Handling

Built-in route (auto-registered)

The package registers a GET & POST route at /api/whatsapp/webhook (configurable via WHATSAPP_WEBHOOK_PATH).

  • GET — handles Meta's verification challenge automatically.
  • POST — receives incoming events. The route always returns 200 {"status": "handled"}.

Custom callback handling

Use the Larawhat::webhook() handler to register callbacks. Add this to your AppServiceProvider::boot() or a dedicated service provider:

use Larawhat\Facades\Larawhat;

// Must reference the same instance — we use resolve() to avoid boot order issues
$this->app->booted(function () {
    Larawhat::webhook()
        ->onMessage(function ($webhook) {
            // Handle incoming message
            Log::info('Message from: '.$webhook->from());
            Log::info('Text: '.$webhook->textBody());

            // Auto-reply
            Larawhat::sendText($webhook->from(), 'Thanks for your message!');
        })
        ->onStatus(function ($webhook) {
            foreach ($webhook->statuses() as $status) {
                Log::info("Message {$status['id']} status: {$status['status']}");
            }
        })
        ->onAny(function ($webhook) {
            Log::debug('Webhook received', $webhook->payload());
        });
});

The WebhookRequest API

$webhook->isValid();          // Check if payload is well-formed
$webhook->from();             // Sender's phone number
$webhook->messageType();      // 'text', 'image', 'interactive', etc.
$webhook->textBody();         // Text content (if text message)
$webhook->messages();         // Array of all messages
$webhook->statuses();         // Array of status updates
$webhook->interactiveReply(); // Button/list reply data
$webhook->phoneNumberId();    // Phone number that received the message
$webhook->payload();          // Raw payload array

Response handling

All API methods that contact Meta return an Illuminate\Http\Client\Response instance. Check success:

$response = Larawhat::sendText('+1234567890', 'Hello!');

if ($response->successful()) {
    $wamId = $response->json('messages')[0]['id'] ?? null;
}

Errors are thrown as exceptions:

  • Larawhat\Exceptions\AuthenticationException — 401/403
  • Larawhat\Exceptions\RateLimitException — 429
  • Larawhat\Exceptions\LarawhatException — other errors

Testing

composer test

License

The MIT License (MIT). See LICENSE for details.