Appearance
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-phpPublish the configuration file:
bash
php artisan vendor:publish --tag=cubeconnect-configConfiguration
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=30Multiple 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()
| Parameter | Type | Required | Description |
|---|---|---|---|
$phone | string | Yes | Recipient phone number with country code (e.g., +966501234567) |
$body | string | Yes | Message text (max 4096 characters) |
$scheduledAt | string|null | No | ISO 8601 datetime for scheduled delivery (e.g., 2026-05-01T10:00:00) |
$timezone | string|null | No | IANA timezone (e.g., Asia/Riyadh). Required when $scheduledAt is set |
$whatsappAccountId | string|null | No | Override the default WhatsApp account. Useful when managing multiple numbers |
php
use CubeConnect\Facades\CubeConnect;
$response = CubeConnect::sendText('+966501234567', 'مرحباً بك في متجرنا!');
$response->status; // "queued"
$response->messageLogId; // 4521Scheduled 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.
| Parameter | Type | Required | Description |
|---|---|---|---|
$phone | string | Yes | Recipient phone number with country code (e.g., +966501234567) |
$name | string | Yes | Template name (e.g., order_confirmation) |
$languageCode | string | Yes | Language code matching the approved template (e.g., ar, en_US) |
$params | array | No | Parameters mapping to 1, 2, etc. in the template body |
$scheduledAt | string|null | No | ISO 8601 datetime for scheduled delivery (e.g., 2026-05-01T10:00:00) |
$timezone | string|null | No | IANA timezone (e.g., Asia/Riyadh). Required when $scheduledAt is set |
$whatsappAccountId | string|null | No | Override 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()
| Parameter | Type | Required | Description |
|---|---|---|---|
$messageLogId | int | Yes | The 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(); // falseTIP
For real-time updates, configure a webhook to receive message.status_updated events instead of polling.
List Templates
getTemplates()
| Parameter | Type | Required | Description |
|---|---|---|---|
$status | string|null | No | Filter by approval status. Use APPROVED to fetch only sendable templates |
$whatsappAccountId | string|null | No | Override 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.
| Parameter | Type | Required | Description |
|---|---|---|---|
message_type | string | Yes | Must be template |
template_name | string | Yes | Template name (e.g., order_confirmation) |
template_language | string | Yes | Language code matching the approved template (e.g., ar, en_US) |
recipients | array | Yes | List of recipients. Max 50,000 per call |
recipients[].phone | string | Yes | Recipient phone number with country code |
recipients[].name | string | No | Recipient display name |
recipients[].variables | array | No | Per-recipient template variables (e.g., ['1' => 'Ahmed', '2' => 'ORD-1234']) |
campaign_name | string | No | Human-readable campaign name |
scheduled_at | string | No | ISO 8601 datetime for scheduled delivery |
timezone | string | No | IANA timezone. Required when scheduled_at is set |
whatsapp_account_id | string | No | Override 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(); // truegetCampaign()
| Parameter | Type | Required | Description |
|---|---|---|---|
$campaignId | string | Yes | The 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(); // truecancelCampaign()
| Parameter | Type | Required | Description |
|---|---|---|---|
$campaignId | string | Yes | The campaignId of a pending campaign to cancel |
php
$ok = CubeConnect::cancelCampaign($campaignId); // true on successHealth Check
php
$health = CubeConnect::health();
if ($health['status'] === 'healthy') {
// Platform is operational
}Response Objects
MessageResponse
Returned by sendText() and sendTemplate():
| Property | Type | Description |
|---|---|---|
status | string | queued for immediate, scheduled for deferred delivery |
messageLogId | string | Unique tracking ID |
conversationCategory | string | MARKETING, UTILITY, or AUTHENTICATION |
cost | float | Message cost |
scheduledAt | string|null | UTC datetime if scheduled, otherwise null |
php
$response->queued(); // true if status is "queued"
$response->scheduled(); // true if status is "scheduled"
$response->toArray(); // Array representationMessageStatusResponse
Returned by getMessageStatus():
| Property | Type | Description |
|---|---|---|
messageLogId | string | Unique message ID |
status | string | Current delivery status |
toPhone | string | Recipient phone number |
messageType | string | template |
metaMessageId | string|null | WhatsApp message ID (after sending) |
sentAt | string|null | UTC datetime when sent |
scheduledAt | string|null | Scheduled UTC datetime |
costAmount | float | Message cost |
costCurrency | string | Currency code |
errorMessage | string|null | Error details if failed |
createdAt | string | Log 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 representationCampaignResponse
Returned by createCampaign() and getCampaign():
| Property | Type | Description |
|---|---|---|
campaignId | string | Unique campaign ULID |
name | string|null | Campaign name |
status | string | pending, processing, completed, cancelled, failed |
totalCount | int | Total recipients |
sentCount | int | Successfully sent |
failedCount | int | Failed deliveries |
scheduledAt | string|null | Scheduled UTC datetime |
createdAt | string | Creation 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 representationError Reference
| HTTP | Error Code | Cause |
|---|---|---|
| 401 | AUTHENTICATION_REQUIRED | No API key provided in the request |
| 401 | INVALID_API_KEY | API key is invalid or has been revoked |
| 403 | FORBIDDEN | API key does not have permission for this action |
| 403 | API_KEY_NO_TENANT | API key is not linked to any account |
| 404 | NOT_FOUND | The requested resource does not exist |
| 404 | TEMPLATE_NOT_FOUND | Template name not found in your account |
| 422 | VALIDATION_ERROR | Request failed input validation — check error.details for field-level errors |
| 422 | INVALID_PHONE_NUMBER | Phone number is not in a valid international format |
| 422 | NO_ACTIVE_ACCOUNT | No connected WhatsApp number found for the given whatsapp_account_id |
| 422 | MISSING_ACCESS_TOKEN | The selected WhatsApp number has no Meta access token configured |
| 422 | TEMPLATE_LANGUAGE_MISMATCH | Language code does not match any approved version of this template |
| 422 | TEMPLATE_PARAMS_MISMATCH | Fewer parameters provided than the template requires |
| 429 | RATE_LIMIT_EXCEEDED | Too many API requests — apply exponential backoff and retry |
| 429 | PLAN_LIMIT_REACHED | Monthly message quota reached — upgrade your plan |
| 429 | SUBSCRIPTION_EXPIRED | Subscription has expired |
| 500 | MESSAGE_SEND_FAILED | WhatsApp API rejected or failed to deliver the message |
| 500 | INTERNAL_ERROR | Unexpected server error — contact support if this persists |
| 503 | SERVICE_DEGRADED | One 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_hereSignature 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;
}
}