Skip to content

PHP SDK (Laravel)

Official Laravel package for integrating with the CubeConnect WhatsApp Business API.

Requirements

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

Installation

bash
composer require cubesoftware/cube-connect-sdk-php

Publish the configuration file:

bash
php artisan vendor:publish --tag=cubeconnect-config

Configuration

ini
CUBECONNECT_API_KEY=your_api_key_here                   # Settings → API
CUBECONNECT_WHATSAPP_ACCOUNT_ID=your_account_id_here   # Dashboard → WhatsApp Numbers → API ID:

Optional:

ini
CUBECONNECT_URL=https://cubeconnect.io
CUBECONNECT_TIMEOUT=30

Multiple WhatsApp Numbers

If your account has more than one connected number, set the default in .env and override it per call:

php
// Send from a different number
CubeConnect::sendTemplate(
    '+966501234567',
    'offer_reminder',
    'ar',
    ['50%'],
    null,                  // $scheduledAt
    null,                  // $timezone
    '01JX_MARKETING',      // $whatsappAccountId — override
);

// List templates for a specific number
CubeConnect::getTemplates('APPROVED', '01JX_MARKETING');

// Create a campaign from a specific number
CubeConnect::createCampaign([
    'message_type'        => 'template',
    'template_name'       => 'offer_reminder',
    'template_language'   => 'ar',
    'recipients'          => [...],
    'whatsapp_account_id' => '01JX_MARKETING',  // override
]);

Find each number's ID in Dashboard → WhatsApp Numbers → API ID:.

Sending Messages

sendText()

ParameterTypeRequiredDescription
$phonestringYesRecipient phone number with country code (e.g., +966501234567)
$bodystringYesMessage text (max 4096 characters)
$scheduledAtstring|nullNoISO 8601 datetime for scheduled delivery (e.g., 2026-05-01T10:00:00)
$timezonestring|nullNoIANA timezone (e.g., Asia/Riyadh). Required when $scheduledAt is set
$whatsappAccountIdstring|nullNoOverride the default WhatsApp account. Useful when managing multiple numbers
php
use CubeConnect\Facades\CubeConnect;

$response = CubeConnect::sendText('+966501234567', 'مرحباً بك في متجرنا!');

$response->status;       // "queued"
$response->messageLogId; // 4521

Scheduled delivery:

php
$response = CubeConnect::sendText(
    '+966501234567',
    'تذكير: موعدك غداً الساعة 10 صباحاً.',
    '2026-05-01T09:00:00', // $scheduledAt (ISO 8601)
    'Asia/Riyadh',         // $timezone (IANA)
);

$response->status;      // "scheduled"
$response->scheduledAt; // "2026-05-01T06:00:00Z" (UTC)

sendTemplate()

Send a pre-approved template message to any number at any time.

ParameterTypeRequiredDescription
$phonestringYesRecipient phone number with country code (e.g., +966501234567)
$namestringYesTemplate name (e.g., order_confirmation)
$languageCodestringYesLanguage code matching the approved template (e.g., ar, en_US)
$paramsarrayNoParameters mapping to 1, 2, etc. in the template body
$scheduledAtstring|nullNoISO 8601 datetime for scheduled delivery (e.g., 2026-05-01T10:00:00)
$timezonestring|nullNoIANA timezone (e.g., Asia/Riyadh). Required when $scheduledAt is set
$whatsappAccountIdstring|nullNoOverride the default WhatsApp account. Useful when managing multiple numbers
php
use CubeConnect\Facades\CubeConnect;

$response = CubeConnect::sendTemplate(
    '+966501234567',          // $phone
    'order_confirmation',     // $name
    'ar',                     // $languageCode
    ['ORD-1234', '500 SAR'],  // $params → {{1}}, {{2}}
);

$response->status;               // "queued"
$response->messageLogId;         // 4521
$response->conversationCategory; // "UTILITY"

Without parameters:

php
$response = CubeConnect::sendTemplate('+966501234567', 'welcome_message', 'ar');

Scheduled delivery:

php
$response = CubeConnect::sendTemplate(
    '+966501234567',
    'appointment_reminder',
    'ar',                      // $languageCode
    ['Dr. Ahmed', '10:00 AM'], // $params
    '2026-05-01T09:00:00',     // $scheduledAt (ISO 8601)
    'Asia/Riyadh',             // $timezone (IANA)
);

