tabi/sdk

Official PHP SDK for the Tabi WhatsApp business messaging API

v0.3.0 2026-04-20 19:07 UTC

This package is not auto-updated.

Last update: 2026-04-21 17:42:09 UTC


README

Official PHP SDK for the Tabi WhatsApp Business Messaging API.

Latest Stable Version License: MIT

Table of Contents

Installation

composer require tabi/sdk

Getting Started

<?php

use Tabi\SDK\TabiClient;

$tabi = new TabiClient(
    'tk_your_api_key',
    'https://api.tabi.africa/api/v1'
);

$result = $tabi->messages()->send('your-channel-id', [
    'to' => '2348012345678',
    'content' => 'Hello from the Tabi PHP SDK!',
]);

Credential sources

Value Location
API key / JWT Dashboard → Developer → API Keys (or login flow for JWT)
Base URL https://api.tabi.africa/api/v1 (default when the second constructor argument is omitted)
Channel ID Dashboard → Channels → open a channel → copy the ID from the URL

Configuration

$tabi = new TabiClient(
    'tk_...',   // required — API key or JWT
    'https://api.tabi.africa/api/v1' // optional — defaults to production API base
);

OTP over WhatsApp

Hosted OTP uses Tabi\SDK\Resources\Channels::sendOtp and verifyOtp, which map to POST /channels/{channelId}/otp/send and POST /channels/{channelId}/otp/verify on the Tabi API.

Hosted OTP (recommended)

  1. Create or reuse a workspace API key (or JWT) with the same scopes as messages()->send() (for example messages:send).
  2. Call sendOtp with the channel UUID and an E.164 phone value.
  3. Call verifyOtp with 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 code. Client applications must talk to the integrator’s backend; the Tabi API key must not be embedded in mobile or browser 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 account configuration (Meta Business Manager).
  • Limit OTP to legitimate verification flows (for example sign-in); do not use it for cold outreach or bulk marketing.

Custom OTP (without hosted routes)

  1. Generate codes and store hashes in application-controlled storage (for example Redis).
  2. Deliver the code with messages()->send(), using messageClass and other fields as required by the HTTP API reference for transactional messages.

Resource groups (feature areas)

The SDK mirrors how the Tabi API is organised. Each method on TabiClient returns a resource object mapped 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

Request parameters: Resource methods document JSON bodies and query maps in PHPDoc using array{ key: type, … } shapes. These mirror the REST DTOs in the HTTP API reference (OpenAPI schemas).

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();

// Workspace invite link token (public preview)
$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']); // optional body

$tabi->channels()->status('channel-id');

$tabi->channels()->disconnect('channel-id');

$tabi->channels()->delete('channel-id');

// Requires user JWT — not a channel API key
$tabi->channels()->update('channel-id', ['riskEngineEnabled' => false]);

$tabi->channels()->reconnect('channel-id');

// Hosted OTP — same API key / scopes as messages()->send() — see "OTP over WhatsApp" above
$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',
    // sticker payload per API
]);

$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', [
    'content' => '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 API key).

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']; omit for full access in scope
expiresAt ISO 8601 string no Key stops working after this time
$key = $tabi->apiKeys()->create([
    'name' => 'Production Key',
    'scopes' => ['messages:send', 'channels:read'],
]);
// $key['rawKey'] — save immediately; only returned once

$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 ranges (pass date/query params as arrays).

$tabi->analytics()->dashboard(['from' => '2025-01-01', 'to' => '2025-01-31']);
$tabi->analytics()->channels(['from' => '2025-01-01', 'to' => '2025-01-31']);
$tabi->analytics()->conversations(['from' => '2025-01-01', 'to' => '2025-01-31']);

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 = new TabiClient('tk_...');

try {
    $tabi->messages()->send('channel-id', ['to' => '234...', 'content' => 'Hi']);
} catch (TabiException $e) {
    echo $e->getMessage();   // API message
    echo $e->statusCode;     // e.g. 400, 401, 403, 404
    var_dump($e->body);     // full JSON error payload, if any
}

Common HTTP status codes

Status Meaning
400 Bad request — check your 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). Other successful shapes are returned as decoded JSON (mixed). Refer to the API docs for each endpoint’s schema.

Requirements

  • PHP >= 8.1
  • Extensions: curl, json

Related

Support

License

MIT — see LICENSE for details.