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

# SMS

> Send transactional SMS from your own alphanumeric sender ID, priced per destination network.

Use the SMS API to send any text message — order updates, alerts, or your own one-time codes. For verification codes, [Verify](/guides/verify) is usually simpler: it generates and checks the code for you.

## Sender IDs

Every SMS is sent from an alphanumeric **sender ID**, such as `Acme`, that appears as the sender on your user's phone. You need at least one approved sender ID before you can send.

A sender ID has 3–11 letters and digits, with at least one letter. No spaces or symbols.

<Steps>
  <Step title="Request a sender ID">
    In the dashboard, open **Messaging → SMS** and click **Add sender ID**. Enter the sender ID and the website or app it's for.
  </Step>

  <Step title="Wait for approval">
    The sender ID starts as **pending**. Once it's **approved**, you can send from it.
  </Step>

  <Step title="Set a default (optional)">
    Pick a **Default sender ID** in SMS settings. Requests without `from` use it.
  </Step>
</Steps>

`from` is matched without regard to case, and the message goes out with the casing you registered.

## Send an SMS

```bash theme={null}
curl -X POST https://api.otpbay.com/v1/messages/sms \
  -H "Authorization: Bearer $OTPBAY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "+447700900123",
    "from": "Acme",
    "body": "Your Acme order 1042 has shipped.",
    "callback": "https://example.com/webhooks/otpbay"
  }'
```

| Field      | Required | Description                                                                                         |
| ---------- | -------- | --------------------------------------------------------------------------------------------------- |
| `to`       | Yes      | Phone number in international format.                                                               |
| `body`     | Yes      | Message text, up to 10,240 characters.                                                              |
| `from`     | No       | An approved sender ID. Defaults to your default sender ID.                                          |
| `metadata` | No       | Your own data, returned in responses and webhooks.                                                  |
| `callback` | No       | URL for a [webhook](/webhooks) when the SMS is sent or fails. Defaults to the SMS default callback. |

The API responds right away with `201` and status `queued`. Delivery happens in the background.

## Message status

```mermaid theme={null}
flowchart LR
  queued --> processing --> sent
  processing -->|retries used up| failed
```

| Status           | Meaning                                                                              |
| ---------------- | ------------------------------------------------------------------------------------ |
| `queued`         | Accepted and waiting to be sent.                                                     |
| `processing`     | Being handed to the carrier.                                                         |
| `sent`           | The carrier accepted the message.                                                    |
| `failed`         | Delivery failed after all retries. `last_error` has the reason.                      |
| `enqueue_failed` | The message was saved but couldn't be queued. The API returned `503`; send it again. |

If the carrier call fails, OTPBay retries up to 5 times with exponential backoff. Follow the status with [Get an SMS](/api-reference/sms/get), or set a `callback` to receive a [webhook](/webhooks) on `sent` and `failed`.

<Note>
  `sent` means the carrier accepted the message. It doesn't confirm the message reached the handset.
</Note>

## Segments and encoding

Carriers split long messages into segments, and each segment is billed. The segment size depends on the characters in `body`:

| Text                                                             | One segment    | Each segment of a longer message |
| ---------------------------------------------------------------- | -------------- | -------------------------------- |
| Plain ASCII                                                      | 160 characters | 153 characters                   |
| Contains any other character (emoji, accents, non-Latin scripts) | 70 characters  | 67 characters                    |

A single emoji or accented letter switches the whole message to the smaller size. The response shows the count in `sms_segments`.

## Pricing

SMS is priced per segment by destination network. OTPBay identifies the network from the phone number; if it can't, it charges the highest price in that country. See the current prices in **Billing → Pricing** in the dashboard.

* `cost` in the response is the quoted price: per-segment price × `sms_segments`.
* Your balance must cover `cost` when you send, or the request fails with `402 INSUFFICIENT_BALANCE`.
* The charge is taken when the message is handed to the carrier. If delivery fails after all retries, it's refunded.
* A destination with no price fails with `422 DESTINATION_NOT_PRICED`.

## Common errors

| Status | Code                     | Fix                                                |
| ------ | ------------------------ | -------------------------------------------------- |
| `403`  | `CHANNEL_DISABLED`       | Turn SMS on in **Messaging → SMS**.                |
| `422`  | `SENDER_ID_REQUIRED`     | Pass `from`, or set a default sender ID.           |
| `422`  | `SENDER_ID_NOT_APPROVED` | Use a sender ID that is approved for this project. |
| `422`  | `DESTINATION_NOT_PRICED` | The destination isn't supported yet.               |
| `402`  | `INSUFFICIENT_BALANCE`   | [Add funds](/billing#add-funds) to your balance.   |

See [Errors](/errors) for the full list.