$response->status;      // "scheduled"
$response->scheduledAt; // "2026-05-01T06:00:00Z" (UTC)

Override the sending number (when you manage multiple WhatsApp numbers):

php
$response = CubeConnect::sendTemplate(
    '+966501234567',
    'order_confirmation',
    'ar',
    ['ORD-1234'],
    null,          // $scheduledAt
    null,          // $timezone
    'account_id_2' // $whatsappAccountId — overrides the default
);

Get Message Status

getMessageStatus()

ParameterTypeRequiredDescription
$messageLogIdintYesThe messageLogId returned when the message was sent
php
$sent = CubeConnect::sendTemplate('+966501234567', 'order_confirmation', 'ar', ['ORD-1234']);

$status = CubeConnect::getMessageStatus($sent->messageLogId);

$status->status;        // "delivered"
$status->toPhone;       // "966501234567"
$status->metaMessageId; // "wamid.HBgN..."
$status->sentAt;        // "2026-05-01T07:05:00Z"
$status->isDelivered(); // true
$status->isRead();      // false

TIP

For real-time updates, configure a webhook to receive message.status_updated events instead of polling.

List Templates

getTemplates()

ParameterTypeRequiredDescription
$statusstring|nullNoFilter by approval status. Use APPROVED to fetch only sendable templates
$whatsappAccountIdstring|nullNoOverride the default WhatsApp account
php
$templates = CubeConnect::getTemplates('APPROVED');

foreach ($templates as $t) {
    $t->name;         // "order_confirmation"
    $t->language;     // "ar"
    $t->category;     // "UTILITY"
    $t->status;       // "APPROVED"
    $t->paramsCount;  // 3
    $t->body;         // "Hello {{1}}, your order {{2}} has been shipped."
    $t->header;       // null
    $t->isApproved(); // true
}

All templates (no filter):

php
$templates = CubeConnect::getTemplates();

Templates for a specific number:

php
$templates = CubeConnect::getTemplates('APPROVED', 'account_id_2');

Bulk Campaigns

createCampaign()

Send a pre-approved template to a large list in a single API call. The whatsapp_account_id from your config is used automatically. Pass whatsapp_account_id inside the array to override it.

ParameterTypeRequiredDescription
message_typestringYesMust be template
template_namestringYesTemplate name (e.g., order_confirmation)
template_languagestringYesLanguage code matching the approved template (e.g., ar, en_US)
recipientsarrayYesList of recipients. Max 50,000 per call
recipients[].phonestringYesRecipient phone number with country code
recipients[].namestringNoRecipient display name
recipients[].variablesarrayNoPer-recipient template variables (e.g., ['1' => 'Ahmed', '2' => 'ORD-1234'])
campaign_namestringNoHuman-readable campaign name
scheduled_atstringNoISO 8601 datetime for scheduled delivery
timezonestringNoIANA timezone. Required when scheduled_at is set
whatsapp_account_idstringNoOverride the default WhatsApp account
php
$campaign = CubeConnect::createCampaign([
    'message_type'      => 'template',
    'template_name'     => 'order_confirmation', // $name used in sendTemplate()
    'template_language' => 'ar',                 // $languageCode used in sendTemplate()
    'recipients'        => [
        ['phone' => '+966501234567', 'name' => 'Ahmed', 'variables' => ['1' => 'Ahmed', '2' => 'ORD-1234', '3' => 'CUBE20']],
        ['phone' => '+966509876543', 'name' => 'Sara',  'variables' => ['1' => 'Sara',  '2' => 'ORD-5678', '3' => 'CUBE15']],
    ],
    'campaign_name' => 'Offer Reminder',
    'scheduled_at'  => '2026-05-01T09:00:00', // optional
    'timezone'      => 'Asia/Riyadh',           // required when scheduled_at is set
]);

$campaign->campaignId;    // "01JX..."
$campaign->status;        // "pending"
$campaign->totalCount;    // 2
$campaign->isScheduled(); // true

getCampaign()

ParameterTypeRequiredDescription
$campaignIdstringYesThe campaignId returned from createCampaign()
php
$campaign = CubeConnect::getCampaign($campaignId);

$campaign->status;        // "processing", "completed", "cancelled"
$campaign->totalCount;    // 500
$campaign->sentCount;     // 320
$campaign->failedCount;   // 12
$campaign->isCompleted(); // true

