SMS-шлюз: beeline-service Для разработчиков. Состояние на 28.09.2026. Переменные, все ручки и выкатка по шагам — README репозитория Buharestskie/beeline-service. Как лендинги работают целиком — статья о квиз-лендингах в этой же книге. Коротко beeline-service — общий шлюз исходящих SMS. Сервис присылает номер и готовый текст, шлюз проверяет, что номер российский мобильный, применяет антиспам-лимиты и отдаёт сообщение Билайну (A2P) или хабу МегаФона. Шаблонов у шлюза нет: текст целиком приходит от вызывающего. Снаружи шлюз доступен по адресу https://donula26.ru/service/beeline, пускает только адреса из своего списка доступа. Кто вызывает Сейчас — три квиз-лендинга, которые подтверждают телефон посетителя кодом из SMS («Ваш код подтверждения: 1234»): Лендинг Что передаёт do-nula-refinancing-2 phone, message, ip посетителя. lead_id не передаёт никто bfl_fspp mfcspisan (на стороннем сервере) mfctest шлюз не вызывает: SMS-подтверждения у него нет, после телефона посетитель уходит в мессенджер. Все вызывающие ходят через nginx по публичному адресу, поэтому адрес каждого нового вызывающего нужно заранее внести в ALLOWED_CALLER_CIDRS, иначе он получит 403 Caller IP is not permitted. Как устроен Один процесс FastAPI (Python 3.12, httpx, APScheduler) и свой Redis в том же compose. Redis нужен только для лимитов. Базы нет, тестов нет. Номер приводится к +7XXXXXXXXXX. Принимаются +7…, 8… и десять цифр с пробелами, скобками и дефисами. Всё, что не складывается в десять цифр с первой девяткой, получает 400 «Введите правильный российский номер» — до лимитов и до провайдера. Четыре лимита проверяются по порядку, каждый отвечает 429 с заголовком Retry-After: Лимит Порог Когда действует Повтор на тот же номер 1 SMS в 60 секунд только если передан ip Разных номеров с одного ip 3 за 24 часа только если передан ip SMS на один номер 3 за 24 часа всегда Всего SMS 1000 за сутки по UTC всегда ip — адрес конечного посетителя, который видел бэкенд лендинга, а не адрес самого лендинга. Пороги — константы в app/constants.py, меняются только вместе с кодом. Отправитель в обоих каналах — donulya.ru. Вызывающий получает 202 и сырой ответ провайдера строкой в provider_response. Ошибка провайдера — 502. Примечание в amoCRM. Если в запросе есть lead_id, после ответа фоновой задачей в сделку добавляется примечание «исходящая SMS» с номером и текстом. Шлюз пишет его своим токеном напрямую, не через amocrm-service и не через amo_queue, со своим ограничением 6 запросов в секунду. Ошибка примечания только пишется в лог. Билайн или МегаФон SMS уходит в хаб МегаФона, только если выполнены оба условия: на странице настроек шлюза включён переключатель «Использовать сервис Мегафон»; номер по плану нумерации выделен МегаФону: таблица диапазонов лежит в app/services/megafon_ranges.json (25 DEF-кодов) и читается один раз при старте. Всё остальное идёт через Билайн. По умолчанию переключатель выключен, то есть без ручного включения все SMS уходят Билайну. Страница настроек — https://donula26.ru/service/beeline/megafon-sms-settings: переключатель и кнопка «Сохранить». Нужны одновременно адрес из списка доступа и отдельный логин и пароль страницы ( MEGAFON_SETTINGS_PAGE_LOGIN / MEGAFON_SETTINGS_PAGE_PASSWORD, к учётке МегаФона отношения не имеют). Первого числа каждого месяца в 00:00 по Москве переключатель выключается сам. Если в этот момент сервис лежал, выключение сработает при старте в тот же день. Оператор определяется без учёта переноса номера. Номер, ушедший от МегаФона к другому оператору, всё равно пойдёт через МегаФон, и наоборот. Скрипта для обновления диапазонов в репозитории нет. Как вызвать curl -X POST https://donula26.ru/service/beeline/sms/send \ -H 'Content-Type: application/json' \ -d '{"phone": "+7 000 000-00-01", "message": "Ваш код подтверждения: 4821", "ip": ""}' Поле Обязательно Что это phone да Российский мобильный в любом написании, 5–32 символа message да Текст, 1–1000 символов, уходит провайдеру как есть ip нет Адрес посетителя. Без него не действуют лимит повтора и лимит номеров на IP lead_id нет Сделка amoCRM для примечания об SMS Проверка живости — GET /sms/health, открыта без списка доступа, отвечает {"status":"ok"}. Где код и как выкатывается Репозиторий Buharestskie/beeline-service, ветка main. На сервере — git-клон main: git pull --ff-only и docker compose up -d --build. Контейнер опубликован только на локальном интерфейсе сервера, снаружи — через nginx с префиксом /service/beeline/. Внешней пробы в мониторинге у шлюза нет, поэтому окно обслуживания на выкатку объявить не на что. Локально: cp .env.example .env, заполнить, поднять Redis и uvicorn app.main:app --port 8001. С боевыми ключами SMS уйдут на настоящие номера. На что наступают После выкатки переключатель МегаФона снова выключен. Его состояние хранится в файле внутри контейнера, тома под него нет. Если маршрут был включён — перед выкаткой проверьте его ( GET /megafon-sms-settings/api) и после неё включите заново на странице настроек. 202 от шлюза не значит, что МегаФон принял SMS. Хаб отвечает HTTP 200 и при отказе, вердикт лежит в теле. Шлюз такой отказ только пишет в лог. Смотрите в provider_response: у МегаФона там error.code, 0 — принято. Передавайте номер всегда в одном формате. Лимиты считаются по строке phone как её прислали: +79000000001 и 8 900 000-00-01 — это разные счётчики. Счётчики растут и на отказах. Если сработал другой лимит или провайдер вернул 502, повторить отправку на тот же номер можно только через 60 секунд, а суточный счётчик номера уже увеличен. Суточный лимит в 1000 SMS обнуляется в 03:00 по Москве (сутки считаются по UTC), а ежемесячный сброс МегаФона срабатывает в 00:00 по Москве. Адрес шлюза в .env.example лендингов неправильный. У bfl_fspp и mfcspisan там /services/beeline с «s», у do-nula-refinancing-2 — адрес внутри чужой docker-сети. Рабочий адрес один: https://donula26.ru/service/beeline. Страница настроек локально не загрузит состояние: её скрипт ходит на боевой путь /service/beeline/megafon-sms-settings/api. Локально переключайте маршрут через curl на /megafon-sms-settings/api. Примечания с lead_id нагружают общий лимит amoCRM мимо общего шлюза. Если начнёте передавать lead_id массово, учитывайте это вместе с остальными сервисами (см. «Как ходить в amoCRM из своего сервиса»).