Skip to content
PaalChat Docs

Build and operate

PHP SDK and Laravel package

paaltech/paalchat-php and paaltech/paalchat-laravel - typed errors, safe retries and verified callbacks.

.md

In a nutshell

composer require paaltech/paalchat-laravel, set PAALCHAT_API_KEY and PAALCHAT_WEBHOOK_SECRET. Writes get an Idempotency-Key automatically and are retried safely; errors are typed exceptions with error codes; callbacks arrive verified and de-duplicated as Laravel events.

Install

cURL
composer require paaltech/paalchat-laravel   # Laravel (includes the PHP client)
composer require paaltech/paalchat-php       # any PHP 8.2+ project
Dotenv
PAALCHAT_API_KEY=sk_live_...        # sk_test_... for the sandbox
PAALCHAT_WEBHOOK_SECRET=...         # your webhook endpoint's secret

Send

PHP
use PaalTech\PaalChat\Laravel\Facades\PaalChat;

PaalChat::businesses()->upsert(['external_id' => 'presec', 'name' => 'Presec Legon']);

PaalChat::messages('presec')->sendTemplate(
    '+233241234567', 'fee_reminder', 'en_US',
    ['parent' => 'Mrs Mensah', 'student' => 'Kofi'],
    reference: 'notification-4411',
);

Without Laravel: $paalchat = new PaalTech\PaalChat\PaalChat(getenv('PAALCHAT_API_KEY')); then the same calls on $paalchat.

Area Calls
Product me(), usage([...])
Businesses businesses()->upsert(), get(), list()
WhatsApp whatsapp($business)->connectLink(), status(), phones(), disconnect()
Templates templates($business)->list(), create(), delete()
Messages messages($business)->send(), sendText(), sendTemplate(), get()
Media media($business)->upload($path), get($id)
Contacts contacts($business)->list(), upsert(), get(), update()
Conversations conversations($business)->list(), get(), update(), messages(), addNote()
Inbox inbox($business)->signIn($staff), members(), removeAccess()
Sandbox sandbox($business)->incoming(), status()

Retries and errors

  • Every write gets an Idempotency-Key (reused across its retries), so a retry never acts twice. Pass your own as the last argument of send() if you retry later yourself.
  • Network errors, 429 (after Retry-After) and 5xx are retried with backoff (max_retries, default 2).
  • Errors are exceptions: AuthenticationException (401), PermissionException (403), NotFoundException (404), ConflictException (409), ValidationException (422, with $e->errors), RateLimitException (429, $e->retryAfter), ServerException, ConnectionException. Each has $e->errorCode and $e->requestId.
PHP
use PaalTech\PaalChat\Exceptions\ValidationException;

try {
    PaalChat::messages('presec')->sendText('233241234567', 'Hello', reference: 'inbox-991');
} catch (ValidationException $e) {
    if ($e->errorCode === 'outside_service_window') {
        // offer a template instead
    }
}

Callbacks

The Laravel package registers POST /paalchat/callback (set PAALCHAT_WEBHOOK_PATH to change it; exclude it from CSRF). Each callback is verified on the raw body, de-duplicated on X-PaalChat-Delivery, answered at once, and fired as PaalChatEventReceived and paalchat.<event>:

PHP
use Illuminate\Support\Facades\Event;
use PaalTech\PaalChat\Event as PaalChatEvent;

Event::listen('paalchat.message.status', function (PaalChatEvent $event) {
    Notification::where('id', $event->data['reference'])->first()?->advanceTo($event->data['status']);
});

Without Laravel, verify yourself:

PHP
use PaalTech\PaalChat\Webhook;

$event = Webhook::constructEvent(file_get_contents('php://input'), $_SERVER['HTTP_X_PAALCHAT_SIGNATURE'] ?? '', $secret);