---
title: "SMS API"
description: "Send single and bulk SMS, choose TEXT or UNICODE encoding, count segments, schedule, control bulk jobs and look up numbers before you send."
canonical: "https://omnichannels.ai/developers/sms/"
language: "en"
updated: "2026-10-10"
publisher: "BroadNet Technologies"
brand: "OmniChannels.ai"
section: "Messaging channels"
---

# SMS API

Canonical: https://omnichannels.ai/developers/sms/
Language: en
Publisher: OmniChannels.ai by BroadNet Technologies

The SMS API sends one message with a JSON request, or many messages from an uploaded file, over direct operator connectivity. This page covers single sends, encoding and segments, bulk jobs and number lookup.

## Endpoints at a glance

| Method | Path | Use it to |
| --- | --- | --- |
| POST | `/sms/sendSms` | Send one SMS (JSON). Returns the message ID. |
| POST | `/sms/identify` | Detect encoding and count characters and segments. |
| POST | `/sms/sendbulk` | Send one message to a file of numbers (multipart). |
| GET | `/sms/list` | List bulk jobs with processed and pending counts. |
| GET | `/sms/edit` | Read the details of one bulk job. |
| GET | `/sms/pause` | Pause a running bulk job. |
| GET | `/sms/resume` | Resume a paused bulk job. |
| DELETE | `/sms/abort` | Abort a bulk job; pending messages are not sent. |
| GET | `/sms/listSchedule` | List scheduled campaigns. |
| POST | `/sms/send-now` | Release a scheduled campaign immediately. |
| DELETE | `/sms/abort/schedule` | Cancel a scheduled campaign. |
| POST | `/sms/lookup` | Check a number's status (HLR / MNP) before sending. |

## Send a single SMS

`POST /sms/sendSms` takes a JSON body and returns `200 OK` with the message ID as plain text.

### Request fields

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `senderId` | string | Yes | Your approved sender ID, shown to the recipient. |
| `destinationNumber` | string | Yes | Country code plus number, digits only. No `+`, `00`, spaces or leading zero. |
| `message` | string | Yes | The message text. |
| `messageType` | string | Yes | `TEXT` for GSM-7 text, `UNICODE` for Arabic, emoji and other scripts. |
| `charCount` | integer | No | Character count, as returned by `/sms/identify`. |
| `smsParts` | integer | No | Number of segments, as returned by `/sms/identify`. |
| `charLimit` | integer | No | Characters per segment, as returned by `/sms/identify`. |
| `expiryHour` | integer | No | Validity period in hours. |
| `campaignName` | string | No | Your label, used in reporting. |
| `isSchedule` | boolean | No | `true` to schedule instead of sending now. |
| `time` | — | No | Scheduled send time (used with `isSchedule`). |
| `gmt` | string | No | GMT offset for the scheduled time, for example `+04:00`. |
| `repeat` | — | No | Repeat setting for scheduled sends. |
| `delay` | number | No | Delay before sending. |
| `peId` | string | No | Principal entity ID, where a regulator requires one. |
| `templateId` | string | No | Registered content template ID, where a regulator requires one. |
| `telemarketerId` | string | No | Telemarketer ID, where a regulator requires one. |

Use exactly these field names. Fields such as `to`, `from` or `text` are not part of this endpoint. Leave the scheduling fields out to send immediately; for scheduled campaigns, the bulk endpoints below are the better fit, and our team can confirm the expected time format for your account.

### Example

```bash title="cURL"
curl -X POST https://api.broadnet.me/sms/sendSms \
  -H "Authorization: Bearer $BROADNET_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "senderId": "OmniDemo",
    "destinationNumber": "971500000000",
    "message": "رمز التحقق الخاص بك هو 482913",
    "messageType": "UNICODE",
    "campaignName": "login-otp"
  }'
```

```js title="Node.js"
const res = await fetch("https://api.broadnet.me/sms/sendSms", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.BROADNET_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    senderId: "OmniDemo",
    destinationNumber: "971500000000",
    message: "رمز التحقق الخاص بك هو 482913",
    messageType: "UNICODE",
    campaignName: "login-otp",
  }),
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
const messageId = await res.text();
```

```python title="Python"
import os
import requests

r = requests.post(
    "https://api.broadnet.me/sms/sendSms",
    headers={"Authorization": f"Bearer {os.environ['BROADNET_TOKEN']}"},
    json={
        "senderId": "OmniDemo",
        "destinationNumber": "971500000000",
        "message": "رمز التحقق الخاص بك هو 482913",
        "messageType": "UNICODE",
        "campaignName": "login-otp",
    },
    timeout=15,
)
r.raise_for_status()
message_id = r.text
```

