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 *657# 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"