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

# Send SMS messages

> Send single, bulk, and scheduled SMS with UgaText, format phone numbers, and understand segments and encoding.

All requests go to `https://ugatext.com/api/v1` and need your [API credentials](https://docs.ugatext.com/authentication.md).

## Send a single SMS

`POST /sms/send` sends one message.

```bash theme={null}
curl -X POST https://ugatext.com/api/v1/sms/send \
  -H "Client-ID: YOUR_CLIENT_ID" \
  -H "Client-Secret: YOUR_CLIENT_SECRET" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 8f14e45f-ceea-467f-a8f1-4e45fceea67f" \
  -d '{
    "to": "+256750625684",
    "message": "Your verification code is 482910.",
    "sender_id": "UGATEXT"
  }'
```

| Field | Required | Description |
| - | - | - |
| `to` | Yes | Recipient phone number. Aliases: `phone`, `recipient`. |
| `message` | Yes | Message text. Aliases: `text`, `body`. |
| `sender_id` | Yes | Approved [Sender ID](https://docs.ugatext.com/sms/sender-ids.md), 1 to 11 characters. Aliases: `senderId`, `from`. |

The response contains a `message_id` (format `msg_<13-digit-unix-ms>_<8-hex-chars>`) and the initial status `queued`. Add an `Idempotency-Key` header to retry safely. See [Idempotency](https://docs.ugatext.com/errors.md#idempotency).

> [!WARNING]
>
> `sender_id` is required. A missing, blank, or unapproved value returns `403 SENDER_ID_NOT_APPROVED`.

## Phone number formats

UgaText delivers to Uganda (+256), Kenya (+254), Tanzania (+255), and DR Congo (+243). Each of these formats is accepted:

| Format | Example |
| - | - |
| E.164 with `+` | `+256750625684`, `+254712345678`, `+255712345678`, `+243810000000` |
| E.164 without `+` | `256750625684`, `255712345678` |
| Local national format | `0750625684`, `0712345678`, `0810000000` |

The API normalizes leading zeros and local formatting before sending. Invalid numbers return `422 INVALID_PHONE_NUMBER`. To test for free, send to `+256700000000`, `+254700000000`, `+255700000000`, or `+243810000000`. See [Sandbox](https://docs.ugatext.com/sandbox.md).

### Tanzania numbers

Tanzanian national numbers have 9 digits after the country code and start with `6` or `7`, for example `0712 345 678` or `0682 123 456`. Send them as `+255712345678` or `255712345678`.

```bash theme={null}
curl -X POST https://ugatext.com/api/v1/sms/send \
  -H "Client-ID: YOUR_CLIENT_ID" \
  -H "Client-Secret: YOUR_CLIENT_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "+255712345678",
    "message": "Habari! Your verification code is 849201. Valid for 10 minutes.",
    "sender_id": "UGATEXT"
  }'
```

## Supported destinations

| Country | Calling code | Supported networks | Standard rate | Custom Sender IDs |
| - | - | - | - | - |
| Uganda | `+256` | MTN, Airtel, UTL, Lycamobile | UGX 25 per SMS | Yes (UCC) |
| Kenya | `+254` | Safaricom, Airtel Kenya, Telkom | KES 0.40 per SMS | Yes (CA) |
| Tanzania | `+255` | Vodacom, Airtel TZ, Tigo, Halotel | TZS 16 per SMS | No, default `UGATEXT` only |
| DR Congo | `+243` | Vodacom, Airtel, Orange, Africell | USD 0.035 per SMS | Yes (ARPTC / Coussema), USD 120 one-time fee |

## Costs and currency

Rates are listed in [Supported destinations](#supported-destinations). Volume discounts apply. See [ugatext.com/pricing](https://ugatext.com/pricing) for current rates.

Send and status responses report cost in several currencies:

| Field | Description |
| - | - |
| `cost_ugx` | Cost in Ugandan shillings. |
| `cost_kes` | Cost in Kenyan shillings. |
| `cost_usd` | Cost in US dollars. |
| `currency` | Billing currency: `UGX`, `KES`, or `USD`. |
| `country` | Destination country, for example `Uganda`. |
| `network` | Destination carrier, for example `Airtel`. |

### Cross-border billing

Accounts billed in UGX, KES, or USD can send to Tanzania (+255) numbers directly. The TZS 16 rate is converted to your wallet currency at the real-time exchange rate and deducted from your balance.

## Send bulk SMS

`POST /sms/bulk` sends one message to up to **5,000 recipients** per request. Larger batches return `400 BATCH_SIZE_EXCEEDED`.

```bash theme={null}
curl -X POST https://ugatext.com/api/v1/sms/bulk \
  -H "Client-ID: YOUR_CLIENT_ID" \
  -H "Client-Secret: YOUR_CLIENT_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "recipients": ["+256700000000", "+256772123456"],
    "message": "Important community update from UgaText.",
    "sender_id": "UGATEXT"
  }'
```

* `recipients` also accepts the aliases `phones`, `to`, and `messages`.
* Items can be objects with their own `to`, `message`, and `sender_id`, so each recipient can get a different message.

The response includes a `batch_id`, totals (`total_recipients`, `dispatched_count`, `failed_count`, `total_segments`, `total_cost_ugx`), and a `results` array with one entry per recipient. See [Bulk results](https://docs.ugatext.com/errors.md#bulk-results).

## Schedule a message

`POST /sms/schedule` queues a message for a future time. Send to one number or pass an array for a scheduled bulk broadcast.

```bash theme={null}
curl -X POST https://ugatext.com/api/v1/sms/schedule \
  -H "Content-Type: application/json" \
  -H "Client-ID: YOUR_CLIENT_ID" \
  -H "Client-Secret: YOUR_CLIENT_SECRET" \
  -d '{
    "to": "+256750625684",
    "sender_id": "UGATEXT",
    "message": "Reminder: Your invoice #1042 is due tomorrow.",
    "scheduled_at": "2026-11-01T09:00:00.000Z",
    "timezone": "Africa/Kampala",
    "recurrence": "none",
    "title": "Invoice Reminder"
  }'
```

| Field | Type | Required | Description |
| - | - | - | - |
| `to` | string or string\[] | Yes | One E.164 number (for example `+256750625684`) or an array of numbers for a scheduled bulk broadcast. Aliases: `phone`, `recipients`, `phones`. |
| `message` | string | Yes | SMS text. 160 characters per segment for GSM-7, 70 for Unicode. Alias: `text`. |
| `scheduled_at` | string | Yes | Future send time in ISO-8601, for example `2026-11-01T09:00:00.000Z`. Alias: `scheduledAt`. |
| `sender_id` | string | No | Approved 3 to 11 character alphanumeric [Sender ID](https://docs.ugatext.com/sms/sender-ids.md). Defaults to `UGATEXT`. Alias: `senderId`. |
| `timezone` | string | No | IANA timezone used for local scheduling. Defaults to `Africa/Kampala`. |
| `recurrence` | string | No | `none` (default), `daily`, `weekly`, or `monthly`. |
| `title` | string | No | Internal campaign label. Defaults to `API Scheduled Message`. |

```json Response (200 OK) theme={null}
{
  "success": true,
  "message_id": "sched_1760425200000_1a2b3c4d",
  "status": "scheduled",
  "recipient": "+256750625684",
  "sender_id": "UGATEXT",
  "scheduled_at": "2026-11-01T09:00:00.000Z",
  "segments": 1,
  "estimated_cost_ugx": 25
}
```

### Cancel or reschedule

You can change a scheduled message until its send time.

| Action | Endpoint | Body |
| - | - | - |
| Cancel a scheduled SMS | `POST /sms/schedule/{id}/cancel` | None |
| Reschedule a pending SMS | `POST /sms/schedule/{id}/reschedule` | `{"scheduledAt": "2026-11-02T14:00:00.000Z"}`. Optionally also `message`, `recipients`, `timezone`, and `recurrence`. |

```json Cancel response theme={null}
{
  "success": true,
  "message_id": "sched_1760425200000_1a2b3c4d",
  "status": "cancelled",
  "message": "Scheduled SMS cancelled successfully."
}
```

Cancelling an unknown ID returns `404 NOT_FOUND`. Cancelling a message that is already cancelled returns `400 ALREADY_CANCELLED`, and cancelling one that was already sent or delivered returns `400 CANNOT_CANCEL`.

## Campaigns

Group a broadcast under a named campaign to track and cancel it as one unit.

```bash theme={null}
curl -X POST https://ugatext.com/api/v1/campaigns \
  -H "Client-ID: YOUR_CLIENT_ID" \
  -H "Client-Secret: YOUR_CLIENT_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "October Promo",
    "sender_id": "UGATEXT",
    "message": "October deals are live. Reply STOP to unsubscribe.",
    "recipients": ["+256772123456"],
    "scheduled_at": "2026-11-01T09:00:00.000Z"
  }'
```

| Action | Endpoint |
| - | - |
| Create and dispatch a campaign | `POST /campaigns` |
| List campaigns | `GET /campaigns` |
| Get campaign details | `GET /campaigns/{id}` |
| Cancel a pending campaign | `POST /campaigns/{id}/cancel` |

## Track delivery

Messages move from `queued` to `sent` to `delivered`, or end as `failed`.

* **Poll:** `GET /sms/status/{id}` returns the status, timestamps (`sent_at`, `delivered_at`, `failed_at`), `failure_reason`, `network`, and `provider_reference`.
* **Push:** register a [webhook](https://docs.ugatext.com/sms/webhooks.md) to receive `sms.sent`, `sms.delivered`, and `sms.failed` events.
* **History:** `GET /sms/history` (alias `GET /messages`) lists your recent messages.

`GET /sms/history` accepts these optional query parameters:

| Parameter | Description |
| - | - |
| `limit` | Number of results, 1 to 100. Defaults to 20. |
| `status` | `queued`, `sent`, `delivered`, `failed`, `scheduled`, or `cancelled`. |
| `to` | Only messages to this E.164 recipient. |
| `campaign_id` | Only messages from this [campaign](#campaigns). |

## Segments and encoding

Long messages are split into segments, and you're billed per segment.

| Encoding | Used when | Single SMS | Per segment in multi-part messages |
| - | - | - | - |
| `GSM-7` | The message contains only standard characters | 160 | 153 |
| `UCS-2` (Unicode) | The message contains emoji or non-Latin characters | 70 | 67 |

Unicode supports Luganda, Swahili, and other regional character sets. Each response includes `segments` and `encoding` so you can confirm the cost. To check a message before sending, use the [compliance pre-check](https://docs.ugatext.com/errors.md#compliance-pre-check).

## Check your balance

`GET /sms/balance` returns your wallet balance in UGX, the price per SMS, and the remaining SMS units.

```json theme={null}
{
  "success": true,
  "currency": "UGX",
  "balance_ugx": 150000,
  "rate_per_sms_ugx": 25,
  "sms_units_remaining": 6000,
  "account_status": "active"
}
```

When the balance is too low, sends return `402 INSUFFICIENT_BALANCE`. Top up with MTN Mobile Money, Airtel Money, or M-Pesa from the [dashboard](https://ugatext.com).


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