Campaigns

Send one message to a contact group, several groups, or your whole contact book — created as a draft, dispatched when you say so, with live progress counters you can poll.

Campaigns live under /api/v1/ and authenticate with a scoped mbs_ token or JWT. A campaign targets contact groups (or an explicit phone list) and supports {{name}}-style personalization from your stored contact fields.

Create a campaign

POST/api/v1/campaigns

Parameters

FieldTypeRequiredDescription
namestringYesInternal label for the campaign — shows in lists and reports, never sent to recipients.
messagestringYesThe message body. May contain personalization fields like {{name}} filled from each contact.
source_typestringYesgroups (send to the groups in target_group_uuids), all (every contact in your team), or manual (the phones in target_phones).
target_group_uuidsstring[]NoGroup UUIDs to target — required when source_type is groups.
target_phonesstring[]NoExplicit recipient list — required when source_type is manual. Any accepted phone format.
sender_iduuidNoUUID of an approved sender ID. Omit to use your team's default sender.
skip_blacklistedbooleanNoSkip recipients who opted out via the blacklist. Strongly recommended for promotional sends.
send_typestringNoimmediate (default) or scheduled with a scheduled_at ISO-8601 timestamp.
scheduled_atRFC 3339 timestampNoWhen to dispatch a scheduled campaign, e.g. 2026-07-15T09:00:00+03:00.
name_fieldstringNoWhich contact field fills {{name}}: first_name (default) or full name.
curl -X POST https://api.mobilesasa.com/api/v1/campaigns \
  -H "Authorization: Bearer $MOBILESASA_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "July offer — Nairobi customers",
    "message": "Hi {{name}}, enjoy 20% off all orders this weekend. Dial *384# to redeem.",
    "source_type": "groups",
    "target_group_uuids": [
      "7d1a44f0-…"
    ],
    "sender_id": "9b2f6c1e-…",
    "skip_blacklisted": true
  }'
Response — 200
{
  "success": true,
  "data": {
    "uuid": "c4e8a90d-…",
    "name": "July offer — Nairobi customers",
    "status": "draft",
    "source_type": "groups",
    "message_parts": 1,
    "total_recipients": 0,
    "created_at": "2026-07-11T10:02:11Z"
  }
}

Draft first, send second

Creating a campaign never sends anything. It sits in draft until you call the send endpoint — giving you a chance to review the rendered message and recipient count.

Send it

POST/api/v1/campaigns/{uuid}/send

Sending resolves the audience, computes total message parts, and reserves the SMS units from your balance up front— if the balance can't cover the whole campaign the call fails with an insufficient-balance error and nothing is sent. A scheduled campaign is queued for its scheduled_at time instead of dispatching immediately.

curl -X POST https://api.mobilesasa.com/api/v1/campaigns/c4e8a90d-…/send \
  -H "Authorization: Bearer $MOBILESASA_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{}'

Track progress

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

Poll the campaign for live counters; the recipients endpoint pages through per-recipient delivery state.

curl "https://api.mobilesasa.com/api/v1/campaigns/c4e8a90d-…" \
  -H "Authorization: Bearer $MOBILESASA_TOKEN"
Response — 200 (sending)
{
  "success": true,
  "data": {
    "uuid": "c4e8a90d-…",
    "status": "sending",
    "total_recipients": 4210,
    "total_sent": 3900,
    "total_delivered": 3711,
    "total_failed": 12,
    "total_blacklisted_skipped": 38,
    "total_parts_sent": 3900,
    "started_at": "2026-07-11T10:05:00Z"
  }
}

Campaign statuses

FieldTypeRequiredDescription
draftinitialNoCreated but not sent. Editable (PUT) and deletable.
queuedin flightNoAccepted for dispatch (or waiting for its scheduled time).
sendingin flightNoWorkers are fanning messages out; counters update live.
completedfinalNoAll recipients processed. total_failed tells you how many didn't go out.
failedfinalNoThe campaign as a whole failed — failure_reason says why (e.g. insufficient balance, no sender configured).
cancelledfinalNoStopped via the cancel endpoint before finishing.

Cancel

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

Cancels a draft or queued campaign — handy for calling off a scheduled send. Cancelling a queued campaign releases its reserved units back to your balance. Once a campaign is sending it can no longer be cancelled.

Resend

POST/api/v1/campaigns/{uuid}/resend

Resending a completed, failed or cancelled campaign clones it into a new campaign (new UUID) and dispatches the clone immediately — the original and its stats stay untouched. The response is the new campaign; store its UUID for tracking.

curl -X POST https://api.mobilesasa.com/api/v1/campaigns/c4e8a90d-…/resend \
  -H "Authorization: Bearer $MOBILESASA_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{}'

Resend re-bills

A resend targets the audience as it stands now (groups may have grown) and reserves units again at full price. Double-check you aren't about to message the same customers twice by accident.

List campaigns

GET/api/v1/campaigns
curl "https://api.mobilesasa.com/api/v1/campaigns?page=1&page_size=20" \
  -H "Authorization: Bearer $MOBILESASA_TOKEN"