Skip to content

Campaigns

Send a pre-approved template message to a large list of recipients in a single API call. Campaigns support both immediate and scheduled delivery.

Template Required

WhatsApp requires a pre-approved template for bulk campaigns. Text messages can only be sent within a 24-hour window after the recipient last contacted you — which cannot be guaranteed for bulk lists. Always use "message_type": "template" for campaigns.

Daily Tier Limit

Meta enforces a daily messaging limit per phone number based on its tier:

TierDaily limit
Tier 1 (new numbers)1,000 conversations/day
Tier 210,000 conversations/day
Tier 3100,000 conversations/day
UnlimitedNo limit

If your recipient count exceeds the number's remaining daily capacity, the API returns MESSAGING_TIER_LIMIT_EXCEEDED (HTTP 429) with a details object showing daily_limit, used_today, remaining, and requested. Split your campaign across multiple days or upgrade your tier by increasing message volume gradually.

Endpoints

MethodPathDescription
POST/api/v1/campaignsCreate a bulk campaign
GET/api/v1/campaigns/{id}Get campaign status and statistics
GET/api/v1/campaigns/{id}/recipientsList recipients with delivery status
POST/api/v1/campaigns/{id}/cancelCancel a scheduled campaign

Create Campaign

POST /api/v1/campaigns

Headers

HeaderRequiredDescription
AuthorizationYesBearer YOUR_API_KEY
Content-TypeYesapplication/json

Body Parameters

ParameterTypeRequiredDescription
whatsapp_account_idstringYesSender account ID. Find it in Dashboard → WhatsApp Numbers → copy icon next to API ID:
message_typestringYesMust be template
template_namestringYesTemplate name (e.g., order_confirmation) — same name used in sendTemplate
template_languagestringNoTemplate language code (e.g., ar, en_US). Defaults to the first matching template.
template_paramsarrayNoArray of string parameters for template placeholders
recipientsarrayYesList of recipients (max 50,000)
recipients[].phonestringYesRecipient phone number with country code
recipients[].namestringNoRecipient display name
recipients[].variablesobjectNoPer-recipient template variables
campaign_namestringNoHuman-readable campaign name (max 120 characters)
scheduled_atstringNoISO 8601 datetime for scheduled delivery (e.g., 2026-05-01T09:00:00). Must be in the future.
timezonestringNoIANA timezone for scheduled_at (e.g., Asia/Riyadh). Defaults to UTC.

Example Request

bash
curl -X POST https://cubeconnect.io/api/v1/campaigns \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "whatsapp_account_id": "01JX...",
    "message_type": "template",
    "template_name": "order_confirmation",
    "template_language": "ar",
    "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",
    "timezone": "Asia/Riyadh"
  }'
php
$response = Http::withToken('YOUR_API_KEY')
    ->post('https://cubeconnect.io/api/v1/campaigns', [
        'whatsapp_account_id' => '01JX...',
        'message_type'        => 'template',
        'template_name'       => 'order_confirmation',
        'template_language'   => 'ar',
        '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',
        'timezone'      => 'Asia/Riyadh',
    ]);
javascript
const response = await fetch('https://cubeconnect.io/api/v1/campaigns', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    whatsapp_account_id: '01JX...',
    message_type: 'template',
    template_name: 'order_confirmation',
    template_language: 'ar',
    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',
    timezone: 'Asia/Riyadh',
  }),
})

Response 201 Created

json
{
  "success": true,
  "data": {
    "campaign_id": "01JX...",
    "name": "Offer Reminder",
    "status": "pending",
    "message_type": "template",
    "total_count": 2,
    "sent_count": 0,
    "failed_count": 0,
    "scheduled_at": "2026-05-01T06:00:00Z",
    "created_at": "2026-04-19T10:00:00Z"
  }
}
FieldTypeDescription
data.campaign_idstringUnique campaign ULID
data.namestring|nullCampaign name
data.statusstringInitial status: pending
data.total_countintegerNumber of recipients
data.sent_countintegerSuccessfully sent (0 at creation)
data.failed_countintegerFailed deliveries (0 at creation)
data.scheduled_atstring|nullUTC scheduled datetime, or null for immediate
data.created_atstringUTC creation datetime

Get Campaign

GET /api/v1/campaigns/{id}

Path Parameters

ParameterDescription
idCampaign ULID returned from create

Example Request

bash
curl https://cubeconnect.io/api/v1/campaigns/01JX... \
  -H "Authorization: Bearer YOUR_API_KEY"
