SendAfrica logoSendAfricaDocs

Campaigns

Schedule bulk SMS campaigns against a contact list, track live per-recipient stats, cancel or delete, and understand the worker loop.


Campaigns are scheduled bulk sends linked to a contact list. A dedicated worker process polls for scheduled campaigns every 30 seconds and executes them with correct per-recipient credit accounting.

#Endpoints

MethodPathDescription
GET / POST/v1/campaigns/List (with live stats) / create & schedule
GET/v1/campaigns/{id}Detail with live stats
POST/v1/campaigns/{id}/cancelCancel a draft/scheduled campaign
DELETE/v1/campaigns/{id}Delete a campaign that never sent anything
GET/v1/campaigns/{id}/recipients?status=Per-recipient tracking

#Create a campaign

POST/v1/campaigns/JWT
bash
bash
curl -s -X POST https://api.sendafrica.online/v1/campaigns/ \
  -H "Authorization: Bearer $JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: promo-june-launch" \
  -d '{
    "name": "June Promo Blast",
    "message": "Mambo! Enjoy 20% off all weekend. Karibu!",
    "contact_list_id": 3,
    "scheduled_at": "2026-06-20T09:00:00Z",
    "sender_id": "MyBrand"
  }'

Register your sender ID first

sender_id must be registered and authorized — omit it to send from the platform default SendAfrika. Unregistered IDs are rejected by the carrier at send time and the message is re-sent under the platform fallback, so a whole campaign could silently lose your branding. See Sender IDs.

201 Created
json
{
  "success": true,
  "data": {
    "id": 42,
    "name": "June Promo Blast",
    "status": "scheduled",
    "recipients_count": 482,
    "estimated_credits": 482,
    "scheduled_at": "2026-06-20T09:00:00Z"
  }
}

Idempotent creation

Campaign creation supports Idempotency-Key. Double-clicking "Schedule" in a UI or retrying after a timeout will not create duplicates.

#Live stats

GET/v1/campaigns/{id}JWT
200 OK
json
{
  "success": true,
  "data": {
    "id": 42,
    "status": "processing",
    "total_recipients": 482,
    "sent": 310,
    "delivered": 295,
    "failed": 4,
    "pending": 168,
    "credits_spent": 310,
    "cost_tzs": 10850
  }
}

#Per-recipient tracking

GET/v1/campaigns/{id}/recipients?status=failedJWT

Filter by sent, delivered, failed, or pending to build delivery dashboards or build a retry list of failed numbers.

#How execution works

  1. The worker polls for scheduled campaigns every 30 s.
  2. It acquires a Redis distributed lock — already-running campaigns are skipped, so multiple workers are safe.
  3. The campaign is marked processing, then contacts are fetched from the linked list.
  4. Each recipient is sent individually via the single-send path, in chunks of 500 — intentional, so per-recipient credit accounting stays exact.
  5. The campaign is marked completed (or failed if its list was missing/empty), and the lock is released.

Cancellation window

Only draft or scheduled campaigns can be cancelled — once the worker flips it to processing, sends are underway. Delete is only allowed for campaigns that never sent anything.