# Проектирование REST API сообщений: ресурсы, методы, коды состояния

> REST API сообщений моделирует каждую отправку как ресурс в коллекции messages. Клиент выполняет POST сообщения, и, поскольку доставка асинхронна, сервер возвращает 202 Accepted с идентификатором сообщения и status URL, а затем сообщает окончательный результат через webhook. HTTP-методы и коды состояния следуют семантике RFC 9110.

- Канонический URL: https://www.broadnet.me/ru/reference/rest-messaging-api-design
- Язык: ru
- Последнее обновление: 2026-07-19
- Издатель: BroadNet (https://www.broadnet.me)

Как моделируется REST API сообщений на основе стандартов: коллекция messages, HTTP-методы и семантика кодов состояния по RFC 9110, синхронная и асинхронная отправка (202 Accepted плюс status callback), согласование содержимого и версионирование в URL или заголовке — с таблицей сравнения REST и SMPP.

## Модель ресурса messages

REST API сообщений трактует каждое сообщение как ресурс внутри коллекции — по соглашению /messages — адресуемый по назначенному сервером идентификатору. Намерение выражает глагол, а не поле полезной нагрузки: POST /messages создаёт и ставит в очередь новое исходящее сообщение; GET /messages/{id} читает его текущее состояние; GET /messages перечисляет и фильтрует. Это REST-слой, который находится над теми же соединениями с операторами, до которых SMPP добирается на уровне провода, поэтому ресурс, отправленный клиентом через POST, ниже по стеку отображается в submit_sm на привязанной сессии SMPP. Поскольку идентификатор порождается сервером и возвращается в ответе (и обычно дублируется в заголовке Location), у клиента есть стабильный дескриптор для опроса или для сопоставления с последующими status callback.

*Создание сообщения в коллекции (обобщённый паттерн REST)*

```http
POST /v1/messages HTTP/1.1
Host: api.example
Content-Type: application/json
Authorization: Bearer <token>
Idempotency-Key: 8f14e45f-ea7b-4c0f-9b2a-1d2c3e4f5a6b

{
  "to": "+15551234567",
  "from": "BRANDNAME",
  "text": "Your one-time code is 401 928",
  "callback_url": "https://app.example/webhooks/dlr"
}
```

## HTTP-методы и их гарантии

RFC 9110 определяет два свойства, которые управляют тем, как клиент сообщений и любой посредник могут обращаться с запросом. Безопасные методы (GET, HEAD) не имеют побочных эффектов, свободно кэшируются и повторяются. Идемпотентные методы (GET, HEAD, PUT, DELETE) можно повторять с тем же итоговым эффектом. POST не является ни безопасным, ни идемпотентным, и это ровно та проблема, что стоит перед отправкой сообщений: повторённый POST мог бы отправить сообщение дважды. Стандартное решение — не злоупотреблять PUT, а сохранить POST и сделать его идемпотентным с помощью заголовка запроса Idempotency-Key, чтобы повторённая отправка возвращала исходный результат вместо создания второго сообщения.

*HTTP-методы на ресурсе messages (RFC 9110)*

| Метод | На /messages | Безопасный | Идемпотентный |
|---|---|---|---|
| POST | Создать и добавить в очередь новое сообщение | No | Нет (сделайте идемпотентным через Idempotency-Key) |
| GET | Прочитать сообщение или вывести список коллекции | Да | Да |
| HEAD | Проверка метаданных / существования | Да | Да |
| PUT | Заменить изменяемый ресурс (например, конфигурацию webhook) | No | Да |
| PATCH | Частичное обновление изменяемого ресурса | No | No |
| DELETE | Отменить сообщение, находящееся в очереди | No | Да |

## Семантика кодов состояния для отправки

Код состояния, возвращаемый сервером, кодирует то, было ли сообщение принято, завершено или отклонено, — а отправка сообщения почти всегда асинхронна. RFC 9110 определяет 202 Accepted именно для этого случая: запрос принят к обработке, но обработка не завершена, и ответ должен описывать, где проверить её ход. Поэтому API сообщений возвращает 202 для поставленной в очередь отправки, резервируя 201 Created для ресурса, который полностью материализован, и 200 OK для действительно синхронной операции. Клиентские ошибки (некорректная полезная нагрузка, недопустимый получатель, неизвестный отправитель) — это 4xx, и их нельзя повторять без изменений; отказы сервера и вышестоящих систем — это 5xx, и их безопасно повторять с выдержкой под тем же Idempotency-Key.

*Семантика кодов состояния при отправке сообщения (RFC 9110)*

| Код | Значение | Когда его использует API обмена сообщениями |
|---|---|---|
| 202 Accepted | Принято, обработка не завершена | Асинхронная отправка поставлена в очередь; итоговое состояние приходит через callback |
| 201 Created | Ресурс создан | Синхронное создание, когда ресурс готов полностью |
| 200 OK | Запрос выполнен | Синхронное чтение статуса или действительно мгновенная операция |
| 400 / 422 | Ошибка клиента / необрабатываемый запрос | Некорректное тело запроса или недопустимое поле — не повторяйте без изменений |
| 401 / 403 | Не аутентифицирован / запрещено | Отсутствуют или недостаточны учетные данные |
| 409 Conflict | Конфликт состояния | Idempotency-Key повторно использован с другой полезной нагрузкой |
| 429 Too Many Requests | Превышен лимит запросов | Ограничьте скорость; соблюдайте Retry-After и выполняйте backoff |
| 5xx | Сбой сервера / вышестоящего узла | Временная — повтор с backoff под тем же ключом |

## Синхронная и асинхронная доставка

В API сообщений есть две различные задержки, и смешивать их — проектная ошибка. Задержка API — это время, за которое HTTP-вызов принимает сообщение; задержка доставки — это время, за которое сеть оператора передаёт сообщение на устройство и возвращает отчёт о доставке. Поскольку вторая не ограничена и вне контроля отправителя, хорошо спроектированный API подтверждает немедленно (202) состоянием accepted или queued, а затем доставляет конечный результат асинхронно — через callback о статусе доставки (HTTP-аналог отчёта о доставке SMPP). Клиенты, которым нужен окончательный статус, либо подписываются на callback (push), либо опрашивают GET /messages/{id} (pull); callback предпочтительнее, поскольку избегает нагрузки опроса и сообщает об изменениях состояния в течение секунд.

*Ответ 202 Accepted с дескриптором статуса*

```http
HTTP/1.1 202 Accepted
Location: /v1/messages/01HZY8Q3M6
Content-Type: application/json

{
  "id": "01HZY8Q3M6",
  "status": "accepted",
  "created_at": "2026-07-19T09:15:22Z",
  "status_url": "/v1/messages/01HZY8Q3M6"
}
```

## Согласование содержимого и версионирование

Клиенты и серверы договариваются о представлении через заголовки согласования содержимого RFC 9110: запрос несёт Content-Type для отправляемого тела и Accept для того, что хочет получить в ответ, а сервер отражает выбранный медиатип в Content-Type ответа. Большинство API сообщений стандартизируются на application/json и используют application/problem+json для ошибок. У версионирования API есть два основных подхода. Версионирование в URL (/v1/messages) явное, дружественное к кэшу и тривиальное для маршрутизации, ценой грубого скачка всей поверхности сразу. Версионирование в заголовке или медиатипе (Accept: application/vnd.example.v2+json) сохраняет URL стабильными и позволяет ресурсу развиваться независимо, ценой невидимости в браузере и большей сложности ручного тестирования. Какой бы подход ни был выбран, аддитивные изменения (новые необязательные поля) никогда не должны требовать смены версии; только ломающие изменения этого требуют.

*Подходы к версионированию API*

| Подход | Пример | Компромисс |
|---|---|---|
| Путь URL | /v1/messages | Явные, кэшируемые, легко маршрутизируются; грубые скачки версий |
| Пользовательский заголовок | Api-Version: 2026-07-19 | Чистые URL, привязка к дате; невидимо для кэшей и браузеров |
| Принимаемый тип медиа | Accept: application/vnd.example.v2+json | Эволюция на уровне ресурса; сложнее тестировать вручную |
| Параметр запроса | /messages?version=2 | Просто; запутывает кэширование и идентичность ресурса |

## REST против SMPP: выбор транспорта

HTTP/REST-слой и SMPP — это два транспорта к одной и той же сети сообщений, и зрелые отправители используют оба. REST — правильный выбор для интеграции приложений: он не сохраняет состояние, дружелюбен к межсетевым экранам поверх обычного HTTPS, легко защищается bearer token и прост в инструментировании. SMPP — правильный выбор для очень высокой устойчивой пропускной способности и наименьших накладных расходов на сообщение, поскольку долгоживущая бинарная сессия избегает затрат на соединение для каждого сообщения и даёт тонкий контроль окна. Таблица противопоставляет два транспорта, чтобы интегратор мог сопоставить транспорт с нагрузкой; конкретные REST-эндпоинты BroadNet см. в справочнике API для разработчиков, а протокол уровня провода — в обзоре SMPP.

*REST (HTTP) против SMPP как транспорта сообщений*

| Свойство | REST / HTTP | SMPP |
|---|---|---|
| Модель | Запрос/ответ без сохранения состояния по HTTPS | Долговременная бинарная сессия с сохранением состояния (bind) |
| Фрейминг | JSON по HTTP/1.1 или HTTP/2 | Бинарные PDU (submit_sm, deliver_sm) |
| Отчёт о доставке | Вебхук статуса / опрос GET | отчёт о доставке deliver_sm в рамках той же сессии |
| Пропускная способность | Хорошо; накладные расходы на каждый запрос | Наивысшая устойчивая; оконная передача, низкие накладные расходы |
| Auth | Токен Bearer / API ключ / OAuth 2.0 | system_id + пароль при привязке |
| Управление потоком | 429 + заголовки Retry-After, RateLimit | Размер окна + ограничение скорости enquire_link |
| Лучший вариант для | Интеграция приложений, веб-хуки, умеренный объем | Высоконагруженные потоки A2P операторского класса |

## Частые вопросы

### Почему REST API сообщений возвращает 202 вместо 200 или 201?

Потому что доставка сообщения асинхронна. RFC 9110 определяет 202 Accepted как «принято к обработке, обработка не завершена», что в точности описывает поставленную в очередь отправку. API немедленно подтверждает идентификатором сообщения и дескриптором статуса, а затем позже сообщает конечный результат доставки через callback. 201 и 200 подразумевают, что операция уже завершена.

### Как следует моделировать ресурс messages?

Как коллекцию: POST /messages создаёт и ставит в очередь сообщение, GET /messages/{id} читает его состояние, а GET /messages перечисляет и фильтрует. Сервер порождает идентификатор и возвращает его (часто в заголовке Location), чтобы у клиента был стабильный дескриптор для опроса или сопоставления с status callback.

### POST не идемпотентен — как избежать двойных отправок?

Вы сохраняете POST (он корректно выражает «создать новое сообщение») и делаете его идемпотентным с помощью заголовка запроса Idempotency-Key. Повторённая отправка с тем же ключом возвращает исходный результат вместо создания второго сообщения. Злоупотребление PUT ради принудительной идемпотентности ломает модель ресурсов REST.

### В чём разница между задержкой API и задержкой доставки?

Задержка API — это время, за которое HTTP-вызов принимает сообщение; задержка доставки — это время, за которое сеть оператора достигает устройства и возвращает отчёт о доставке. Вторая не ограничена и вне контроля отправителя, поэтому API подтверждает быстро (202) и сообщает об окончательной доставке асинхронно.

### Версионировать API сообщений в URL или в заголовке?

Версионирование в URL (/v1/messages) явное, дружественное к кэшу и лёгкое для маршрутизации, но меняет всю поверхность сразу. Версионирование в заголовке или медиатипе сохраняет URL стабильными и позволяет ресурсам развиваться независимо, но невидимо для кэшей и сложнее в ручном тестировании. В любом случае аддитивные изменения никогда не должны вынуждать смену версии.

### Когда использовать REST, а когда SMPP?

Используйте REST для интеграции приложений: HTTPS без состояния, дружественный к межсетевым экранам, аутентификация bearer token и лёгкое инструментирование. Используйте SMPP для наивысшей устойчивой пропускной способности и наименьших накладных расходов на сообщение, где окупается долгоживущая оконная бинарная сессия. Оба достигают одной и той же сети сообщений; многие отправители держат REST для управляющего трафика и SMPP для высокообъёмных потоков.

## Источники

- [IETF RFC 9110 — HTTP Semantics](https://datatracker.ietf.org/doc/html/rfc9110)
- [IETF RFC 9111 — HTTP Caching](https://datatracker.ietf.org/doc/html/rfc9111)

## См. также

- [Обзор протокола SMPP](https://www.broadnet.me/ru/reference/smpp-protocol-overview)
- [Доставка и надёжность webhook](https://www.broadnet.me/ru/reference/webhook-delivery-and-reliability)
- [Идемпотентность и ограничение скорости](https://www.broadnet.me/ru/reference/idempotency-and-rate-limiting)
- [Ошибки API и пагинация](https://www.broadnet.me/ru/reference/api-errors-and-pagination)
- [Справочник API BroadNet](https://www.broadnet.me/ru/developers/api-reference)