```php title="PHP"
<?php
$ch = curl_init("https://api.broadnet.me/sms/sendSms");
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer " . getenv("BROADNET_TOKEN"),
        "Content-Type: application/json",
    ],
    CURLOPT_POSTFIELDS => json_encode([
        "senderId" => "OmniDemo",
        "destinationNumber" => "971500000000",
        "message" => "رمز التحقق الخاص بك هو 482913",
        "messageType" => "UNICODE",
        "campaignName" => "login-otp",
    ], JSON_UNESCAPED_UNICODE),
]);
$messageId = curl_exec($ch);
curl_close($ch);
```

The response body is the message ID:

```text title="Response"
2603121407537573731
```

### Phone number format

| Input | Valid? | Why |
| --- | --- | --- |
| `447700900123` | Yes | Country code 44 plus the national number, digits only. |
| `+44 7700 900123` | No | Remove the `+` and spaces first. |
| `07700900123` | No | National format: replace the leading 0 with the country code. |

Store numbers in E.164 (`+447700900123`) in your own database and strip the `+` when you build the request.

## Encoding and segments

`messageType` sets the encoding, and the encoding sets how many characters fit in each SMS segment. Long messages are split into segments and reassembled on the handset; most operators bill per segment.

| `messageType` | Single SMS | Per segment when split | Use for |
| --- | --- | --- | --- |
| `TEXT` | 160 characters | 153 characters | Text that uses only the GSM-7 alphabet. |
| `UNICODE` | 70 characters | 67 characters | Arabic, Cyrillic, Chinese, emoji, and characters outside GSM-7. |

A single non-GSM-7 character switches the whole message to Unicode. Common culprits are curly quotes, the em dash, the ellipsis character and emoji.

### Let the API count for you

`POST /sms/identify` analyses a message and returns its encoding, character count, segment count and per-segment limit. You can pass the values straight into a send request.

```bash title="cURL"
curl -X POST https://api.broadnet.me/sms/identify \
  -H "Authorization: Bearer $BROADNET_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "message": "Your OmniDemo code is 482913" }'
```

The response describes the message:

```json title="Response"
{
  "message": "Your OmniDemo code is 482913",
  "messageType": "TEXT",
  "smscount": 1,
  "charCount": 28,
  "charLimit": 160
}
```

| Response field | Description |
| --- | --- |
| `messageType` | Detected encoding. GSM-7 text may be labelled `GSM-7`; send it as `TEXT`. |
| `smscount` | Number of segments needed. Map it to `smsParts` in a send request. |
| `charCount` | Total characters. |
| `charLimit` | Characters per segment: 160, 153, 70 or 67. |

## Send in bulk

`POST /sms/sendbulk` sends one message to every number in an uploaded file. It is a `multipart/form-data` request with two parts:

| Part | Type | Required | Description |
| --- | --- | --- | --- |
| `destinationNumberFile` | file | Yes | CSV or TXT file of numbers, one per line, in the same format as `destinationNumber`. |
| `bulkRequest` | string (JSON) | Yes | The message settings, as a JSON string. |

Fields inside `bulkRequest`:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `senderId` | string | Yes | Your approved sender ID. |
| `message` | string | Yes | The message text. |
| `messageType` | string | Yes | `TEXT` or `UNICODE`. |
| `campaignName` | string | No | Your label for reporting. |
| `allowDuplicate` | boolean | No | `false` removes duplicate numbers from the file. |
| `expiryHour` | integer | No | Validity period in hours. |
| `delay` | number | No | Throttle between messages. |
| `schedule` | boolean | No | `true` to schedule the job. |
| `smsParts`, `charCount`, `charLimit` | integer | No | Values from `/sms/identify`. |
| `peId`, `templateId`, `telemarketerId` | string | No | Regulatory IDs, where required. |

```bash title="cURL"
curl -X POST https://api.broadnet.me/sms/sendbulk \
  -H "Authorization: Bearer $BROADNET_TOKEN" \
  -F destinationNumberFile=@recipients.csv \
  -F 'bulkRequest={"senderId":"OmniDemo","message":"Your statement is ready in the app.","messageType":"TEXT","campaignName":"oct-statements","allowDuplicate":false}'
```

