michotechnologies/michowapi-laravel

Laravel SDK for MichoWAPI - WhatsApp messaging gateway by Micho Technologies

Maintainers

Package info

github.com/namz182/michowapi-laravel

pkg:composer/michotechnologies/michowapi-laravel

Statistics

Installs: 5

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.0 2026-04-13 01:04 UTC

This package is auto-updated.

Last update: 2026-04-13 01:22:20 UTC


README

Latest Version on Packagist License PHP Version

Official Laravel SDK for MichoWAPI — the WhatsApp messaging gateway by Micho Technologies.

Send text messages, upload media files, retrieve message history, and handle webhooks — all from your Laravel application using a clean, fluent API.

Requirements

  • PHP 8.1+
  • Laravel 10, 11, or 12

Installation

composer require michotechnologies/michowapi-laravel

Laravel auto-discovers the service provider and WApi facade automatically. No manual registration needed.

Publish the config file:

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

Configuration

Add the following to your .env file:

MICHOWAPI_URL=https://wapi.michotech.me
MICHOWAPI_KEY=mw_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
MICHOWAPI_TIMEOUT=30
MICHOWAPI_MEDIA_TIMEOUT=120
MICHOWAPI_WEBHOOK_SECRET=your-webhook-secret
Variable Description Default
MICHOWAPI_URL Base URL of the MichoWAPI gateway https://wapi.michotech.me
MICHOWAPI_KEY Your API key (format: mw_ + 40 chars)
MICHOWAPI_TIMEOUT HTTP timeout in seconds for text requests 30
MICHOWAPI_MEDIA_TIMEOUT Timeout for media file uploads 120
MICHOWAPI_WEBHOOK_SECRET HMAC secret for verifying webhook payloads

Note: Each API key is bound to one WhatsApp session automatically — you do not pass a session ID in requests. To use multiple WhatsApp numbers, generate a separate key per session and instantiate the client separately for each.

Getting Started

1. Create a Session

Log into the MichoWAPI dashboard, go to Sessions, click New Session, and scan the QR code with your phone via WhatsApp > Linked Devices.

2. Generate an API Key

Go to API Keys, click Generate API Key, select the session, and copy the key. It will not be shown again.

3. Send Your First Message

use MichoTech\WApi\Facades\WApi;

WApi::messages()->sendText('260971000000', 'Hello from MichoWAPI!');

Usage

Via Facade

use MichoTech\WApi\Facades\WApi;

WApi::messages()->sendText('260971000000', 'Hello!');

Via Dependency Injection

use MichoTech\WApi\MichoWapi;

class NotificationController extends Controller
{
    public function __construct(protected MichoWapi $wapi) {}

    public function send(): void
    {
        $this->wapi->messages()->sendText('260971000000', 'Hello!');
    }
}

Messages

Send Text

WApi::messages()->sendText('260971000000', 'Your exam results are ready.');

Phone numbers must include the country code and contain digits only (e.g. 260971000000, not +260 971 000 000).

Send Media

Send any file (image, video, audio, PDF, etc.) using sendMedia(). The file is uploaded directly — no public URL required.

// From a file path
WApi::messages()->sendMedia('260971000000', '/path/to/photo.jpg', caption: 'School photo');

// From an SplFileInfo (e.g. from a Laravel uploaded file)
$file = $request->file('attachment'); // UploadedFile extends SplFileInfo
WApi::messages()->sendMedia('260971000000', $file, caption: 'Your invoice');

// From an open stream
$stream = fopen('/path/to/audio.mp3', 'r');
WApi::messages()->sendMedia('260971000000', $stream, filename: 'voice-note.mp3');

Parameters:

Parameter Type Required Description
$to string Yes Recipient phone number
$file string / SplFileInfo / resource Yes File path, SplFileInfo, or stream
$filename string|null No Override the filename shown to recipient
$caption string|null No Caption shown below the media
$sendAsDocument bool No Force the file to be sent as a document

Send as Document

Use sendDocument() as a shortcut to always send as a document attachment:

WApi::messages()->sendDocument(
    '260971000000',
    '/path/to/report.pdf',
    filename: 'Term 2 Report Card.pdf',
    caption: 'Please find your results attached.',
);

Send Bulk

Send the same text to multiple recipients. Returns results keyed by phone number.

$results = WApi::messages()->sendBulk(
    ['260971111111', '260972222222', '260973333333'],
    'School fees for Term 3 are due. Log in to MichoSMS to view your balance.'
);

foreach ($results as $phone => $result) {
    $messageId = $result['data']['message_id'];
}

Message History

List Messages

// All messages (paginated)
$messages = WApi::messages()->list();

// Filtered
$messages = WApi::messages()->list([
    'from_me' => true,                 // outbound only
    'type'    => 'image',              // chat | image | video | audio | document
    'chat_id' => '260971000000@c.us',  // specific contact
    'page'    => 2,
]);

Get a Single Message

$message = WApi::messages()->find('3EB0A0B4F3...');

// $message['data']['status'] can be:
// queued | sent | delivered | read | failed

Received Messages

// Only inbound messages
$received = WApi::messages()->received(page: 1);

Sync Messages

Pull in messages that arrived while the session was offline:

WApi::messages()->sync();

Messages in a Specific Chat

$chatMessages = WApi::messages()->forChat('260971000000@c.us', page: 1);

Webhooks

Configure

Set your webhook URL in the MichoWAPI dashboard under your session settings.

Register a route:

// routes/api.php
Route::post('/webhooks/whatsapp', [WhatsAppWebhookController::class, 'handle']);

Exclude from CSRF in bootstrap/app.php (Laravel 11+):

