> For the complete documentation index, see [llms.txt](https://academy.cegedim.cloud/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://academy.cegedim.cloud/messaging/sms/sms-sending-an-sms.md).

# SMS - Send with the API

This page details the SMS sending request (`POST /sms`), with full JSON examples and the list of possible response codes and error payloads.

For authentication, the base URL and the general request format, see [SMS - Get started](/messaging/sms/sms-get-started.md).

{% hint style="warning" %}
A successful request does not guarantee delivery of the SMS. The request is accepted synchronously, but the SMS is delivered asynchronously to the recipient's device. Use the SMS status request to check its delivery afterwards.
{% endhint %}

## Request

```
POST /sms
Content-Type: application/json
Authorization: Basic <your credentials, base64-encoded>
```

### Request body

| Field          | Type    | Required | Description                                                                                                                            |
| -------------- | ------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| `phoneNumber`  | string  | Yes      | Recipient's phone number: either a French local number (10 digits), or an international number formatted as `+<country-code><number>`. |
| `message`      | string  | Yes      | Message content, 600 characters maximum. One SMS is charged every 160 characters.                                                      |
| `allowUnicode` | boolean | No       | Defaults to `true`. If `false`, forces GSM 03.38 encoding instead of unicode.                                                          |
| `metadata`     | object  | No       | Free-form JSON object to tag the message with custom data (see example below).                                                         |

### Simple example

```json
{
  "phoneNumber": "+33612345678",
  "message": "Please call me back"
}
```

### Example with custom metadata

The added `metadata` tag is returned when retrieving the SMS details.

```json
{
  "phoneNumber": "+33612345678",
  "message": "Please call me back",
  "metadata": {
    "myCustomerId": "123de32",
    "caller": "MyApp 2.2"
  }
}
```

## Successful response

An accepted request returns `200` with the message identifier, to be used later on to check its delivery status.

```json
{
  "messageId": "77a76363bd882992893",
  "smsCount": 1
}
```

## Response codes and possible errors

The error body shape depends on which check rejected the request - there is no single unified format across status codes.

| Code  | Meaning                                                                                   |
| ----- | ----------------------------------------------------------------------------------------- |
| `200` | SMS accepted for sending.                                                                 |
| `400` | Invalid request (missing/too-long field, unparsable phone number, or provider rejection). |
| `422` | Recipient not allowed for this account (see below).                                       |
| `429` | Rate threshold (SMS/minute or SMS/hour) reached for this account.                         |
| `503` | No SMS provider currently available - retry later.                                        |

### `400` Bad Request

Three different situations return `400`, with three different body shapes:

* A missing/blank field, or a `message` over 600 characters, is caught by field validation. The body is a flat map, one entry per invalid field:

  ```json
  {
    "message": "length must be between 1 and 600"
  }
  ```
* A `phoneNumber` that cannot be parsed as a valid number at all (wrong format, unknown country code, non-numeric characters...) fails a later check, once past field validation. The body only has an `error` key, whose value is the `phoneNumber` you sent, verbatim:

  ```json
  {
    "error": "+33 6 not-a-number"
  }
  ```
* If your account's SMS provider configuration itself rejects the request (for example an invalid `fromName`), the body is an array of causes instead:

  ```json
  [
    {
      "code": "INVALID_MESSAGE_LENGTH",
      "on": "message",
      "message": "Message length cannot exceed 600 characters"
    }
  ]
  ```

### `422` Unprocessable Entity

The recipient is not allowed for your account. This covers three independent per-account policies: a destination-country whitelist, a destination-country blacklist, and a phone-number whitelist. All three produce the same body - the response does not say which one fired. This is `422`, not `403`: your credentials and role are fine, it's specifically this request's recipient that your account's policy rejects.

```json
{
  "error": "RECIPIENT_NOT_ALLOWED"
}
```

No SMS is sent and no delivery is attempted when this happens.

{% hint style="info" %}
If a country whitelist has at least one entry, it always takes precedence over the country blacklist, which is then ignored for that account.
{% endhint %}

### `429` Too Many Requests

Returned when your account's per-minute or per-hour SMS rate threshold is reached. The body shape matches the provider-rejection case above (an array of causes) - note that `message` is a generic, non-interpolated template here (the literal `%s` is not a typo), the actual detail (which threshold, how many SMS) is in `args` instead:

```json
[
  {
    "code": "LIMIT_REACHED",
    "message": "Request cannot be completed because of limit [%s]",
    "args": ["Threshold of 10 SMS within a minute has been reached."]
  }
]
```

### `503` Service Unavailable

No SMS provider is currently available to send your message - retry later, following the pattern described below.

```json
{
  "error": "No providers are currently available to send SMS, retry later"
}
```

## Recommended client pattern

Vortext can have up to 5 seconds of downtime in case one of our clustered infrastructure nodes fails. In this case you will get a `502` or `503` error. To ensure message delivery, consider calling the service with this pattern:

```
sendSms();
if (failure) {
  wait(5 seconds);
  sendSms();
  if (failure) {
    thisIsARealFailure();
  }
}
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://academy.cegedim.cloud/messaging/sms/sms-sending-an-sms.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
