Skip to main content
SMS campaigns send the same message (or templated variations) to many recipients in one API call. Useful for promotions, appointment batches, announcements, and reminders.
Campaigns are one-time broadcasts. They send the message and stop. If a recipient replies, the reply is handled by your agent using its system_prompt — not by the campaign itself.

How replies work

Replies to a campaign SMS are handled by the agent’s system_prompt — the same system that handles all inbound SMS. Mention the campaign context in your agent prompt before launching so the agent knows what’s going on. For example:
You are the receptionist at Acme Dental. We are running a March promotion: 20% off cleanings, valid weekdays 9am-5pm next week. If a customer replies YES, confirm the offer and ask which day suits them.
When someone replies “YES” — or texts asking about the offer — the agent already knows what to say. For the full reply behaviour (caller history, variables, conversation context), see SMS replies.

Create a campaign (API)

curl -X POST https://api.nixflex.com/v1/sms/campaigns \
  -H "Authorization: Bearer KEY_ID:KEY_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "agent_id": "agent_125207e452f8714a",
    "from_number": "+447446466847",
    "name": "March promotion",
    "message_template": "Hi {{first_name}}, Acme Dental offering 20% off cleanings this month. Reply YES to book.",
    "recipients": [
      { "phone": "+447111000001", "variables": { "first_name": "Sarah" } },
      { "phone": "+447111000002", "variables": { "first_name": "James" } }
    ],
    "schedule_type": "now"
  }'
Response:
{
  "campaign_id": "smsc_a1b2c3d4",
  "status": "running",
  "total_count": 2,
  "delivered_count": 0,
  "failed_count": 0,
  "pending_count": 2,
  "source": "api"
}

Personalization with placeholders

Use {{variable_name}} in your message_template. Each recipient’s variables object fills the placeholders. Template: Recipient:
{
  "phone": "+447111000001",
  "variables": {
    "first_name": "Sarah",
    "day": "Tuesday",
    "time": "2pm"
  }
}
Sent SMS: Hi Sarah, your appointment is on Tuesday at 2pm. If a placeholder has no matching variable, it renders as an empty string (no crash). Same pattern as Twilio, SendGrid, and Mailchimp.

From the dashboard

In the dashboard, Outbound → SMS Campaigns → New Campaign, you can add recipients two ways:
  • Paste numbers: drop in a list separated by commas, tabs, spaces, or newlines. The form auto-detects phones (E.164 format) and any extra columns become variables.
  • Upload CSV: drag-and-drop or browse. The form auto-detects the phone column from headers (phone, number, mobile, tel, telephone) and treats all other columns as variables.
Either way you get a preview table before sending.

Scheduling

Send later instead of now:
{
  "schedule_type": "schedule",
  "scheduled_at": "2026-05-20T09:00:00Z"
}
Campaign sits at status: scheduled until the time arrives. You can cancel it before then with DELETE /v1/sms/campaigns/:id.

Tracking delivery

Get campaign status:
curl https://api.nixflex.com/v1/sms/campaigns/smsc_a1b2c3d4 \
  -H "Authorization: Bearer KEY_ID:KEY_SECRET"
Response:
{
  "campaign_id": "smsc_a1b2c3d4",
  "name": "March promotion",
  "status": "done",
  "total_count": 100,
  "delivered_count": 95,
  "failed_count": 3,
  "pending_count": 2,
  "source": "dashboard",
  "recipients": [
    {
      "phone": "+447111000001",
      "variables": { "first_name": "Sarah" },
      "status": "delivered",
      "twilio_sid": "SM1234567890"
    }
  ]
}

How counts are calculated

Counts come from per-recipient status, not from a cached field. Same logic in the dashboard and the API — they always match.
CountCalculation
delivered_countRecipients with status delivered
failed_countRecipients with status failed, undelivered, or sent
pending_countRecipients with status pending or queued
total_countAll recipients
Why does sent count as failed? Twilio’s sent status only means Twilio handed the message off to the carrier — not that it reached the handset. Carrier confirmation comes as delivered. We treat anything that did not reach delivered as failed, so what you see in the dashboard matches what really happened.

Campaign status

StatusMeaning
draftCreated but not launched
scheduledWill launch at scheduled_at
runningCurrently sending
doneAll recipients processed
failedAll recipients failed at submission

Per-recipient status

StatusMeaningShown in UI as
pendingJust queued by NixflexPending
queuedSent to Twilio APIPending
sentTwilio handed to carrier (no delivery confirmation)Failed
deliveredCarrier confirmed delivery to handsetDelivered
undeliveredCarrier rejectedFailed
failedTwilio itself failedFailed

List campaigns

curl https://api.nixflex.com/v1/sms/campaigns \
  -H "Authorization: Bearer KEY_ID:KEY_SECRET"
Returns all campaigns for your account, newest first. Each one includes computed counts.

Cancel a campaign

curl -X DELETE https://api.nixflex.com/v1/sms/campaigns/smsc_a1b2c3d4 \
  -H "Authorization: Bearer KEY_ID:KEY_SECRET"
Cancels a scheduled or running campaign. Already-sent messages are not recalled. A done campaign cannot be cancelled.

Source tracking

Every campaign records where it came from in the source field: "dashboard" or "api". The dashboard shows this as a badge so you can tell at a glance which campaigns came from automation versus manual sends.

Rate limiting

Twilio rate-limits SMS at ~1 message per second per number by default. The engine paces sends to respect this. A 1,000-recipient campaign takes roughly 20 minutes to send fully. Need faster? Request a higher MPS limit from Twilio support, or add more sending numbers.

Compliance

Bulk SMS has stricter rules than one-off messages.
  • UK: All recipients must have opted in. Honour STOP keywords. Keep records of consent.
  • US: Requires A2P 10DLC registration. Unregistered traffic is blocked by carriers.
Sending without consent risks carrier blocking, fines, and your sending number being flagged as spam.

Costs

Same per-message Twilio billing as one-off SMS. No platform markup. Budget accordingly for large campaigns.