Campaigns
Overview
Outreach Campaigns let you send a sequence of messages to a list of contacts over multiple days, with built-in daily rate limiting that prevents your sending number from being flagged or blocked. Each campaign draws contacts from explicit IDs, contact lists, segments, or a combination.
GET /account/subscription.Campaign lifecycle
draft → running → paused → running → completed
↘ cancelled
A campaign must be explicitly started after creation. It can be paused and resumed at any time. Once all contacts have been messaged, the status moves to completed automatically.
The Campaign object
{
"id": 12,
"name": "Spring Promo 2026",
"status": "running",
"total_contacts": 250,
"success_count": 87,
"failed_count": 3,
"campaign_day": 4,
"messages_sent_today": 65,
"is_new_number": false,
"started_at": "2026-03-05T08:00:00.000000Z",
"completed_at": null,
"progress_percentage": 36,
"daily_limit": 65,
"remaining_today": 0,
"client_setting": { "id": 5, "name": "Business WhatsApp", "type": "whatsapp" },
"campaign_contacts_count": 250,
"sent_contacts_count": 87,
"failed_contacts_count": 3,
"pending_contacts_count": 160,
"created_at": "2026-03-04T10:00:00.000000Z"
}
| Field | Type | Description |
|---|---|---|
status | string | draft, running, paused, completed, cancelled |
campaign_day | integer | How many days the campaign has been running |
progress_percentage | integer | 0–100, based on contacted vs total |
daily_limit | integer | Max messages the system will send today |
remaining_today | integer | Remaining sends for today |
is_new_number | boolean | Conservative warm-up mode for new phone numbers |
List campaigns
GET /api/v1/campaigns
| Parameter | Type | Description |
|---|---|---|
status | string | draft, running, paused, completed, cancelled |
per_page | integer | Max 100 (default 25) |
Create a campaign
POST /api/v1/campaigns
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
name | string | ✓ | |
channel_id | integer | ✓ | ClientSetting ID — get from GET /account/channels |
messages | array | ✓ | Sequence of message objects |
messages[].type | string | ✓ | text, image, document, video, audio |
messages[].content | string | ✓ | Message text or media URL |
is_new_number | boolean | No | Use conservative warm-up daily limits (default: false) |
contact_ids | array | No* | Explicit contact IDs to target |
list_ids | array | No* | Pull contacts from these contact lists |
segment_ids | array | No* | Pull contacts from these segments |
*At least one of contact_ids, list_ids, or segment_ids is required.
curl -X POST https://dashboard.skylightchat.com/api/v1/campaigns \
-H "X-Api-Key: ••••••••••••" \
-H "Content-Type: application/json" \
-d '{
"name": "Spring Promo 2026",
"channel_id": 5,
"messages": [
{ "type": "text", "content": "Hi! We have a special offer just for you 🎉" },
{ "type": "text", "content": "Reply YES to learn more, or STOP to unsubscribe." }
],
"list_ids": [3, 7],
"is_new_number": false
}'
Returns 201 Created. The campaign starts in draft status — call /start to begin sending.
Daily limits
Daily message limits scale automatically based on campaign_day and is_new_number to protect your sending reputation:
| Day | Normal number | New number |
|---|---|---|
| 1 | up to 35 | up to 20 |
| 3 | up to 65 | up to 40 |
| 5 | up to 65 | up to 60 |
| 7+ | up to 120 | up to 90 |
| 14+ | up to 200 | up to 200 |
For small campaigns, the limit is capped at ⌈total_contacts / 3⌉ to finish in ~3 days.
Get a campaign
GET /api/v1/campaigns/{id}
Update a campaign
PUT /api/v1/campaigns/{id}
Only draft campaigns can be updated. You can change name, messages, and is_new_number.
Start a campaign
POST /api/v1/campaigns/{id}/start
Transitions the campaign from draft to running. Sending begins on the next background job cycle.
Pause a campaign
POST /api/v1/campaigns/{id}/pause
Stops sending immediately. The campaign can be resumed at any time without losing progress.
Resume a campaign
POST /api/v1/campaigns/{id}/resume
Transitions from paused back to running.
Cancel a campaign
POST /api/v1/campaigns/{id}/cancel
Permanently stops the campaign. Cancelled campaigns cannot be restarted. Returns the updated campaign object.
Delete a campaign
DELETE /api/v1/campaigns/{id}
Only draft or cancelled campaigns can be deleted. Returns 204 No Content.
List campaign contacts
GET /api/v1/campaigns/{id}/contacts
Returns the enrolled contacts and their delivery status.
| Parameter | Type | Description |
|---|---|---|
status | string | pending, sent, failed |
per_page | integer | Max 100 |
{
"success": true,
"data": [
{
"id": 1,
"status": "sent",
"phone": "+966501234567",
"sent_at": "2026-03-05T09:14:00.000000Z",
"error": null,
"contact": { "id": 42, "name": "Noura Al-Otaibi", "type": "whatsapp" }
}
]
}
