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

# SMS

> Send transactional and marketing SMS across Africa with the same API key, routed per country, with the compliance rules built in.

## Send an SMS

```bash cURL theme={"dark"}
curl https://api.sendbaze.com/v1/sms \
  -H "Authorization: Bearer $SENDBAZE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "+27821234567",
    "body": "Your OTP is 482 913. It expires in 5 minutes.",
    "class": "transactional"
  }'
```

```ts Node.js theme={"dark"}
const sms = await sendbaze.sms.send({
  to: "+27821234567",
  body: "Your OTP is 482 913. It expires in 5 minutes.",
  class: "transactional",
});
console.log(sms.segments, sms.credits); // 1 1
```

```python Python theme={"dark"}
sms = sendbaze.sms.send(
    to="+27821234567",
    body="Your OTP is 482 913. It expires in 5 minutes.",
    class_="transactional",
)
```

```json Response theme={"dark"}
{ "id": "sm_01j9x7k2r4m8q3v5n6b7c8d9e0", "status": "queued", "mode": "live", "segments": 1, "credits": 0 }
```

## Fields

**to** `string` *required*

E.164, for example `+27821234567`. The country comes from the dialling code; see [Where you can send](#where-you-can-send).

**body** `string` *required*

Up to 1,600 characters. GSM-7 text fits 160 characters in one segment (153 per segment when concatenated); a single emoji or non-GSM character switches the message to UCS-2 at 70 (67) characters per segment. The response tells you how many segments it used.

**class** `'transactional' | 'marketing'` *required*

Marketing SMS carries an opt-out instruction (`Reply STOP to opt out` is appended unless your text already says STOP), honours STOP replies, and is held for the destination's send window: in South Africa 08:00 to 20:00 SAST, Monday to Saturday, not on public holidays; elsewhere 08:00 to 20:00 in the recipient's time zone. A held message answers `"status": "scheduled"` and goes out when the window opens. Transactional SMS (OTPs, receipts, alerts) is never held.

**senderId** `string`

Reserved for registered senders. Messages to South Africa go out from the shared numeric sender regardless of this field, because alphanumeric senders are not accepted there.

**scheduledAt** `string`

ISO 8601 time up to 30 days ahead. The response status is `scheduled`.

**tags** `string[]`

Up to 10 tags of 64 characters.

## Where you can send

SMS is routed by the recipient's country. South Africa, Botswana, Eswatini, Lesotho and Namibia are live today on our `southern-africa` route. The rest of Africa sits on the `pan-africa` route and opens country by country as prices are confirmed; nothing outside Africa is accepted. `GET /v1/sms/coverage` lists every country with its `route`, `available` (the route is live), `enabled` (your organisation has switched it on) and the price. Delivery in every country is carried by licensed local partners; the route name tells you which group of countries a message shares infrastructure with, not which partner carries it.

```json GET /v1/sms/coverage (abridged) theme={"dark"}
{
  "countries": [
    { "code": "ZA", "name": "South Africa", "dialCode": "+27", "route": "southern-africa", "available": true, "enabled": true, "pricing": "plan", "priceRand": null, "provisional": false },
    { "code": "BW", "name": "Botswana", "dialCode": "+267", "route": "southern-africa", "available": true, "enabled": false, "pricing": "wallet", "priceRand": 1.2, "provisional": true },
    { "code": "KE", "name": "Kenya", "dialCode": "+254", "route": "pan-africa", "available": false, "enabled": false, "pricing": "wallet", "priceRand": 0.6, "provisional": true }
  ],
  "enabled": ["ZA"],
  "available": ["ZA", "BW", "SZ", "LS", "NA"],
  "settingsUrl": "https://dashboard.sendbaze.com/settings"
}
```

**Your organisation chooses its destinations.** Every organisation starts with South Africa. An owner or admin adds countries on the dashboard's Settings page (you are asked at sign-up too). A send to any other country is refused with `403 destination_not_enabled` before anything is charged, which means a leaked key cannot run up a bill messaging countries you never meant to reach. A country that is not live yet answers `422 destination_not_supported`.

## Prices and allowances

Each segment is one unit. Messages to **South Africa** use your plan's included SMS first (300, 1,000 or 4,000 a month on Starter, Growth and Scale), then prepaid SMS credits, then your wallet or overage at the plan's rate; on Free and pay as you go they come from the wallet at R0.29. Messages to **other countries** are always paid from the wallet (or as overage on a paid plan) at that country's price per segment, shown in the coverage list; prices marked provisional are reviewed monthly as route rate cards are signed.

Units are debited when the message is accepted and refunded automatically if it never leaves our platform (validation, an opt-out, a route rejecting it on submission). A message the network accepted and then failed to deliver or expired is billed by the route and is not refunded; it is reported as `sms.failed`. `GET /v1/usage` shows the allowance, remaining credits and the reset date.

## Protection on every send

Live SMS runs through guard rails that cannot be switched off:

* **Destinations**: only countries your organisation enabled (above).
* **Opt-outs**: a number that replied STOP, END, CANCEL, UNSUBSCRIBE or QUIT gets no more marketing from your organisation (`422 recipient_suppressed`); transactional still sends. Your team can also block numbers by hand on the Suppressions page.
* **Velocity**: at most 300 segments a minute per organisation (60 on Free and pay as you go), 8 messages an hour and 20 a day to one number, and a burst of new numbers on one prefix from a new organisation is stopped and reviewed. A refused send answers `429 sms_velocity_exceeded` with `Retry-After`; email support if you have a legitimate reason to send faster.
* **Rates**: the shared sender stays inside what the networks allow, so OTPs keep arriving in seconds.

## Delivery and replies

`GET /v1/sms/{id}` returns the message with `segments`, `credits`, `scheduledAt` and its event timeline. Delivery receipts arrive as `sms.delivered` and `sms.failed` [webhooks](https://docs.sendbaze.com/learn/webhooks/events.md) (a message that expires undelivered is `sms.failed` with `error.code: expired`); a STOP reply emits `sms.opted_out` and suppresses the number for marketing.

## Batches

`POST /v1/sms/batch` takes up to 1,000 messages with per-item results, like the [email batch](https://docs.sendbaze.com/learn/sending/batch.md). Items are validated, priced and gated one by one: an item to a country you have not enabled fails on its own with `destination_not_enabled` while the rest of the batch sends.


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