← Все статьи
Журнал изменений

UnifyPort v1 API готов к продакшену: всё, что вошло в первый стабильный релиз — UnifyPort

UnifyPort v1 API теперь готов к продакшену. Это первый стабильный релиз: один эндпоинт отправки, нормализованный слой Webhook-событий и потоки подключения аккаунтов для всех шести каналов — WhatsApp, Telegram, LINE, TikTok, Zalo и X — за единой поверхностью API.

Этот пост — список изменений данного релиза. Если вы ждали версию, на которой можно строить, не опасаясь, что фундамент под вами внезапно изменится, — это она. Ниже описано, что вошло в релиз, эндпоинт за эндпоинтом, с реальными именами событий, потоками аутентификации и поведением при сбоях.

Один API отправки для шести каналов

Ядро v1 — один эндпоинт. POST /v1/messages отправляет нормализованное текстовое сообщение через выбранный аккаунт провайдера. Вам не нужно изучать шесть SDK или шесть форматов полезной нагрузки — только один.

Специфика платформ никуда не делась, она переехала в структурированное поле provider_data. Нужно ответить на конкретное сообщение или задать parse mode в Telegram? Это живёт в provider_data (например, reply_to и parse_mode) без изменения формы запроса верхнего уровня. Типичный случай остаётся простым; специфичный для платформы — остаётся возможным.

Шесть поддерживаемых каналов: WhatsApp, Telegram, LINE, TikTok, Zalo и X. В API аккаунты провайдеров создаются с именами вроде telegram, whatsapp, twitter, line и zalo.

11 стандартных Webhook-событий

Вторая половина v1 — слой стандартных событий. Каждое сообщение от провайдера, каждое обновление статуса доставки и каждое событие жизненного цикла аккаунта преобразуется в одну стабильную JSON-форму. В этом релизе 11 стандартных событий, и ваш бэкенд обрабатывает их все через один обработчик.

Два, которые вы будете использовать чаще всего:

  • message.received — на подключённый аккаунт пришло входящее сообщение
  • message.status.updated — изменился статус доставки отправленного вами сообщения

События жизненного цикла аккаунта делают состояние подключения наблюдаемым, а не непрозрачным:

  • account.auth.required — аккаунту нужно действие входа (скан QR или код подтверждения)
  • account.auth.succeeded — вход завершён; provider_account_ref теперь заполнен, событие несёт стандартные поля профиля (display_name, picture_url, region, status_message)
  • account.started — рантайм аккаунта в сети
  • account.status.updated — статус аккаунта изменился

Событие message.received выглядит одинаково независимо от того, из какого из шести каналов оно пришло:

{
  "event": "message.received",
  "account_id": "acct_8Q2vK",
  "provider": "whatsapp",
  "from": "+6591234567",
  "text": "Здравствуйте, мой заказ готов?",
  "timestamp": 1749254400,
  "message_id": "wamid.HBgLNTU..."
}

Замените whatsapp на line или zalo — и ваша логика маршрутизации не изменится ни на строку. В этом и весь смысл стандартного слоя.

Подключение аккаунта: потоки QR и кода

Подключение аккаунта в v1 — отдельный поток, спроектированный так, что состояние аутентификации — это то, за чем ваш Webhook может наблюдать, а не то, что нужно нянчить вручную.

Вы создаёте аккаунт через POST на эндпоинт аккаунтов, выбирая auth_mode, соответствующий стилю подключения:

{
  "provider": "whatsapp",
  "auth_mode": "code",
  "provider_data": { "phone": "+6591234567" }
}

Для потоков на основе кода номер телефона в формате E.164 передаётся через provider_data.phone. Он сохраняется на аккаунте и автоматически воспроизводится для последующих действий аутентификации, так что повторно отправлять его не нужно. Создание дублирующей идентичности провайдера возвращает чистый 409 duplicate_provider_account вместо тихого создания второго аккаунта.