cancelCampaign()

ParameterTypeRequiredDescription
$campaignIdstringYesThe campaignId of a pending campaign to cancel
php
$ok = CubeConnect::cancelCampaign($campaignId); // true on success

Health Check

php
$health = CubeConnect::health();

if ($health['status'] === 'healthy') {
    // Platform is operational
}

Response Objects

MessageResponse

Returned by sendText() and sendTemplate():

PropertyTypeDescription
statusstringqueued for immediate, scheduled for deferred delivery
messageLogIdstringUnique tracking ID
conversationCategorystringMARKETING, UTILITY, or AUTHENTICATION
costfloatMessage cost
scheduledAtstring|nullUTC datetime if scheduled, otherwise null
php
$response->queued();    // true if status is "queued"
$response->scheduled(); // true if status is "scheduled"
$response->toArray();   // Array representation

MessageStatusResponse

Returned by getMessageStatus():

PropertyTypeDescription
messageLogIdstringUnique message ID
statusstringCurrent delivery status
toPhonestringRecipient phone number
messageTypestringtemplate
metaMessageIdstring|nullWhatsApp message ID (after sending)
sentAtstring|nullUTC datetime when sent
scheduledAtstring|nullScheduled UTC datetime
costAmountfloatMessage cost
costCurrencystringCurrency code
errorMessagestring|nullError details if failed
createdAtstringLog creation timestamp
php
$status->isSent();      // true if status is "sent"
$status->isDelivered(); // true if status is "delivered"
$status->isRead();      // true if status is "read"
$status->isFailed();    // true if status is "failed"
$status->isScheduled(); // true if status is "scheduled"
$status->toArray();     // Array representation

CampaignResponse

Returned by createCampaign() and getCampaign():

PropertyTypeDescription
campaignIdstringUnique campaign ULID
namestring|nullCampaign name
statusstringpending, processing, completed, cancelled, failed
totalCountintTotal recipients
sentCountintSuccessfully sent
failedCountintFailed deliveries
scheduledAtstring|nullScheduled UTC datetime
createdAtstringCreation timestamp
php
$campaign->isScheduled(); // true if pending with a scheduledAt
$campaign->isCompleted(); // true if status is "completed"
$campaign->isCancelled(); // true if status is "cancelled"
$campaign->toArray();     // Array representation

Error Reference

HTTPError CodeCause
401AUTHENTICATION_REQUIREDNo API key provided in the request
401INVALID_API_KEYAPI key is invalid or has been revoked
403FORBIDDENAPI key does not have permission for this action
403API_KEY_NO_TENANTAPI key is not linked to any account
404NOT_FOUNDThe requested resource does not exist
404TEMPLATE_NOT_FOUNDTemplate name not found in your account
422VALIDATION_ERRORRequest failed input validation — check error.details for field-level errors
422INVALID_PHONE_NUMBERPhone number is not in a valid international format
422NO_ACTIVE_ACCOUNTNo connected WhatsApp number found for the given whatsapp_account_id
422MISSING_ACCESS_TOKENThe selected WhatsApp number has no Meta access token configured
422TEMPLATE_LANGUAGE_MISMATCHLanguage code does not match any approved version of this template
422TEMPLATE_PARAMS_MISMATCHFewer parameters provided than the template requires
429RATE_LIMIT_EXCEEDEDToo many API requests — apply exponential backoff and retry
429PLAN_LIMIT_REACHEDMonthly message quota reached — upgrade your plan
429SUBSCRIPTION_EXPIREDSubscription has expired
500MESSAGE_SEND_FAILEDWhatsApp API rejected or failed to deliver the message
500INTERNAL_ERRORUnexpected server error — contact support if this persists
503SERVICE_DEGRADEDOne or more platform services are temporarily unavailable

Error Handling

All exceptions include an errorCode property matching the API error codes:

php
use CubeConnect\Facades\CubeConnect;
use CubeConnect\Exceptions\AuthenticationException;
use CubeConnect\Exceptions\ValidationException;
use CubeConnect\Exceptions\RateLimitException;
use CubeConnect\Exceptions\NotFoundException;
use CubeConnect\Exceptions\CubeConnectException;

