> ## Documentation Index
> Fetch the complete documentation index at: https://www.vobiz.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Vobiz Messaging Broadcasts

> Send bulk SMS with Vobiz broadcasts - create a broadcast with a personalised template, upload recipients from CSV, XLSX or JSON, preview segments, launch at a controlled rate, pause, resume or cancel, track every recipient and export the results.

A broadcast sends one SMS template to a list of recipients, personalised per row, at a rate you choose. You build it in four calls - create, upload recipients, preview, launch - then watch the counters move and export the outcome per recipient. The console offers the same flow under **SMS & RCS → Broadcasts**.

## How a broadcast runs

<Steps>
  <Step title="Create a draft">
    Name it, pick the sending number and write the template.
  </Step>

  <Step title="Upload recipients">
    A CSV, XLSX or JSON list. Extra columns become template variables.
  </Step>

  <Step title="Preview">
    See rendered samples, segment counts and any variables the file is missing.
  </Step>

  <Step title="Launch">
    Send now or at `scheduledAt`. Pause, resume or cancel while it runs.
  </Step>

  <Step title="Track and export">
    Per-recipient statuses, live stats, and a CSV of the results.
  </Step>
</Steps>

<Warning>
  Launching a broadcast sends real messages to every pending recipient and bills each one per segment. Run the launch call only with your own credentials, on a list you have consent to message.
</Warning>

## 1. Create a broadcast

[Create a broadcast](/docs/sms/api/broadcasts/create-broadcast) returns a draft:

```bash cURL theme={null}
curl -X POST "https://api.vobiz.ai/api/v1/sms/broadcasts" \
  -H "X-Auth-ID: MA_XXXXXXXX" \
  -H "X-Auth-Token: YOUR_AUTH_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "October price update",
    "from": "+14155550100",
    "channel": "sms",
    "template": "Hi {{name}}, your new plan price is {{price}} from 1 November. Reply STOP to opt out.",
    "ratePerSecond": 5,
    "tag": "price-update-oct"
  }'
```

| Field | Required | Notes |
| - | - | - |
| `name` | yes | Up to 255 characters. |
| `from` | yes | A phone number on your account (E.164), or an alphanumeric sender ID of up to 11 letters, digits and spaces where the destination allows it. |
| `channel` | no | `sms` (the default and the only value for broadcasts). |
| `template` | no at create, yes at launch | Up to 5000 characters. Placeholders are `{{variable}}`; names are case-insensitive and surrounding spaces are ignored. |
| `scheduledAt` | no | RFC 3339 timestamp. Set it to start the broadcast later; `null` clears it. |
| `ratePerSecond` | no | Messages per second, 1-50. Default 5. |
| `tag` | no | Stored on every message the broadcast sends, so you can filter [List messages](/docs/sms/api/messages/list-messages) by it. |
| `dltEntityId`, `dltHeaderId`, `dltTemplateId` | no | The IDs you registered on a TRAI DLT portal; set them on broadcasts to Indian numbers. See [India DLT](/docs/sms/compliance/india-dlt). |

```json Response - 201 Created theme={null}
{
  "id": "9b2e1c40-8a3d-4f5e-9c1a-7d6b2e8f4a01",
  "accountId": "MA_XXXXXXXX",
  "name": "October price update",
  "from": "+14155550100",
  "channel": "sms",
  "template": "Hi {{name}}, your new plan price is {{price}} from 1 November. Reply STOP to opt out.",
  "status": "draft",
  "scheduledAt": null,
  "ratePerSecond": 5,
  "tag": "price-update-oct",
  "dltEntityId": "",
  "dltHeaderId": "",
  "dltTemplateId": "",
  "columns": null,
  "stats": { "total": 0, "pending": 0, "sent": 0, "delivered": 0, "failed": 0, "optedOut": 0, "skipped": 0 },
  "createdAt": "2026-10-18T09:00:00.000000Z",
  "updatedAt": "2026-10-18T09:00:00.000000Z",
  "startedAt": null,
  "completedAt": null
}
```