Для потоков на основе QR учётные данные приходят асинхронно через ваш Webhook:

  • WhatsApp — вызовите /auth/qr/start, чтобы начать сессию устройства. QR-токен приходит на Webhook как событие account.auth.required (auth_payload.qr_code — это сырой токен, который ваш UI должен отрендерить в виде QR-изображения), а также доступен синхронно через /auth/qr/check для опроса. После сканирования приходит account.auth.succeeded с заполненным provider_account_ref, затем account.started.
  • LINE — URL QR и PIN приходят асинхронно. Слушайте account.auth.required или опрашивайте /auth/session, чтобы прочитать auth_payload.url (изображение QR) и auth_payload.pin.

URL Webhook внедряется автоматически при настройке аккаунта — переходы состояния аутентификации и события входа приходят на тот же эндпоинт, что и входящие сообщения. Один Webhook, все события.

Как только приходит account.auth.succeeded, POST /v1/accounts/{account_id}/runtime/start выводит аккаунт в сеть (no-op, если рантайм уже запущен).

Управляющий слой Webhook

Webhook-эндпоинты в v1 — полноценные настраиваемые объекты, а не одно поле URL, спрятанное в настройках.

Создавая приёмник Webhook, вы точно выбираете, какие события он получает. subscribed_events принимает стандартные имена событий (такие как message.received, account.status.updated) или ["*"] для подписки на всё. Неизвестные имена событий отклоняются при записи, а не падают тихо позже.

Подпись встроена. Укажите signing_secret — и каждая доставка подписывается HMAC-SHA256, чтобы ваш эндпоинт мог проверить подлинность. Оставьте пустым — и подпись явно отключается: это осознанный выбор, а не случайность.

Поведение повторов настраиваете вы. retry_policy.max_attempts — неотрицательное целое; 0 полностью отключает повторы, нецелые значения отклоняются. Повторную доставку берёт на себя платформа; вашему эндпоинту нужно лишь ответить.

Надёжность, удерживающая турбулентность в слое платформы

Продакшен-релиз требует продакшен-настроек по умолчанию, и v1 поставляется с ними. Входящая доставка проходит через очередь с лимитами скорости, повторами, идемпотентностью и изоляцией сбоев, поэтому турбулентность на стороне провайдера остаётся внутри слоя платформы, а не каскадом обрушивается на ваше приложение.

Состояние рантайма аккаунта тоже нормализовано. runtime_status принимает одно из восьми стандартных для платформы значений — unknown, starting, running, stopping, stopped, reconnecting, disconnected, error — а специфичные для провайдера метки отображаются в этот набор ещё до того, как вы их увидите. Вы пишете один автомат состояний, а не шесть.

Для аккаунтов, которым нужна географическая маршрутизация, необязательный объект proxy_config сохраняется вместе с аккаунтом и применяется автоматически.

Что значит этот релиз

Готовность v1 к продакшену — это обязательство, а не просто номер версии. Эндпоинт отправки, 11 стандартных событий, потоки аутентификации и управляющий слой Webhook — это стабильная поверхность, поверх которой можно строить продукт. Смысл UnifyPort всегда был в том, чтобы командам не приходилось переписывать одну и ту же интеграцию шесть раз; v1 — версия, в которой эта поверхность перестаёт двигаться.

Если вы читали другие наши статьи о приёме сообщений без трения официального API — приём входящих WhatsApp без официального API, приём сообщений LINE без официального аккаунта или сравнение стоимости WhatsApp и Telegram — то это тот самый API, на котором работают эти пути.

С чего начать: создайте API-ключ, подключите первый аккаунт с auth_mode, подходящим каналу, зарегистрируйте Webhook-эндпоинт с signing_secret и подпишитесь на message.received. В рамках той же сессии нормализованное входящее событие уже будет приходить на ваш бэкенд. Дальше каждый новый канал — это тот же поток, а не очередной интеграционный проект.