->withMiddleware(function (Middleware $middleware) {
    $middleware->validateCsrfTokens(except: ['webhooks/*']);
})

Handle Webhook Events

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use MichoTech\WApi\Facades\WApi;

class WhatsAppWebhookController extends Controller
{
    public function handle(Request $request)
    {
        // Verify the payload signature
        if (! WApi::webhooks()->verify($request)) {
            abort(403, 'Invalid webhook signature.');
        }

        $event = WApi::webhooks()->parse($request);

        match ($event['event']) {
            'message.received'    => $this->onMessageReceived($event['data']),
            'message.sent'        => $this->onMessageSent($event['data']),
            'session.connected'   => $this->onConnected($event['session_id']),
            'session.disconnected'=> $this->onDisconnected($event['session_id']),
            default               => null,
        };

        return response()->json(['status' => 'ok']);
    }

    protected function onMessageReceived(array $data): void
    {
        // $data['from']    — sender's number
        // $data['message'] — message text
        // $data['type']    — text, image, audio, document, etc.
    }
}

Webhook Event Types

Event Description
message.received Incoming message from a contact
message.sent Outgoing message confirmed sent by WhatsApp
session.connected Session successfully authenticated
session.disconnected Session lost connection

Webhook Payload Shape

{
  "event": "message.received",
  "session_id": 1,
  "timestamp": "2026-03-08T10:30:00Z",
  "data": {
    "from": "260971000000",
    "message": "Hi there!",
    "type": "text"
  }
}

Failed deliveries are retried up to 3 times with exponential backoff (1 min, 5 min, 15 min).

Error Handling

The SDK throws specific, typed exceptions for every error scenario:

use MichoTech\WApi\Facades\WApi;
use MichoTech\WApi\Exceptions\AuthenticationException;
use MichoTech\WApi\Exceptions\SessionNotConnectedException;
use MichoTech\WApi\Exceptions\RateLimitException;
use MichoTech\WApi\Exceptions\ValidationException;
use MichoTech\WApi\Exceptions\MichoWapiException;

try {
    WApi::messages()->sendText('260971000000', 'Hello!');

} catch (SessionNotConnectedException $e) {
    // WhatsApp session is disconnected — user needs to reconnect from dashboard
    Log::warning('WhatsApp session offline: ' . $e->getMessage());

} catch (RateLimitException $e) {
    // Too many requests — back off and retry
    Log::warning('MichoWAPI rate limit hit.');

} catch (ValidationException $e) {
    // Invalid request parameters
    Log::error('Validation failed: ' . $e->getMessage());
    Log::error('Fields: ' . json_encode($e->errors()));
    // Get errors for a specific field:
    $toErrors = $e->errorsFor('to');

} catch (AuthenticationException $e) {
    // Bad API key, revoked key, or key not linked to a session
    Log::error('Auth error: ' . $e->getMessage());

} catch (MichoWapiException $e) {
    // Any other API or network error
    Log::error('WApi error: ' . $e->getMessage());
}

Exception Reference

Exception HTTP Status Error Code(s) Description
SessionNotConnectedException 422 SESSION_NOT_CONNECTED Session not active
RateLimitException 429 RATE_LIMITED Too many requests
AuthenticationException 401, 403 UNAUTHORIZED, INVALID_API_KEY, NO_SESSION Auth failure
ValidationException 422 VALIDATION_ERROR Invalid request params
MichoWapiException any any Base class for all SDK errors

Rate Limits

Plan Limit
Free 60 requests/minute
Pro 300 requests/minute
Enterprise Custom

Real-World Examples

MichoSMS: Notify Parents of Exam Results

use MichoTech\WApi\Facades\WApi;
use MichoTech\WApi\Exceptions\SessionNotConnectedException;

class SendResultsNotifications implements ShouldQueue
{
    public function handle(): void
    {
        Student::with('guardian', 'results')
            ->whereHas('results')
            ->chunk(50, function ($students) {
                foreach ($students as $student) {
                    try {
                        WApi::messages()->sendText(
                            $student->guardian->phone,
                            "Dear {$student->guardian->name}, {$student->name}'s " .
                            "Term 2 results are now available. Log in to MichoSMS to view them."
                        );
                    } catch (SessionNotConnectedException) {
                        // Stop immediately — no point continuing if session is down
                        $this->fail('WhatsApp session is not connected.');
                        return false;
                    }
                }
            });
    }
}

Send an Invoice PDF

use MichoTech\WApi\Facades\WApi;

$pdfPath = storage_path("app/invoices/invoice-{$invoice->id}.pdf");

WApi::messages()->sendDocument(
    $client->phone,
    $pdfPath,
    filename: "Invoice-{$invoice->number}.pdf",
    caption: "Hi {$client->name}, please find your invoice attached.",
);

Auto-Reply to Incoming Messages

// In your webhook controller
protected function onMessageReceived(array $data): void
{
    $from    = $data['from'];
    $message = strtolower(trim($data['message'] ?? ''));

    $reply = match (true) {
        str_contains($message, 'results') => 'Log in to MichoSMS at michosms.com to view your results.',
        str_contains($message, 'fees')    => 'Fees can be paid via mobile money. Reply FEES for details.',
        default                           => 'Thank you for your message. We will get back to you shortly.',
    };

    WApi::messages()->sendText($from, $reply);
}

License

MIT. See LICENSE for details.

About Micho Technologies

MichoWAPI is built and maintained by Micho Technologies, a software company based in Zambia building modern technology for the African market. Other products include MichoSMS (school management) and MyTutor ZM (peer-tutoring marketplace).