API как платформа: multi-tenant, white-label и open SDK
Один эндпоинт `/contacts` — ещё не платформа. Платформа начинается там, где на одном API работают сотня клиентов агентства, партнёр встраивает API в свой продукт, а security-команда корпоративного клиента читает исходник SDK без NDA. TL;DR: три архитектурных свойства — multi-tenant с изоляцией `project_id`, white-label-слой и open-source SDK — превращают API из «ручки для дёргания» в платформу. Разбираем каждое на реальном Public API BOTIX (`v1.1.0`), с кодом и без выдумки.
Коротко
- Multi-tenant — не настройка, а архитектура: один SDK на 100 ботов агентства держится на изоляции
project_id(WHERE project_id = ?+Auth::assertProjectAccess()), одном инстансе SDK на проект, суженных scope и ротации ключей без даунтайма. - White-label — набор свойств, закладываемых заранее: отдельный тип аутентификации, sub-проекты, жёсткая изоляция, usage через API. Сегодня в BOTIX это OAuth-installations (
btx_oauth_*); полноценный partner-API с sub-проектами — в roadmap, честно помечаем как «ещё не в проде». - Open-source SDK — не подарок сообществу, а инструмент: security-аудит без NDA, bug fix через PR, сигнал зрелости. Три zero-dependency SDK BOTIX (
botix-pro/sdk,botix,@botix/sdk) выложены на GitHub.
В большинстве SaaS-команд «стать платформой» обсуждают как маркетинговую амбицию. На деле это три конкретных технических свойства, каждое из которых либо заложено в API с первого дня, либо дорого достраивается после первых жалоб интеграторов. Combo этой статьи — три тезиса, по одному на свойство: multi-tenant, white-label, open-source SDK. Смежные грани — в разборах API как платформа: OAuth, scope, изоляция project_id и OAuth 2.0 vs API-ключ: когда что в SaaS-интеграциях.
Multi-tenant: один SDK на 100 ботов агентства без утечек между проектами
Сценарий. Агентство строит чат-ботов на платформе и ведёт сотню клиентов. Каждый клиент — отдельный проект на стороне платформы: своя база контактов, свои сценарии, свои лимиты по тарифу. Со стороны агентства это одна команда, один репозиторий, один деплой. Между этими двумя реальностями нужен мост, который не утечёт из клиента A в клиента B, переживёт ротацию ключей без даунтайма и не превратится в куст if'ов по проектам.
Большинство ошибок multi-tenant лежит не в коде, а в неявных предположениях: «project_id один на запрос», «токен можно положить в общий конфиг», «лимит у всех одинаковый». Каждое однажды оказывается неверным — и тогда клиент A видит данные клиента B, либо вся интеграция упирается в лимит самого слабого тарифа из ста.
Границы доверия. В multi-tenant есть три зоны — интегратор, платформа, клиент платформы — и ключи как механизм аутентификации эти границы должны явно отражать. Первый паттерн — API-ключ на проект: интегратор хранит по одному ключу на клиента, ключи выпущены через клиентский кабинет. Просто и предсказуемо, но для каждого нового клиента нужен ручной шаг. Второй — OAuth installation: интегратор регистрирует приложение, клиент платформы одним кликом «подключает приложение к своему проекту», на выходе — access_token, привязанный к приложению и к конкретному проекту (стандарт для third-party, как GitHub Apps, Stripe Connect).
В BOTIX доступны оба паттерна. Ключ btx_live_… привязан к project_id явно (btx_api_keys.project_id). OAuth-installations реализованы через таблицы btx_oauth_apps, btx_oauth_auth_codes, btx_oauth_access_tokens (TTL 1 час), btx_oauth_refresh_tokens (TTL 30 дней, ротация при обмене) и btx_oauth_installations для UI «Подключённые приложения». OAuth-токен несёт префикс btx_oauth_, обычный ключ — btx_live_.
Изоляция project_id — главное требование. На уровне платформы: каждый запрос приходит с токеном, токен резолвится в project_id, любой SQL содержит WHERE project_id = ? от текущего токена. В BOTIX это закреплено правилом №6 в CLAUDE.md и проверяется через Auth::assertProjectAccess($projectId) в начале каждого контроллера, работающего с проектом; контекст без сессии ставит Auth::setApiKeyContext($projectId) в middleware PublicApiAuth. Важная деталь дизайна: при попытке достать ресурс чужого проекта возвращается 404, а не 403 — чтобы не утекало само существование объектов.
На уровне интегратора нужна та же дисциплина. Если SDK инициализируется один раз глобально и используется во всех ботах, любая ошибка в параметрах уводит запрос на чужой проект. Правильный паттерн — один инстанс SDK на проект:
import botix
# один инстанс SDK на project_id; ключи — из secret manager, не из общего конфига
clients = {
project_id: botix.Client(api_key=XXXXXXXX(project_id))
for project_id in active_projects
}
def send_to_project(project_id: int, contact_id: int, text: str):
client = clients[project_id]
client.messages.create(contact_id=contact_id, text=text)Инстансы кэшируются на старте, ключи берутся из защищённого хранилища, и в момент вызова невозможно случайно перепутать project_id с api_key. Если у одного project_id ключ ротируется — пересоздаётся только один инстанс, остальные продолжают работать. Почему SDK при этом не тянет 14 зависимостей и как это влияет на multi-tenant-деплой — в разборе Zero-dependency SDK в open-source: стратегия платформы.
Scopes — третий слой защиты. По умолчанию ключ может всё, что доступно тарифу проекта. На практике это редко нужно: сервису отправки сообщений не нужны права на удаление контактов. BOTIX поддерживает scopes на уровне ключа (btx_api_keys.scopes, JSON) и на уровне OAuth-токена (btx_oauth_access_tokens.scopes). Middleware Public API при каждом запросе проверяет, что эндпоинт входит в scope токена; несоответствие — 403 INSUFFICIENT_SCOPE. Список реальных scope: contacts:read, contacts:write, messages:read, messages:send, scenarios:read, scenarios:run, chats:read, channels:read, webhooks:read, webhooks:write.
На стороне интегратора это значит: на каждый изолированный кусок логики — отдельный ключ с минимальным scope. Сервис рассылки — ключ с messages:send. Аналитический воркер, раз в час читающий историю, — отдельный ключ с messages:read. Если оба сервиса упадут в общий лог, ни один из ключей не даст компрометации полного контура.
Ротация без даунтайма. Плановый ротейт раз в полгода — гигиена, внеплановый после утечки — обязателен. У BOTIX два состояния ключа через btx_api_keys.status (active / revoked) и поле revoked_at: выпускается новый ключ, инстанс SDK переключается на него, затем старый помечается revoked. Все три шага независимы. Отдельно есть жёсткий хук: при отзыве подписки или переходе на тариф с api_enabled=0 все ключи проекта авто-revoked (реализация в confirmInternal).
Audit-логи. Финальный слой — журнал всех вызовов: btx_api_log пишет на каждый запрос метод, эндпоинт, статус-код, project_id, response_time_ms и request_id (UUID); retention 90 дней, фильтр по ключу и статусу — на странице API-ключей клиента (/settings?tab=api-keys). Журнал решает три задачи: отладку, расследование инцидентов и оптимизацию (что вызывается чаще всего — может, пора на webhook).
Чек-лист архитектора multi-tenant:
- Ключи хранятся отдельно на каждый
project_idи кэшируются на старте процесса. - Scope каждого ключа сужен до минимально необходимого.
- Каждая операция явно указывает
project_id— нет глобального «текущего проекта». - Ротация ключа возможна без остановки обработчика.
- Журнал API-вызовов читается из платформы и параллельно пишется внутри интегратора.
Пропущен хоть один пункт — архитектура работает только до первого инцидента.
White-label: что нужно от API, чтобы стать платформой для партнёров
Multi-tenant решает задачу одного интегратора со ста клиентами. White-label — следующий уровень: партнёр встраивает API внутрь своего продукта и продаёт его дальше под своим брендом. Партнёр — агентство под своим брендом, ISV со своим SaaS, системный интегратор, собирающий решения из чужих кирпичей.
Чем партнёрская модель отличается от прямого использования. У прямого клиента контур простой: один аккаунт, один проект, несколько ключей, один webhook. У партнёра — многоуровневый: сам партнёр имеет аккаунт у поставщика, внутри него десятки-сотни конечных клиентов, у каждого свой логический проект со своими данными, биллингом, webhook'ом. Обычный ключ работает в контексте одного проекта — партнёрскому нужно работать в контексте партнёрского аккаунта и переключаться между конечными. Скрыть это под тем же эндпоинтом — путь к ошибкам.
Минимальный набор свойств, без которых API не станет платформой:
- Партнёрский уровень аутентификации — отдельный тип ключа с правами создавать проекты, перечислять их, получать ключи для каждого. Обращение к партнёрским операциям обычным ключом должно возвращать 403.
- Sub-проекты под одним партнёрским аккаунтом — иерархия «партнёр сверху, конечные проекты снизу», каждый со своими данными, webhook'ом, биллингом.
- OAuth-installations с реселлерскими scope — чтобы приложение, установленное в партнёрский аккаунт, видело все sub-проекты, а не только тот, куда установлено напрямую.
- Биллинг-события через API — сырые usage-данные (запросы, сообщения, активные контакты) по каждому sub-проекту за период: партнёр биллит по своей модели.
- Audit-логи на партнёрском уровне — когда конечный клиент пишет «не работает», партнёр диагностирует сам, не привлекая поставщика.
- Кастомные домены и брендирование — если в решении есть UI, конечный клиент заходит в кабинет под брендом партнёра.
Изоляция данных — критическая граница. Внутри одного партнёрского аккаунта могут жить sub-проекты конкурирующих компаний. Возможность одного увидеть данные другого — катастрофа. Граница нужна на трёх уровнях: БД (WHERE project_id = ? в каждом запросе), API (явный project_id + проверка принадлежности партнёрскому аккаунту), ключи (нельзя получить ответ по одному sub-проекту, передав ключ другого). В BOTIX базовый слой этой защиты уже боевой — тот же Auth::assertProjectAccess($projectId) в начале каждого эндпоинта.
Что из этого реально в BOTIX сегодня — честно. Партнёрский слой в BOTIX собран не целиком. Что работает в проде: OAuth-installations (btx_oauth_installations) — третье приложение регистрируется, конечный клиент устанавливает его в свой проект, аудит пишется в btx_api_log с колонкой oauth_app_id (какое приложение что делало от имени какого пользователя). Отдельно живёт реферальная партнёрская программа (ставка 20% на 12 месяцев, referral_code, таблицы btx_partner_*) — но это модель «привёл клиента — получаешь процент», а не технический white-label. Полноценный partner-API с sub-проектами, партнёрскими ключами и GET /partner/usage — в roadmap, публичного релиза нет. Текущая партнёрская работа идёт через OAuth-installations и per-project ключи, без кастомного фронтенда. Это ограничение, а не фича, и мы его не прячем.
Чего делать не стоит (грабли, на которые наступают все): не навешивать партнёрские возможности на обычные эндпоинты флагами «если ты партнёр, передай ещё параметр» — лучше отдельный префикс /partner/v1/*; не делать партнёрский API «по запросу избранным под NDA» — работает, пока партнёров три, на двадцати начинаются разные SLA и теневая документация; не ставить высокий порог входа — на старте партнёрский слой это маленькие команды, из которых вырастают крупные. Решение о партнёрском слое — продуктовое, не техническое: не каждый API должен быть платформой. Но если решение принято, семь свойств выше закладываются с первого дня, а не на третьей итерации после жалоб. Как генерировать партнёрские SDK из единой спецификации — в разборе SDK из OpenAPI: генератор, white-label, open-source.
Open-source SDK: security-аудит без NDA, bug fix через PR, сигнал зрелости
Третье свойство платформы — открытый SDK. В большинстве команд вопрос «открывать ли исходник» обсуждают через категорию «щедрость»: «принесём пользу сообществу» против «лучше потратим время на фичи». Так обсуждение не приходит к решению, потому что стороны рассуждают о морали, а не о бизнесе. На деле open-source SDK — инструмент с конкретными функциями.
Функция 1. Security-аудит без участия поставщика. Корпоративная интеграция почти всегда проходит через security-команду. Закрытая библиотека — чёрный ящик, который что-то делает с данными и куда-то их отправляет. Три исхода: заблокировать интеграцию (частый), потребовать SOC 2 Type II + DPA (десятки тысяч долларов и месяцы), попросить исходник под NDA (недели). Open-source закрывает все три: security открывает GitHub-репозиторий, видит, что библиотека делает только заявленное, и пропускает. SOC 2 всё равно нужен для инфраструктуры и процессов, но SDK как точка входа в код корпорации становится прозрачным.
Три SDK BOTIX выложены в организацию BOTIX-pro на GitHub под открытой лицензией:
composer require botix-pro/sdk:^1.1
pip install botix==1.1.0
npm install @botix/sdk@1.1.0Каждый репозиторий — sdk-php, sdk-python, sdk-node — содержит полный исходник: никакого обфусцированного клиента, никаких бинарных модулей. Security читает код за час.
Функция 2. Bug fix не блокируется вендором. В закрытом SDK любая ошибка — тикет в поддержку, новая версия через месяц-три, всё это время у интегратора костыль. В open-source команда сама делает PR либо временно патчит через форк по VCS-ссылке:
{
"require": { "botix-pro/sdk": "dev-fix-retry-bug" },
"repositories": [{ "type": "vcs", "url": "https://github.com/my-org/sdk-php-fork" }]
}Никакой блокировки. Хороший фикс уходит в upstream PR через неделю — и достаётся всем интеграторам.
Функция 3. Сигнал зрелости API. Публикация SDK на GitHub — сигнал стабильного контракта, который не стыдно показать. Закрытый SDK читается как «наколхозили, поэтому прячут». Сигнал работает не на сознательном уровне: покупатель говорит «знакомая компания, серьёзные люди» после того, как его инженер посмотрел репозиторий и не нашёл ничего стыдного. В BOTIX это встроено с старта: три SDK с одинаковой структурой методов, три example-репозитория (example-laravel, example-express, example-flask), CLI botix-cli в PyPI. Структура client.contacts.create(...) одинакова в PHP, Python и Node — переключаясь между языками, разработчику не нужно переучиваться.
Функция 4. Канал контрибьюций. На ранних этапах open-source SDK — односторонняя отдача: поставщик пишет, сообщество потребляет. По мере роста появляются PR с типичными улучшениями (retry, timeout, прокси, совместимость с версией зависимости) — часы, которые поставщик не потратил. У зрелых SDK (Stripe, Twilio, Slack) — десятки внешних контрибьюторов; у молодых — единицы или ноль, и это нормально. SDK BOTIX молодые, эффект оценим через 90 дней после релиза. Бонус — issue tracker как форма документации: ответ на неочевидное поведение остаётся в архиве GitHub, и через полгода следующий интегратор находит его поиском, не открывая нового тикета.
Страхи против open-source — что с ними не так. «Код утащат» — утащить SDK без API невозможно, а под свой API конкуренту переписать легче, чем переиспользовать чужой. «Раскроется архитектура» — решается дисциплиной: SDK делает три вещи (формирует запрос, подписывает, парсит ответ), бизнес-логика и расчёты — на стороне API; открытый SDK не раскрывает ничего сверх того, что и так в OpenAPI-спецификации. «Поддержка станет дороже» — реальная работа, но без неё open-source SDK превращается в кладбище с просрочкой по 200 дней; в BOTIX issue tracker пока почти пустой, с ростом — SLA на triage 48 часов.
Что в BOTIX: все SDK и CLI
Все три SDK и CLI — на GitHub в организации BOTIX-pro:
| Репо | Пакет |
|---|---|
sdk-php | Composer botix-pro/sdk |
sdk-python | PyPI botix |
sdk-node | npm @botix/sdk |
cli | PyPI botix-cli |
example-laravel | Пример Laravel 11 + Pest |
example-express | Пример Express 4 + TypeScript + Vitest |
example-flask | Пример Flask 3 + pytest |
SDK сгенерированы из единого openapi.yaml (v1.1.0) через openapi-generator-cli — структура методов одинакова на всех трёх языках. Зависимостей нет: PHP использует ext-curl, Python — urllib.request, Node — встроенные https/http. composer audit, npm audit, safety check показывают зелёный статус не из-за везения, а по дизайну. Почему generator, а не ручные SDK, и как это удерживает три языка в синхроне — в разборе SDK из OpenAPI: генератор, white-label, open-source.
Пакеты публикуются в стандартные менеджеры:
composer require botix-pro/sdk:^1.1
pip install botix==1.1.0
npm install @botix/sdk@1.1.0
pip install botix-cliКогда всё это не нужно
Открытый SDK и партнёрский слой — не для каждого API. Отложить стоит, если: API нишевой и вся аудитория по NDA; API в стадии активного breaking-change (нестабильность в публичном репозитории даёт обратный сигнал); нет человека на review PR и ответы на issues. Все три — про ранние стадии. Когда продукт зреет и ресурс появляется, открытие и платформизация становятся логичным шагом — и именно тогда закладывают процесс: лицензию, contribution guide, регламент релизов, отдельную документацию. Полный список публичных пакетов — на developers.botix.pro; спецификация — developers.botix.pro/openapi.yaml.
Попробуйте API BOTIX
Получите ключ, отправьте первый запрос и поймайте webhook — прямо в браузере, без боевого канала.
Попробовать API бесплатноЧитайте также
Zero-dependency SDK, open-source и партнёрский API
Генерация SDK из OpenAPI, white-label и open-source
Как публичный API становится платформой для интеграторов
Multi-tenant API: OAuth, scopes и изоляция project_id