php
$response = Http::withToken('YOUR_API_KEY')
    ->get("https://cubeconnect.io/api/v1/campaigns/{$campaignId}");
javascript
const response = await fetch(`https://cubeconnect.io/api/v1/campaigns/${campaignId}`, {
  headers: { 'Authorization': 'Bearer YOUR_API_KEY' },
})

Response 200 OK

json
{
  "success": true,
  "data": {
    "campaign_id": "01JX...",
    "name": "Offer Reminder",
    "status": "processing",
    "message_type": "template",
    "total_count": 500,
    "sent_count": 320,
    "failed_count": 12,
    "scheduled_at": null,
    "created_at": "2026-04-19T10:00:00Z"
  }
}

Campaign Status Values

StatusDescription
pendingCreated, waiting to start (or waiting for scheduled time)
processingCurrently sending to recipients
completedAll recipients processed
cancelledCancelled before execution started
failedCampaign encountered a fatal error

Get Campaign Recipients

GET /api/v1/campaigns/{id}/recipients

Returns a paginated list of recipients with their individual delivery status. Use this to audit exactly which numbers were reached and which failed.

Path Parameters

ParameterDescription
idCampaign ULID returned from create

Query Parameters

ParameterTypeDefaultDescription
pageinteger1Page number
per_pageinteger50Results per page (max 100)
statusstringFilter by status: pending, sent, or failed

Example Request

bash
curl "https://cubeconnect.io/api/v1/campaigns/01JX.../recipients?per_page=100&status=failed" \
  -H "Authorization: Bearer YOUR_API_KEY"
php
$page = $cube->getCampaignRecipients(
    campaignId: '01JX...',
    page: 1,
    perPage: 100,
    status: 'failed',
);

foreach ($page->recipients as $r) {
    echo "{$r->phone}: {$r->status} — {$r->errorMessage}\n";
}

if ($page->hasMorePages()) {
    $nextPage = $cube->getCampaignRecipients('01JX...', $page->currentPage + 1, 100, 'failed');
}
typescript
const page = await cube.getCampaignRecipients('01JX...', {
  page: 1,
  perPage: 100,
  status: 'failed',
})

for (const r of page.recipients) {
  console.log(`${r.phone}: ${r.status} — ${r.errorMessage}`)
}

if (page.pagination.currentPage < page.pagination.lastPage) {
  const next = await cube.getCampaignRecipients('01JX...', { page: 2, perPage: 100, status: 'failed' })
}

Response 200 OK

json
{
  "success": true,
  "data": {
    "campaign_id": "01JX...",
    "recipients": [
      {
        "phone": "966501234567",
        "name": "Ahmed",
        "status": "sent",
        "message_log_id": "01JY...",
        "error_message": null,
        "sent_at": "2026-04-19T10:05:32Z"
      },
      {
        "phone": "966509876543",
        "name": "Sara",
        "status": "failed",
        "message_log_id": null,
        "error_message": "Invalid phone number",
        "sent_at": null
      }
    ],
    "pagination": {
      "current_page": 1,
      "per_page": 50,
      "total": 1000,
      "last_page": 20
    }
  }
}

Recipient Status Values

StatusDescription
pendingNot yet processed
sentMessage dispatched successfully
failedDelivery failed — see error_message for reason

Cancel Campaign

POST /api/v1/campaigns/{id}/cancel

Cancels a scheduled campaign. Only campaigns in pending status can be cancelled.

Example Request

bash
curl -X POST https://cubeconnect.io/api/v1/campaigns/01JX.../cancel \
  -H "Authorization: Bearer YOUR_API_KEY"
php
$response = Http::withToken('YOUR_API_KEY')
    ->post("https://cubeconnect.io/api/v1/campaigns/{$campaignId}/cancel");
javascript
const response = await fetch(`https://cubeconnect.io/api/v1/campaigns/${campaignId}/cancel`, {
  method: 'POST',
  headers: { 'Authorization': 'Bearer YOUR_API_KEY' },
})

Response 200 OK

json
{
  "success": true,
  "data": {
    "success": true
  }
}

Finding Your Account ID

The whatsapp_account_id is the ULID identifier for your WhatsApp Business account on CubeConnect.

To find it: Go to Dashboard → WhatsApp Numbers and click the copy icon next to API ID: on any connected number.


Webhooks

Track campaign progress in real time using webhook events:

EventTrigger
campaign.createdCampaign accepted
campaign.startedExecution begins
campaign.completedAll messages processed

CubeConnect WhatsApp Business Platform