try {
    CubeConnect::sendTemplate('+966501234567', 'order_confirmation', 'ar', ['ORD-1234']);
} catch (AuthenticationException $e) {
    // 401/403 — Invalid API key or permissions
    $e->errorCode;  // "INVALID_API_KEY", "FORBIDDEN", ...
    $e->statusCode; // 401 or 403
} catch (ValidationException $e) {
    // 422 — Invalid request data
    $e->errorCode; // "VALIDATION_ERROR", "TEMPLATE_LANGUAGE_MISMATCH", ...
    $e->errors;    // ['available_languages' => ['ar']]
} catch (NotFoundException $e) {
    // 404 — Resource not found
    $e->errorCode; // "NOT_FOUND", "TEMPLATE_NOT_FOUND"
} catch (RateLimitException $e) {
    // 429 — Rate or plan limit exceeded
    $e->errorCode; // "RATE_LIMIT_EXCEEDED", "PLAN_LIMIT_REACHED", ...
} catch (CubeConnectException $e) {
    // 5xx or network errors
    $e->errorCode;  // "INTERNAL_ERROR", "MESSAGE_SEND_FAILED", ...
    $e->statusCode;
}

Dependency Injection

php
use CubeConnect\Contracts\Messaging;

class OrderController extends Controller
{
    public function shipped(Order $order, Messaging $messaging)
    {
        $messaging->sendTemplate(
            $order->customer_phone,                // $phone
            'order_shipped',                       // $name
            'ar',                                  // $languageCode
            [$order->id, $order->tracking_number], // $params
        );
    }
}

Webhooks

Receive real-time notifications from CubeConnect for messages, campaigns, templates, chatbot flows, and quality events.

Setup

ini
CUBECONNECT_WEBHOOK_SECRET=your_webhook_secret_here

Signature Verification Middleware

php
// routes/api.php
use CubeConnect\Webhooks\WebhookHandler;

Route::post('/cubeconnect/webhook', [WebhookController::class, 'handle'])
    ->middleware(WebhookHandler::class);

Manual Verification

php
use CubeConnect\Webhooks\WebhookSignature;

$isValid = WebhookSignature::verify(
    payload:   $request->getContent(),
    signature: $request->header('X-Webhook-Signature'),
    timestamp: $request->header('X-Webhook-Timestamp'),
    secret:    config('cubeconnect.webhook_secret'),
);

Handling Events

php
use CubeConnect\DTOs\WebhookEvent;

public function handle(Request $request)
{
    $event = WebhookEvent::fromRequest($request);

    match (true) {
        $event->isMessageReceived()       => $this->handleMessage($event),
        $event->isMessageStatusUpdated()  => $this->handleStatus($event),
        $event->isCampaignCompleted()     => $this->handleCampaign($event),
        $event->isTemplateStatusChanged() => $this->handleTemplate($event),
        $event->isFlowSessionCompleted()  => $this->handleFlow($event),
        $event->isQualityEvent()          => $this->handleQuality($event),
        default => null,
    };

    return response('OK', 200);
}

See Webhook Events for full payload examples and Signature Verification for security details.

Real-World Examples

E-Commerce Order Confirmation

php
use CubeConnect\Facades\CubeConnect;

public function store(Request $request)
{
    $order = Order::create($request->validated());

    CubeConnect::sendTemplate(
        $order->customer->phone,                      // $phone
        'order_confirmation',                         // $name
        'ar',                                         // $languageCode
        [$order->reference, $order->total . ' SAR'],  // $params
    );

    return redirect()->route('orders.show', $order);
}

OTP Verification

php
$code = str_pad(random_int(0, 999999), 6, '0', STR_PAD_LEFT);
cache()->put("otp:{$phone}", $code, now()->addMinutes(5));

CubeConnect::sendTemplate($phone, 'verification_code', 'ar', [$code]);

Scheduled Notifications

php
$expiringOrders = Order::where('expires_at', '<', now()->addDay())->get();

foreach ($expiringOrders as $order) {
    try {
        CubeConnect::sendTemplate(
            $order->customer->phone,
            'order_expiring',
            'ar',
            [$order->reference, $order->expires_at->format('Y-m-d')],
        );
    } catch (RateLimitException $e) {
        SendWhatsAppNotification::dispatch($order)->delay(now()->addMinute());
        break;
    }
}

Source Code

GitHub: github.com/CubeSoftLabs/cube-connect-sdk

CubeConnect WhatsApp Business Platform