tabi / laravel-sdk
Laravel integration for the Tabi WhatsApp Business Messaging API (wraps tabi/sdk)
Requires
- php: ^8.1
- illuminate/support: ^10.0|^11.0|^12.0
- tabi/sdk: ^0.3
Requires (Dev)
- orchestra/testbench: ^9.0|^10.0
- phpunit/phpunit: ^10.5|^11.0
This package is not auto-updated.
Last update: 2026-04-21 12:24:12 UTC
README
Official Laravel integration for the Tabi WhatsApp Business Messaging API. Registers
tabi/sdk(Tabi\SDK\TabiClient) from environment configuration.
This package does not duplicate the HTTP client: it wires TabiClient into Laravel’s service container and provides a facade. All request/response behaviour, PHPDoc array{…} shapes, and endpoint coverage come from tabi/sdk (see vendor/tabi/sdk/README.md after install).
Table of Contents
- Requirements
- Installation
- Configuration
- Resolving the client
- Getting started
- OTP over WhatsApp
- Resource groups
- Resources
- Error handling
- Return values
- Related
- Support
- License
Requirements
- PHP >= 8.1
- Laravel 10, 11, or 12
- Extensions (pulled in via
tabi/sdk): curl, json
Installation
composer require tabi/laravel-sdk
The service provider and Tabi facade are auto-discovered (no manual config/app.php registration).
Configuration
Publish the config file (optional):
php artisan vendor:publish --tag=tabi-config
| Env variable | Description |
|---|---|
TABI_API_KEY |
Workspace or channel API key (tk_…) or JWT when an endpoint requires dashboard auth |
TABI_BASE_URL |
Optional. Default: https://api.tabi.africa/api/v1 |
Credential sources
| Value | Location |
|---|---|
| API key / JWT | Dashboard → Developer → API Keys (or login flow for JWT) |
| Base URL | TABI_BASE_URL or default above |
| Channel ID | Dashboard → Channels → open a channel → copy the ID from the URL |
Resolving the client
Use any of the following; they resolve the same singleton TabiClient:
Constructor injection
use Tabi\SDK\TabiClient; public function __construct(private TabiClient $tabi) {}
Facade
use Tabi\Laravel\Facades\Tabi; Tabi::messages()->send('channel-id', [ /* ... */ ]);
Container
$tabi = app(TabiClient::class); // or $tabi = app('tabi.client');
In the Resources section below, $tabi always means an instance of TabiClient obtained in one of these ways.
Getting started
<?php use Tabi\SDK\TabiClient; // In a controller or job — inject TabiClient, or use app(TabiClient::class) $result = $tabi->messages()->send('your-channel-id', [ 'to' => '2348012345678', 'content' => 'Hello from Laravel!', ]);
OTP over WhatsApp
Hosted OTP uses channels()->sendOtp and verifyOtp, mapped to POST /channels/{channelId}/otp/send and POST /channels/{channelId}/otp/verify on the Tabi API.
Hosted OTP (recommended)
- Create or reuse a workspace API key (or JWT) with the same scopes as
messages()->send()(for examplemessages:send). - Call
sendOtpwith the channel UUID and an E.164phonevalue. - Call
verifyOtpwith the same channel,phone, and the code the user submitted.
<?php $tabi->channels()->sendOtp('channel-uuid', ['phone' => '+2347000000000']); $tabi->channels()->verifyOtp('channel-uuid', [ 'phone' => '+2347000000000', 'code' => '123456', ]);
REST: POST /api/v1/channels/{channelId}/otp/send and POST /api/v1/channels/{channelId}/otp/verify.
Security: Call these endpoints only from server-side Laravel code (controllers, jobs, commands). Do not expose the Tabi API key to the browser or mobile clients.
Compliance and rate limits
- OTP traffic shares the same WhatsApp Business channel and rate limits as other sends.
- Follow Meta / WhatsApp Business policies for templates, opt-in, and WABA configuration (Meta Business Manager).
- Use OTP only for legitimate verification flows (for example sign-in), not for cold outreach or bulk marketing.
Custom OTP (without hosted routes)
- Generate codes and store hashes in application-controlled storage (for example Redis).
- Deliver the code with
messages()->send(), usingmessageClassand other fields per the HTTP API reference.
Resource groups (feature areas)
The client mirrors the Tabi API. Each method on TabiClient maps to one REST area.
| Group | Client methods | What it covers |
|---|---|---|
| Getting started | auth(), workspaces() |
Login/register/tokens; workspaces, members, invites |
| Messaging & inbox | channels(), messages(), conversations(), contacts(), quickReplies(), notifications() |
WhatsApp lines, sends, threads, people, shortcuts |
| Automations & campaigns | automationTemplates(), automationInstalls(), campaigns() |
Template catalog, installed flows, broadcasts |
| Integrations | apiKeys(), webhooks(), integrations() |
Keys (create with JWT), outbound webhooks, third-party links |
| Media & insights | files(), analytics() |
Uploads, KPIs |
Parameters: Request bodies and query maps are documented in PHPDoc on tabi/sdk resource classes (array{ key: type, … }). See the HTTP API reference (OpenAPI).
Resources
Auth
Login, register, refresh tokens, session helpers, and invite preview.
$session = $tabi->auth()->login('user@example.com', 'password'); $tabi->auth()->register([ 'email' => 'user@example.com', 'password' => 'securePassword', 'firstName' => 'John', 'lastName' => 'Doe', 'workspaceName' => 'My Company', ]); $tabi->auth()->refresh('your-refresh-token'); $me = $tabi->auth()->me(); $tabi->auth()->logout(); $tabi->auth()->invitePreview('invite-token-from-link');
Channels
Create, list, connect, and manage WhatsApp channels.
$tabi->channels()->list(); $tabi->channels()->get('channel-id'); $tabi->channels()->create([ 'name' => 'Support Line', 'provider' => 'messaging', ]); $tabi->channels()->connect('channel-id'); $tabi->channels()->connect('channel-id', ['some' => 'payload']); $tabi->channels()->status('channel-id'); $tabi->channels()->disconnect('channel-id'); $tabi->channels()->delete('channel-id'); $tabi->channels()->update('channel-id', ['riskEngineEnabled' => false]); $tabi->channels()->reconnect('channel-id'); $tabi->channels()->sendOtp('channel-id', ['phone' => '+2347000000000']); $tabi->channels()->verifyOtp('channel-id', ['phone' => '+2347000000000', 'code' => '123456']);
Messages
Send text and rich messages, list timeline, reply, reactions, and media.
$tabi->messages()->send('channel-id', [ 'to' => '2348012345678', 'content' => 'Hello! How can we help you today?', ]); $tabi->messages()->send('channel-id', [ 'to' => '2348012345678', 'content' => 'Check this out!', 'messageType' => 'image', 'mediaUrl' => 'https://example.com/image.png', ]); $tabi->messages()->send('channel-id', [ 'to' => '2348012345678', 'content' => 'Here is your invoice', 'messageType' => 'document', 'mediaUrl' => 'https://example.com/invoice.pdf', ]); $tabi->messages()->get('message-id'); $tabi->messages()->listByConversation('conversation-id', [ 'page' => 1, 'limit' => 50, ]); $tabi->messages()->reply('conversation-id', [ 'content' => 'Thanks for reaching out!', ]); $tabi->messages()->sendPoll('channel-id', [ 'to' => '2348012345678', 'question' => 'What do you prefer?', 'options' => ['Option A', 'Option B', 'Option C'], 'maxAnswer' => 1, ]); $tabi->messages()->sendLocation('channel-id', [ 'to' => '2348012345678', 'latitude' => '6.5244', 'longitude' => '3.3792', ]); $tabi->messages()->sendContact('channel-id', [ 'to' => '2348012345678', 'contactName' => 'Jane Doe', 'contactPhone' => '2348099999999', ]); $tabi->messages()->sendSticker('channel-id', [ 'to' => '2348012345678', 'stickerUrl' => 'https://example.com/sticker.webp', ]); $tabi->messages()->react('channel-id', 'provider-msg-id', [ 'emoji' => '👍', ]); $tabi->messages()->markRead('channel-id', 'provider-msg-id'); $tabi->messages()->edit('channel-id', 'provider-msg-id', [ 'text' => 'Updated message text', ]); $tabi->messages()->revoke('channel-id', 'provider-msg-id'); $tabi->messages()->downloadMedia('channel-id', 'provider-msg-id');
Contacts
Create, update, import, tags, and consent.
$tabi->contacts()->list(['page' => 1, 'limit' => 20, 'search' => 'John']); $tabi->contacts()->get('contact-id'); $tabi->contacts()->create([ 'phone' => '2348012345678', 'name' => 'John Doe', 'email' => 'john@example.com', ]); $tabi->contacts()->update('contact-id', ['name' => 'Johnathan Doe']); $tabi->contacts()->delete('contact-id'); $tabi->contacts()->import([ 'contacts' => [ ['phone' => '2348012345678', 'name' => 'Alice'], ['phone' => '2348087654321', 'name' => 'Bob'], ], ]); $tabi->contacts()->getTags('contact-id'); $tabi->contacts()->addTag('contact-id', 'vip'); $tabi->contacts()->removeTag('contact-id', 'vip'); $tabi->contacts()->optIn('contact-id'); $tabi->contacts()->optOut('contact-id');
Conversations
List, update, resolve, reopen, and read state.
$tabi->conversations()->list(['page' => 1, 'limit' => 25, 'status' => 'open']); $tabi->conversations()->get('conversation-id'); $tabi->conversations()->update('conversation-id', [ 'assignedTo' => 'member-id', ]); $tabi->conversations()->resolve('conversation-id'); $tabi->conversations()->reopen('conversation-id'); $tabi->conversations()->markRead('conversation-id');
Webhooks
Subscriptions, delivery logs, secrets, and test capture.
$tabi->webhooks()->create([ 'url' => 'https://example.com/webhook', 'events' => ['message.received', 'message.sent', 'conversation.created'], ]); $tabi->webhooks()->list(); $tabi->webhooks()->get('webhook-id'); $tabi->webhooks()->update('webhook-id', [ 'url' => 'https://example.com/new-webhook', 'events' => ['message.received'], ]); $tabi->webhooks()->ping('webhook-id'); $tabi->webhooks()->rotateSecret('webhook-id'); $tabi->webhooks()->deliveryLogs(['channelId' => 'channel-uuid', 'limit' => 50]); $tabi->webhooks()->startTestCapture(['channelId' => 'channel-uuid']); $tabi->webhooks()->testCaptureStatus(['channelId' => 'channel-uuid']); $tabi->webhooks()->stopTestCapture(['channelId' => 'channel-uuid']); $tabi->webhooks()->delete('webhook-id');
API Keys
Create and manage API keys. Creating keys requires a user JWT from the dashboard (not an integrator API key alone).
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | yes | Label in the dashboard |
channelId |
string (UUID) | no | Restrict key to one channel |
scopes |
string[] | no | e.g. ['channels:read', 'messages:send'] |
expiresAt |
ISO 8601 string | no | Key stops working after this time |
$key = $tabi->apiKeys()->create([ 'name' => 'Production Key', 'scopes' => ['messages:send', 'channels:read'], ]); $tabi->apiKeys()->list(); $tabi->apiKeys()->list(['channelId' => 'uuid-of-channel']); $tabi->apiKeys()->revoke('key-id'); $tabi->apiKeys()->delete('key-id');
Files
List files, metadata, signed URLs, delete.
$tabi->files()->list(); $tabi->files()->get('file-id'); $tabi->files()->getUrl('file-id'); $tabi->files()->delete('file-id');
Campaigns
Draft, schedule, and control broadcast campaigns.
$tabi->campaigns()->create([ 'name' => 'Promo Blast', 'channelId' => 'channel-id', 'content' => 'Flash sale — 50% off today!', 'audienceFilter' => ['tags' => ['subscribers']], ]); $tabi->campaigns()->list(['page' => 1, 'limit' => 10]); $tabi->campaigns()->get('campaign-id'); $tabi->campaigns()->update('campaign-id', ['name' => 'Updated Promo']); $tabi->campaigns()->schedule('campaign-id'); $tabi->campaigns()->pause('campaign-id'); $tabi->campaigns()->resume('campaign-id'); $tabi->campaigns()->cancel('campaign-id'); $tabi->campaigns()->delete('campaign-id');
Automation Templates
Browse the template catalog.
$tabi->automationTemplates()->list(); $tabi->automationTemplates()->get('template-id');
Automation Installs
Install, configure, enable/disable, uninstall.
$tabi->automationInstalls()->install([ 'templateId' => 'template-uuid', 'config' => ['greeting' => 'Welcome!'], ]); $tabi->automationInstalls()->list(); $tabi->automationInstalls()->get('install-id'); $tabi->automationInstalls()->update('install-id', [ 'config' => ['greeting' => 'Hi there!'], ]); $tabi->automationInstalls()->enable('install-id'); $tabi->automationInstalls()->disable('install-id'); $tabi->automationInstalls()->uninstall('install-id');
Quick Replies
Canned responses for agents.
$tabi->quickReplies()->list(); $tabi->quickReplies()->create([ 'title' => 'Greeting', 'shortcut' => '/hello', 'content' => 'Hello! How can I help you today?', ]); $tabi->quickReplies()->update('reply-id', ['content' => 'Updated greeting']); $tabi->quickReplies()->delete('reply-id');
Analytics
Dashboard and reporting (query parameters are reserved for future API filters where documented).
$tabi->analytics()->dashboard(); $tabi->analytics()->channels(); $tabi->analytics()->conversations();
Notifications
In-app notification inbox.
$tabi->notifications()->list(['page' => 1, 'limit' => 20]); $tabi->notifications()->markRead('notification-id'); $tabi->notifications()->markAllRead(); $tabi->notifications()->unreadCount();
Integrations
Third-party providers (CRM, helpdesk, etc.).
$tabi->integrations()->listProviders(); $tabi->integrations()->create([ 'provider' => 'hubspot', 'credentials' => ['apiKey' => 'hs_...'], 'config' => [], ]); $tabi->integrations()->list(); $tabi->integrations()->get('integration-id'); $tabi->integrations()->test('integration-id'); $tabi->integrations()->update('integration-id', [ 'config' => ['apiKey' => 'hs_new_key'], ]); $tabi->integrations()->delete('integration-id');
Workspaces
Workspaces and team members.
$tabi->workspaces()->list(); $tabi->workspaces()->get('workspace-id'); $tabi->workspaces()->create(['name' => 'New Team']); $tabi->workspaces()->update('workspace-id', ['name' => 'Renamed Team']); $tabi->workspaces()->listMembers('workspace-id'); $tabi->workspaces()->inviteMember('workspace-id', [ 'email' => 'colleague@example.com', 'roleSlug' => 'admin', ]);
Error handling
Failed HTTP responses throw Tabi\SDK\TabiException with the status code and decoded body when available.
use Tabi\SDK\TabiClient; use Tabi\SDK\TabiException; $tabi = app(TabiClient::class); try { $tabi->messages()->send('channel-id', ['to' => '234...', 'content' => 'Hi']); } catch (TabiException $e) { $e->getMessage(); $e->statusCode; $e->body; }
Common HTTP status codes
| Status | Meaning |
|---|---|
400 |
Bad request — check payload |
401 |
Unauthorized — invalid or expired token |
403 |
Forbidden — insufficient permissions |
404 |
Not found |
409 |
Conflict — duplicate or invalid state |
429 |
Rate limited — retry with backoff |
500 |
Server error — contact support |
Return values
Successful responses that use the API’s { "success": true, "data": ... } envelope return the data value only (typically an array). See the API docs for each endpoint’s schema.
Related
- Plain PHP (no Laravel):
tabi/sdkon Packagist - JavaScript / TypeScript:
tabi-sdkon npm - HTTP reference: tabi.africa/api-docs
Support
- Product overview: tabi.africa/sdks
- HTTP API reference: tabi.africa/api-docs
License
MIT — see LICENSE for details.