Edit a draft with [Update a broadcast](/docs/sms/api/broadcasts/update-broadcast) (same fields, partial). Once launched, the broadcast is read-only and an edit returns `409 {"error":"cannot edit a broadcast that is running"}`.

## 2. Upload recipients

[Upload recipients](/docs/sms/api/broadcasts/upload-recipients) replaces the draft's recipient list. Two body formats are accepted.

<Tabs>
  <Tab title="CSV or XLSX file">
    Send `multipart/form-data` with a `file` field (`.csv` or `.xlsx`, up to 10 MB) and an optional `defaultCountry`:

    ```bash cURL theme={null}
    curl -X POST "https://api.vobiz.ai/api/v1/sms/broadcasts/9b2e1c40-8a3d-4f5e-9c1a-7d6b2e8f4a01/recipients" \
      -H "X-Auth-ID: MA_XXXXXXXX" \
      -H "X-Auth-Token: YOUR_AUTH_TOKEN" \
      -F "file=@recipients.csv" \
      -F "defaultCountry=US"
    ```

    ```csv recipients.csv theme={null}
    phone,name,price
    +14155550123,Ana,$29
    4155550124,Ben,$35
    ```

    * The phone column is detected by header (`phone`, `mobile`, `msisdn`, `number`, `to`, `phonenumber` and similar). Without a match the first column is used.
    * A file with no header row gets columns named `phone`, `column2`, `column3`, ...
    * The delimiter (`,`, `;` or tab) is sniffed. For XLSX the first sheet is read.
    * Every other column becomes a template variable with the header as its name.
  </Tab>

  <Tab title="JSON list">
    Send a JSON body when you only have numbers and no variables:

    ```bash cURL theme={null}
    curl -X POST "https://api.vobiz.ai/api/v1/sms/broadcasts/9b2e1c40-8a3d-4f5e-9c1a-7d6b2e8f4a01/recipients" \
      -H "X-Auth-ID: MA_XXXXXXXX" \
      -H "X-Auth-Token: YOUR_AUTH_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{ "numbers": ["+14155550123", "4155550124"], "defaultCountry": "US" }'
    ```
  </Tab>
</Tabs>

`defaultCountry` (default `US`) is the ISO 3166-1 alpha-2 country used to turn national-format numbers into E.164. Numbers that already start with `+` are kept as they are.

<Accordion title="Supported defaultCountry values">
  `US`, `CA`, `GB`, `IN`, `AU`, `NZ`, `IE`, `DE`, `FR`, `ES`, `IT`, `NL`, `BE`, `CH`, `AT`, `SE`, `NO`, `DK`, `FI`, `PL`, `PT`, `BR`, `MX`, `AR`, `CO`, `CL`, `SG`, `MY`, `ID`, `PH`, `TH`, `VN`, `JP`, `KR`, `CN`, `HK`, `AE`, `SA`, `QA`, `IL`, `TR`, `ZA`, `NG`, `KE`, `EG`, `PK`, `BD`, `LK`, `NP`. Any other value returns `400 {"error":"defaultCountry must be a supported ISO 3166-1 alpha-2 code"}`.
</Accordion>

The response summarises what was accepted:

```json Response - 200 OK theme={null}
{
  "total": 1250,
  "valid": 1231,
  "invalid": [
    { "row": 7, "value": "abc", "reason": "contains letters or symbols" },
    { "row": 42, "value": "41555", "reason": "too short" }
  ],
  "invalidCount": 2,
  "duplicates": 14,
  "optedOut": 3,
  "columns": ["phone", "name", "price"]
}
```

| Field | Meaning |
| - | - |
| `total` | Rows read from the file or list. |
| `valid` | Recipients stored and ready to send (`pending`). |
| `invalid` | Up to 100 rejected rows with `row`, `value` and `reason`: `missing phone number`, `contains letters or symbols`, `too short`, `too long`, `not a valid US/Canada number`, `not a valid Indian mobile number`, `unsupported defaultCountry`. |
| `invalidCount` | Total rejected rows, including those beyond the first 100. |
| `duplicates` | Rows dropped because the number already appeared. |
| `optedOut` | Rows stored with status `opted_out` because the number has an [opt-out](/docs/sms/opt-outs) on your account. They are never sent. |
| `columns` | The variable names now available to the template. |