```python title="Python"
import json
import os
import requests

bulk_request = {
    "senderId": "OmniDemo",
    "message": "Your statement is ready in the app.",
    "messageType": "TEXT",
    "campaignName": "oct-statements",
    "allowDuplicate": False,
}

with open("recipients.csv", "rb") as f:
    r = requests.post(
        "https://api.broadnet.me/sms/sendbulk",
        headers={"Authorization": f"Bearer {os.environ['BROADNET_TOKEN']}"},
        files={"destinationNumberFile": ("recipients.csv", f, "text/csv")},
        data={"bulkRequest": json.dumps(bulk_request)},
        timeout=60,
    )
print(r.json())
```

A successful upload returns a job summary:

```json title="Response"
{
  "status": "success",
  "bulkSessionId": "1_20260228105000887",
  "credits": "9966465.42850",
  "deductcredits": "0.60000",
  "bulkListInfo": {
    "total": 4,
    "validCount": 3,
    "invalidCount": 0,
    "duplicate": 1,
    "errors": {}
  }
}
```

`bulkListInfo` tells you how many numbers were accepted, rejected as invalid, or removed as duplicates. `credits` is your remaining balance and `deductcredits` the amount charged for the job. Store `bulkSessionId` with your campaign record.

## Control bulk jobs

Running jobs are identified by the `id` returned from `GET /sms/list`. Pass it as the `batch_Id` query parameter to the other job endpoints.

```bash title="cURL"
# 1. List jobs: each entry has an id, total, processed, pending and active
curl https://api.broadnet.me/sms/list \
  -H "Authorization: Bearer $BROADNET_TOKEN"

# 2. Pause, resume or abort a job by its id
curl "https://api.broadnet.me/sms/pause?batch_Id=1024" -H "Authorization: Bearer $BROADNET_TOKEN"
curl "https://api.broadnet.me/sms/resume?batch_Id=1024" -H "Authorization: Bearer $BROADNET_TOKEN"
curl -X DELETE "https://api.broadnet.me/sms/abort?batch_Id=1024" -H "Authorization: Bearer $BROADNET_TOKEN"
```

Each entry in the list looks like this (abridged):

```json title="GET /sms/list"
[
  {
    "id": 1024,
    "senderId": "OmniDemo",
    "reqType": "bulk",
    "messageType": "GSM-7",
    "campaignName": "oct-statements",
    "total": 1000,
    "processed": 450,
    "pending": 550,
    "active": true,
    "createdOn": "2026-10-08T21:15:20"
  }
]
```

| Action | Call | Notes |
| --- | --- | --- |
| Inspect | `GET /sms/edit?batch_Id=…` | Full details of one job. |
| Pause | `GET /sms/pause?batch_Id=…` | Only active jobs can be paused. |
| Resume | `GET /sms/resume?batch_Id=…` | Continues from where it stopped. |
| Abort | `DELETE /sms/abort?batch_Id=…` | Permanent; pending messages are not sent. |

`GET /sms/list` is not paginated; it returns all of your bulk jobs in one response.

### Scheduled campaigns

`GET /sms/listSchedule` returns your scheduled campaigns, each with an `id`. Use that ID with `POST /sms/send-now` to release a campaign immediately, or with `DELETE /sms/abort/schedule?scheduleId=…` to cancel it.

## Look up a number before sending

`POST /sms/lookup` checks a number against the network (HLR and MNP) so you can clean lists and route ported numbers correctly. Pass the number in the `destinationNumber` query parameter. The optional `reqType` parameter selects the kind of lookup; ask our team which values are enabled on your account.

```bash title="cURL"
curl -X POST "https://api.broadnet.me/sms/lookup?destinationNumber=447700900123" \
  -H "Authorization: Bearer $BROADNET_TOKEN"
```

Lookup traffic has its own default rate limit. See [Errors and limits](/developers/errors-and-limits/).

## High volume over SMPP

If you already run an SMPP client or need sustained high throughput, the same SMS routes are available over SMPP v3.4 with TLS. See [SMPP](/developers/smpp/).

## Next steps

- [Webhooks](/developers/webhooks/): receive `DELIVERED`, `FAILED` and other statuses.
- [Errors and limits](/developers/errors-and-limits/): retries, rate limits and duplicate protection.
- [RCS](/developers/rcs/): upgrade SMS to branded rich messages where handsets support it.
- [SMPP](/developers/smpp/): bind for high-volume SMS.
