Ни один канал доставки OTP не даёт 100% доходимости: у кого-то нет Telegram, кто-то не может позвонить, где-то не проходит SMS. Каскад — это цепочка каналов с переходом к следующему при неудаче. Разберём, как собрать её поверх API Verificahub: порядок каналов, функцию sendWithCascade на Node.js и почему неудачные попытки в каскаде не тарифицируются.
Каскадная доставка OTP — это стратегия, при которой код (или подтверждение) отправляется не одним фиксированным способом, а по цепочке каналов: если первый не сработал, автоматически подключается второй, затем третий. Цель двойная — поднять доходимость (кто-то из пользователей всегда выпадает из любого одного канала) и снизить стоимость (дешёвый канал закрывает большинство, дорогой подстраховывает остаток).
Сразу честно: в Verificahub нет «автокаскада» на стороне API. Одна сессия POST /v1/verify использует ровно один method. Каскад вы собираете в своём коде — последовательными вызовами с разными методами и переходом к следующему каналу при неудаче инициации или доставки. Ниже — рабочая схема и код.
Весь каскад живёт на бэкенде. Внешний API — server-to-server, api_secret — это пароль: не встраивайте его в браузер или мобильное приложение и не проксируйте на клиент. Фронтенд общается только с вашим сервером.
Определение и общий разбор термина — в глоссарии: каскадная доставка. Практический смысл прост: у каждого канала своя доходимость и цена. Обратный flash-call бесшовный и самый дешёвый, но требует, чтобы пользователь мог позвонить (иногда мешает корпоративная АТС). Telegram отлично доходит, но только если у человека установлен мессенджер. SMS доходит почти всегда, но дороже и медленнее. Каскад комбинирует их сильные стороны.
Дешёвый канал закрывает большинство пользователей, а запасной подстраховывает остальных — так каскад одновременно повышает доходимость и держит среднюю цену подтверждения низкой.
Ответ POST /v1/verify дискриминирован по method: для reverse_flash_call приходит number_to_call (пользователь сам звонит, сессия подтверждается по Caller ID автоматически), для telegram_otp — code_length (код читается в Telegram и проверяется через POST /v1/verify/check). Смешивать методы в одной сессии нельзя — и это удобно: вы полностью контролируете логику перехода и не зависите от «магии» на стороне API.
Переключаться на следующий канал имеет смысл в двух точках:
POST /v1/verify вернул ошибку, означающую, что канал недоступен прямо сейчас (например no_gateway_available, delivery_unavailable для Telegram, provider_error).expired (истёк TTL) или failed; либо пользователь сам нажал «получить код другим способом».Базовый принцип — сначала самый дешёвый и бесшовный, потом запасные. Так вы платите за дорогой канал только тогда, когда дешёвый реально не сработал.
reverse_flash_call: не нужно вводить код, самый дешёвый метод, от 0,25 ₽.telegram_otp: высокая доходимость там, где есть мессенджер, от 0,90 ₽ (официальный Telegram Gateway).sms: универсальный запасной канал, доходит почти везде.| Канал | Как подтверждается | Стоимость | Когда переходить дальше |
|---|---|---|---|
| reverse_flash_call | Пользователь звонит, авто-подтверждение по Caller ID | от 0,25 ₽ | Инициация вернула no_gateway_available/provider_error, либо статус стал expired/failed |
| telegram_otp | Код в Telegram, проверка через /v1/verify/check | от 0,90 ₽ | delivery_unavailable при инициации или истёк TTL без верного кода |
| sms | Код по SMS, проверка через /v1/verify/check | по тарифу | Последний рубеж: если тоже expired/failed — ручная проверка или поддержка |
Порядок подстраивайте под аудиторию: если ваши пользователи активно сидят в Telegram, поставьте telegram_otp первым (метод нужно заранее включить на аккаунте). SMS OTP мы разворачиваем на собственной сети +7 SIM-шлюзов — уточняйте доступность и цену для вашего аккаунта.
Ошибки приходят в формате RFC-7807 со стабильным полем error_code — ветвитесь по нему, а не по тексту `detail`. Не любую ошибку стоит «лечить» сменой канала: неверный номер или пустой баланс новый канал не спасёт.
| error_code | Что означает | Действие в каскаде |
|---|---|---|
no_gateway_available | Нет свободного канала для метода | Перейти к следующему каналу |
delivery_unavailable | Telegram недоступен для этого номера | Перейти к следующему каналу (звонок / SMS) |
provider_error | Временный сбой провайдера | Следующий канал или повтор с backoff |
rate_limit_exceeded | Слишком часто (429) | Канал не менять — выждать Retry-After |
insufficient_balance | Недостаточно средств | Остановиться и пополнить баланс |
validation_error | Номер не в формате E.164 +7… | Остановиться и исправить ввод |
Второй триггер — жизненный цикл сессии sent → delivered → verified | expired | failed. Если после инициации статус дошёл до expired или failed, это сигнал эскалировать на следующий канал. Узнавать статус лучше через webhook (события verification.*, подпись HMAC-SHA256), а не опросом.
Собираем ядро каскада — инициацию с переходом по каналам. Ключи читаем из окружения, авторизация — HTTP Basic. Функция возвращает первую успешно начатую сессию либо бросает исключение, если варианты кончились.
SMS-OTP (метод sms) в Verificahub подключается поэтапно через собственную SIM-сеть — уточните доступность для вашего аккаунта. Пока SMS не включён, используйте каскад из двух каналов: reverse_flash_call → telegram_otp.
Дальше действуйте по методу вернувшейся сессии: для reverse_flash_call покажите пользователю number_to_call, для telegram_otp/sms — попросите ввести код и проверьте его через POST /v1/verify/check.
Если канал начался, но не привёл к подтверждению (истёк TTL, статус expired/failed, или пользователь попросил другой способ), переходим на оставшиеся каналы — снова через sendWithCascade, но уже с «хвостом» списка.
Не запускайте эскалацию, пока не убедились, что текущий канал точно провалился. Для reverse_flash_call дождитесь webhook verification.verified или статуса verified через GET /v1/verify/{request_id} — иначе рискуете зря запустить и оплатить платный SMS, пока пользователь ещё звонит.
Детали интеграции каждого канала разобраны отдельно: верификация по звонку на Node.js и Telegram-верификация на Next.js. Каскад просто оркеструет то, что описано в этих гайдах.
Модель оплаты — за результат, со списанием при доставке (charge-on-delivery): неудачная инициация не тарифицируется. Поэтому переход к следующему каналу после ошибки инициации (no_gateway_available, delivery_unavailable, provider_error) бесплатен — за канал, который даже не начал доставку, вы ничего не платите.
Списание происходит в момент доставки, а не подтверждения. Если канал уже доставил код или звонок, он будет оплачен, даже если пользователь так и не подтвердился (expired/failed) — и следующий канал, если вы эскалируете, тоже. Отсюда правило порядка: дешёвый бесшовный канал первым удерживает среднюю стоимость подтверждения минимальной, а за дорогой Telegram или SMS вы платите только когда до них реально дошла очередь. Актуальные цены по каналам — на странице тарифов.
method, цепочку строите в своём коде.reverse_flash_call → telegram_otp → sms.error_code инициации или по статусу expired/failed, а не по тексту detail.Нет. Одна сессия POST /v1/verify использует ровно один method. Каскад вы собираете сами — последовательными вызовами с разными методами и переходом к следующему каналу при неудаче инициации или доставки.
По error_code при инициации (no_gateway_available, delivery_unavailable, provider_error) и по финальному статусу сессии expired/failed. Ветвитесь по стабильному error_code, а не по тексту detail.
Оплата — за результат со списанием при доставке (charge-on-delivery): неудачная инициация не тарифицируется, поэтому переход к следующему каналу после ошибки инициации бесплатен. Но если канал уже доставил код или звонок, он будет оплачен, даже если подтверждение не состоялось. Поэтому дешёвый бесшовный канал ставят первым — так средняя цена подтверждения остаётся минимальной.
Обычно самый дешёвый и бесшовный — reverse flash call (от 0,25 ₽). Если аудитория активно пользуется Telegram, первым можно поставить Telegram OTP. Универсальный запасной канал — SMS.