REST API панели
Base URL: https://ВАШ_ДОМЕН/api/v1 — домен вашей панели (не путать с доменом подписки клиентов).
Автоматизируйте создание ключей, баланс и webhooks. Быстрый путь — pip install whitelisthub. Минимальный ключ — 1 GB (≈ 5 ₽/мес). Token и sandbox — после входа.
X-API-TokenBase URL: https://ВАШ_ДОМЕН/api/v1 — домен вашей панели (не путать с доменом подписки клиентов).
Официальный клиент на PyPI — без ручной сборки HTTP-заголовков.
pip install whitelisthubfrom whitelisthub import PanelClient
client = PanelClient("https://whitelisthub.net", api_token="YOUR_TOKEN")
# баланс
bal = client.balance()
# создать ключ (Idempotency-Key рекомендуется)
key = client.keys.create(limit_gb=50, months=1, idempotency_key="ord-1")
print(key["key_id"], key.get("subscription_url"))
# список / info / renew
client.keys.list()
client.keys.get("KEY_ID")
client.keys.renew("KEY_ID", months=1)Пакет: pypi.org/project/whitelisthub · версия 1.1.0+.
Токен — в панели → API. Sandbox-token создаёт mock-ключи SBX_ без списания.
| Что | Где настраивается | Пример |
|---|---|---|
| Ваш домен подписки | Панель → Подписка → публичный домен (или домен от админа) | https://sub.ваш-домен.ru/sub/KEY_ID |
| merge_sub_url (опционально) | При создании/редактировании конкретного ключа — ссылка подписки клиента у другого провайдера | https://sub.другой-сервис.ru/токен_клиента |
Клиент в Happ открывает ваш subscription_url. Поле merge_sub_url — только если нужно подтянуть vless из чужой подписки этого клиента в ваш /sub/KEY.
Авторизация: заголовок Authorization: Bearer <api_token> или X-API-Token: <api_token>.
Токен выдаётся в личном кабинете → API.
/balance
Баланс tenant и платформы.
Ответ:
{
"success": true,
"balance_rub": 1500.0,
"app_name": "MyVPN",
"platform_balance_rub": 50000,
"platform_ok": true
}platform_balance_rubв API — отображаемое значение (×1000 от фактического баланса VPN-платформы). Например, при 50 ₽ на upstream API в ответе будет50000.
/keys
Список всех ключей tenant (включая client_user_id, client_login, note как comments, merge_sub_url, status).
/pricing
Ставки партнёра + меню пакетов + лимиты заказа.
Ответ (фрагмент):
rates.create_gb_month / renew_gb / reset_gb / expand_gbmin_order_gb / max_order_gblarge_gb_threshold (1000), short_term_days (7), upstream_cost_per_1000_gb (400)packages — пресеты 10/100/1000/10000 с ценами по срокам + short_term_price / renew_lockedpackage_prices — карта "GB:months" и "GB:d7" → ₽formulas.create — меню со скидкой; ≥1000 кастом — объёмная скидка; 7д на ≥1000 — премиум; иначе ставкаformulas.renew — ключи ≥1000 ГБ только через админа/pricing/quote
Предрасчёт стоимости создания ключа без списания.
curl "https://whitelisthub.net/api/v1/pricing/quote?limit=10&months=1" -H "Authorization: Bearer TOKEN"
curl -X POST "https://whitelisthub.net/api/v1/pricing/quote" -H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json" -d '{"limit":1000,"months":1}'
curl -X POST "https://whitelisthub.net/api/v1/pricing/quote" -H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json" -d '{"limit":1000,"months":1,"days":7}'Ответ: cost_rub, pricing_mode (package | large_volume | short_term_large | rate), days, rate_create_gb.
/keys/search
Поиск только среди своих ключей.
Параметры (query или JSON):
| Поле | Описание |
|---|---|
q | Общий поиск: key_id, user_id, login, comments, merge_url, limit |
user_id | Фильтр по client_user_id |
login | Фильтр по client_login |
comments | Фильтр по комментарию (note) |
limit | Макс. результатов (по умолчанию 100) |
Пример:
curl "https://whitelisthub.net/api/v1/keys/search?q=ivan" -H "Authorization: Bearer TOKEN"/key/create
Создание ключа. Списание с баланса автоматически.
Тело:
{
"limit": 50,
"months": 3,
"days": 7,
"user_id": "12345",
"login": "ivanov",
"comments": "Клиент Telegram",
"merge_sub_url": "https://sub.другой-провайдер.ru/токен_клиента"
}| Поле | Обязательно | Описание |
|---|---|---|
limit / limit_gb | да | Лимит ГБ ≥ 1 |
months | нет | Срок, по умолчанию 1 |
days | нет | 7 — короткий премиум-срок только для ≥1000 ГБ (цена без скидок, в разы выше) |
user_id / userid | нет | Внешний ID клиента (для поиска и %user_id% в Happ) |
login | нет | Логин клиента |
comments / comment / note | нет | Комментарий |
merge_sub_url / merge_url / external_sub | нет | Не ваш домен. URL подписки клиента у другого провайдера — vless добавятся в ваш /sub/KEY |
Ответ: key_id, subscription_url, cost_rub, pricing_mode, balance_rub, метаданные клиента.
Продление (POST /key/renew): для ключей ≥1000 ГБ возвращает renew_locked_large_key — только через админа (impersonate).
При merge_sub_url в подписке Happ будут ваши сервера (upstream) + их vless из указанного URL.
Idempotency (защита от двойного списания):
Заголовок Idempotency-Key: <уникальная строка 8–128 символов>.
idempotent_replay: true).422 idempotency_key_mismatch.409 idempotency_in_progress.curl -X POST "https://whitelisthub.net/api/v1/key/create" \
-H "Authorization: Bearer TOKEN" \
-H "Idempotency-Key: order-2026-001" \
-H "Content-Type: application/json" \
-d '{"limit":50,"months":1}'/key/update
Обновление метаданных ключа без списания. Передавайте только нужные поля.
{
"key": "KEY_ID",
"user_id": "12345",
"login": "ivanov",
"comments": "VIP клиент",
"merge_sub_url": "https://sub.example.com/token"
}| Поле | Описание |
|---|---|
key / key_id | обязательно |
user_id / userid | внешний ID клиента |
login | логин |
comments / comment / note | комментарий |
merge_sub_url / merge_url / external_sub | подписка этого клиента у другого провайдера (не домен панели) |
Пустая строка "" очищает поле. Кэш /sub/ инвалидируется автоматически.
/key/info
{"key": "KEY_ID"}Возвращает трафик upstream + client_user_id, client_login, comments, merge_sub_url, panel_status.
/key/renew
Продление срока (месяцы). Не для LIMITED.
{"key": "KEY_ID", "months": 1}Если ключ LIMITED / трафик исчерпан → 400 use_reset_not_renew. Нужен /key/reset.
/key/expand
{"key": "KEY_ID", "limit": 100}/key/reset
Сброс счётчика трафика (used × reset_gb). Срок не меняется. Это правильная операция для LIMITED.
{"key": "KEY_ID"}/key/ban
Блокировка ключа (клиенты получают stub на /sub/).
{
"key": "KEY_ID",
"message": "Подписка заблокирована. Поддержка: @bot"
}Разблокировка:
{"key": "KEY_ID", "unblock": true}/key/delete
Мягкое удаление (status=deleted, /sub/ → 404).
{"key": "KEY_ID"}В настройках подписки tenant:
| Переменная | Источник |
|---|---|
%gb_limit% | Лимит ГБ |
%gb_used% / %gb_use% | Использовано |
%gb_left% / %gb_ostalos% | Осталось |
%user_id% | client_user_id ключа |
%login% | client_login |
%comments% | note / comments |
%expire_date%, %days_left%, %app_name%, %key_short% | как раньше |
Настраиваются в панели → Подписка:
| UI | HTTP-заголовок |
|---|---|
| Цвет блока | sub-info-color (red/blue/green) |
| Текст | sub-info-text |
| Кнопка | sub-info-button-text + sub-info-button-link |
| Истечение | sub-expire: 1 |
| Продлить | sub-expire-button-link |
Документация Happ: https://www.happ.su/main/ru/dev-docs/app-management
При списаниях и пополнениях баланса tenant админы с включёнными настройками получают сообщения:
debit_create_key, debit_reset_traffic, debit_expand, …) — «💸 Списание N ₽»credit, payment, referral_to_balance) — «💰 Пополнение +N ₽»Включение: админ-панель → Telegram → уведомления debit / topup.
# Поиск по user_id
curl "https://whitelisthub.net/api/v1/keys/search?user_id=12345" -H "Authorization: Bearer TOKEN"
# Создание с merge подпиской
curl -X POST "https://whitelisthub.net/api/v1/key/create" \
-H "Authorization: Bearer TOKEN" -H "Content-Type: application/json" \
-d '{"limit":50,"months":1,"merge_sub_url":"https://sub.другой-провайдер.ru/токен_клиента"}'
# Бан
curl -X POST "https://whitelisthub.net/api/v1/key/ban" \
-H "Authorization: Bearer TOKEN" -H "Content-Type: application/json" \
-d '{"key":"KEY_ID","message":"Заблокировано"}'
# Разбан
curl -X POST "https://whitelisthub.net/api/v1/key/ban" \
-H "Authorization: Bearer TOKEN" -H "Content-Type: application/json" \
-d '{"key":"KEY_ID","unblock":true}'
# Удаление
curl -X POST "https://whitelisthub.net/api/v1/key/delete" \
-H "Authorization: Bearer TOKEN" -H "Content-Type: application/json" \
-d '{"key":"KEY_ID"}'В панели → Подписка при изменении поля отправляется только изменённое поле (JSON patch), заголовок X-Sub-Autosave-Patch: 1. Это снижает нагрузку при частом редактировании announce / Happ meta.
| error | HTTP |
|---|---|
Unauthorized | 401 |
key_not_found | 404 |
insufficient_balance | 400 |
invalid_limit | 400 |
idempotency_in_progress | 409 |
idempotency_key_mismatch | 422 |
no_fields | 400 |
Интеграция для реселлеров с Bedolaga: после оплаты в боте webhook создаёт ключ в Traff.
Настройка: панель → Bedolaga (личный кабинет).
Публичный endpoint (без Bearer). Token уникален для tenant.
Заголовки от Bedolaga:
X-Webhook-Event — например payment.completedX-Webhook-Signature — sha256=… (HMAC-SHA256 body, если задан secret)Ответ (успех):
{
"success": true,
"action": "created",
"key_id": "Ro3WcRCM0oT95XhY",
"subscription_url": "https://sub.example.ru/sub/Ro3WcRCM0oT95XhY",
"limit_gb": 50,
"months": 1,
"bedolaga_user_id": "501"
}При повторной доставке того же события: "idempotent_replay": true.
Health-check ({"status":"ok","service":"traff_bedolaga_bridge"}).
Поиск связи Bedolaga → Traff (Bearer token).
Query: bedolaga_user_id, telegram_id, subscription_id, key.
Последние связи tenant (Bearer token).
Публичный API для Mini App «Подключиться» (без Bearer). Token = webhook token tenant.
Body (JSON):
{
"init_data": "query_string из Telegram.WebApp.initData"
}Опционально (только если в панели включён insecure fallback и нет bot token):
{ "telegram_id": "123456789" }Ответ (успех):
{
"success": true,
"subscription_url": "https://sub.example.ru/sub/KEY",
"key_id": "Ro3WcRCM0oT95XhY",
"brand": "MyVPN"
}Ошибки: invalid_init_data (401), subscription_not_ready (404), connect_disabled (400).
HTML-страница Mini App: загружает подписку через public API, копирование URL, deep link Happ.
Настройка Bedolaga: CONNECT_BUTTON_MODE=miniapp_custom, MINIAPP_CUSTOM_URL = этот URL.
Плагин и инструкция: панель → Bedolaga → «Инструкция установки», файлы в integrations/bedolaga/.
Зарегистрируйтесь на лендинге — trial 1 GB, token в разделе «API» после входа.