Limits and errors: up to 10,000 recipients per broadcast (`422 too many recipients: the limit is 10000 per broadcast`), files over 10 MB return `413`, other extensions return `400 file must be .csv or .xlsx`, and uploads to a broadcast that is no longer a draft return `409`.

## 3. Preview

[Preview a broadcast](/docs/sms/api/broadcasts/preview-broadcast) renders the template against the first recipients:

```bash cURL theme={null}
curl "https://api.vobiz.ai/api/v1/sms/broadcasts/9b2e1c40-8a3d-4f5e-9c1a-7d6b2e8f4a01/preview?limit=2" \
  -H "X-Auth-ID: MA_XXXXXXXX" \
  -H "X-Auth-Token: YOUR_AUTH_TOKEN"
```

```json Response - 200 OK theme={null}
{
  "samples": [
    { "to": "+14155550123", "text": "Hi Ana, your new plan price is $29 from 1 November. Reply STOP to opt out.", "segments": 1, "encoding": "GSM-7" },
    { "to": "+14155550124", "text": "Hi Ben, your new plan price is $35 from 1 November. Reply STOP to opt out.", "segments": 1, "encoding": "GSM-7" }
  ],
  "missingVariables": [],
  "estimatedSegments": 1231
}
```

* `samples` shows the rendered `text`, its `segments` and `encoding` (`GSM-7` or `UCS-2`) for each of the first `limit` recipients (default 5, maximum 50). See [segments and encoding](/docs/sms/channels/sms#segments-and-encoding).
* `missingVariables` lists placeholders in the template that no uploaded column provides. Fix the template or re-upload before you launch.
* `estimatedSegments` is the total segments across all pending recipients - multiply by your per-segment rate for the expected cost.

## 4. Launch

[Launch a broadcast](/docs/sms/api/broadcasts/launch-broadcast) starts sending. Only a `draft` can be launched.

```bash cURL theme={null}
curl -X POST "https://api.vobiz.ai/api/v1/sms/broadcasts/9b2e1c40-8a3d-4f5e-9c1a-7d6b2e8f4a01/launch" \
  -H "X-Auth-ID: MA_XXXXXXXX" \
  -H "X-Auth-Token: YOUR_AUTH_TOKEN"
```

The response is the broadcast object. Its `status` is `running` when sending starts at once, or `scheduled` when `scheduledAt` is in the future - it moves to `running` at that time.

Launch checks return `422` with one of:

| Error | Fix |
| - | - |
| `template is empty` | Set a `template` with [Update a broadcast](/docs/sms/api/broadcasts/update-broadcast). |
| `the broadcast has no valid recipients to send to` | Upload a list with at least one `pending` recipient. |
| `+14155550100 is not an SMS number on this account` | Use a number from [List your numbers](/docs/sms/api/numbers/list-numbers) as `from`. |
| `no SMS route for sender X` | The alphanumeric sender has no route; use a phone number or contact support. |

Each recipient is sent through the same path as [Send a message](/docs/sms/api/messages/send-message) with the broadcast's `tag` and DLT IDs, at `ratePerSecond`. Every send carries an idempotency key of `broadcast:<broadcastId>:<recipientId>`, so a retried worker never double-sends.

## Pause, resume, cancel and delete

| Action | Allowed from | Result |
| - | - | - |
| [Launch](/docs/sms/api/broadcasts/launch-broadcast) | `draft` | `scheduled` or `running` |
| [Pause](/docs/sms/api/broadcasts/pause-broadcast) | `running` | `paused` - sending stops after in-flight messages |
| [Resume](/docs/sms/api/broadcasts/resume-broadcast) | `paused` | `running` - also clears a `pauseReason` of `insufficient_balance` once you have topped up |
| [Cancel](/docs/sms/api/broadcasts/cancel-broadcast) | `draft`, `scheduled`, `running`, `paused` | `cancelled` - every `pending` recipient becomes `skipped` |
| [Delete](/docs/sms/api/broadcasts/delete-broadcast) | `draft`, `cancelled` | `204 No Content` |

Any other transition returns `409 {"error":"cannot <action> a broadcast that is <status>"}`. An unknown or malformed id returns `404 {"error":"broadcast not found"}`.

The worker moves a `running` broadcast to `completed` when every recipient has been processed, or to `failed` if the run cannot continue. When a send is refused for insufficient balance, the broadcast is paused with `pauseReason: "insufficient_balance"`; top up and call resume.

### Broadcast statuses

`draft`, `scheduled`, `running`, `paused`, `completed`, `cancelled`, `failed`.

## Track recipients and stats

[Get a broadcast](/docs/sms/api/broadcasts/get-broadcast) returns fresh `stats`:

```json theme={null}
"stats": { "total": 1234, "pending": 310, "sent": 880, "delivered": 612, "failed": 41, "optedOut": 3, "skipped": 0 }
```

[List recipients](/docs/sms/api/broadcasts/list-recipients) pages through individual rows (`limit` 1-500, default 50; `offset`) and filters by `status`:

| Recipient `status` | Meaning |
| - | - |
| `pending` | Waiting to be sent. |
| `sending` | Picked up by the worker. |
| `sent` | Accepted by the carriers; `messageId` links to the message. |
| `delivered` | A delivery receipt arrived. |
| `failed` | Rejected; see `errorCode` (`send_failed`, `routing_failed`, or the message's own code) and `errorMessage`. |
| `opted_out` | The number has an opt-out on your account. Never sent. |
| `skipped` | Was `pending` when the broadcast was cancelled. |

Each recipient carries `row`, `to`, `variables` (the uploaded columns), `messageId`, `errorCode`, `errorMessage` and `sentAt`. Open the message itself with [Retrieve a message](/docs/sms/api/messages/get-message) for its full status history.

### Live updates

Connect to the [WebSocket](/docs/sms/websocket) and listen for `broadcast.updated`. The event carries the full broadcast object with `stats` on every status change and roughly every 2 seconds while the broadcast is running - enough to drive a progress bar without polling.

## Export the results

[Export a broadcast](/docs/sms/api/broadcasts/export-broadcast) streams a CSV (`text/csv`, `Content-Disposition: attachment; filename="broadcast-<id>.csv"`):

```bash cURL theme={null}
curl -o broadcast.csv "https://api.vobiz.ai/api/v1/sms/broadcasts/9b2e1c40-8a3d-4f5e-9c1a-7d6b2e8f4a01/export" \
  -H "X-Auth-ID: MA_XXXXXXXX" \
  -H "X-Auth-Token: YOUR_AUTH_TOKEN"
```

Columns: `row`, `to`, `status`, `message_id`, `error_code`, `error_message`, `sent_at`, followed by one column per uploaded variable. Cells that begin with `=`, `+`, `-`, `@` or a tab are prefixed with `'` so spreadsheets do not treat them as formulas.

## Listing broadcasts

[List broadcasts](/docs/sms/api/broadcasts/list-broadcasts) returns `{"broadcasts": [...], "total": n}`, newest first, with `limit` 1-500 (default 50) and `offset`.

## Before you go live

<CardGroup cols={2}>
  <Card title="Compliance overview" icon="shield-check" href="/docs/sms/compliance/overview">
    Register before you send at volume.
  </Card>

  <Card title="Messaging numbers" icon="hashtag" href="/docs/sms/numbers">
    Pick a registered sender for the broadcast.
  </Card>
</CardGroup>

## Related

<CardGroup cols={2}>
  <Card title="Opt-outs" icon="ban" href="/docs/sms/opt-outs">
    How opted-out numbers are excluded from every broadcast.
  </Card>

  <Card title="SMS" icon="message" href="/docs/sms/channels/sms">
    Segments, encoding and statuses behind each recipient.
  </Card>

  <Card title="India DLT" icon="file-shield" href="/docs/sms/compliance/india-dlt">
    DLT IDs for broadcasts to Indian numbers.
  </Card>

  <Card title="WebSocket" icon="bolt" href="/docs/sms/websocket">
    Live `broadcast.updated` events.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.