HTTP API и вебхуки
Всё, что делает админка, доступно и через HTTP API: биллинг, мониторинг или свой скрипт управляют панелью так же, как администратор. В обратную сторону работают вебхуки — панель сама сообщает внешней системе о событиях. Биллингу обычно нужно и то, и другое. Готовый модуль для WHMCS — Модуль WHMCS.
API-ключи
API-ключ — пароль для программы. С ним внешняя система работает от своего имени, без логина сотрудника и кода 2FA. Ключи выпускает только администратор: Доступ → API-ключи → Новый ключ, название и скоуп:
read— только чтение: ноды, тарифы, каталог. Изменить через него ничего нельзя. Игровых серверов такой ключ не видит — у ключа нет владельца, а серверы привязаны к владельцам.admin— полный доступ ко всему API, как у администратора. Нужен биллингу. Выдавайте его только системам, которым доверяете так же, как администратору.
Ключ вида gmk_… показывается один раз — панель хранит только его хэш. Потеряли — отзовите и выпустите новый.
Ключ передаётся в заголовке каждого запроса:
curl -H "Authorization: Bearer gmk_…" https://panel.example.com/api/nodes
Советы:
- Выпускайте отдельный ключ для каждой системы. Тогда в списке видно, когда каждый ключ использовался, в журнале аудита действия записаны под его именем, а отзыв одного ключа не ломает остальные интеграции.
- Храните ключ в настройках или переменных окружения системы, не в коде и не в репозитории.
- Ключ утёк — Отозвать: он перестаёт работать сразу. Отозванный ключ остаётся в списке, чтобы не терялась история в аудите.
- На каждый ключ действует лимит: по умолчанию 20 запросов в секунду со всплеском до 200. При превышении панель отвечает
429. Лимит меняется в Настройках.
Описание методов
Полное описание API — в самой панели по адресу /api/docs/ (Swagger): методы, поля, коды ответов. Там же запросы можно попробовать со своим ключом.
Своя интеграция с биллингом
Отдельного «биллингового API» нет — биллинг пользуется обычными методами с ключом admin. Услуга биллинга — это игровой сервер с полем external_id (номер услуги у вас), клиент биллинга — пользователь панели с ролью клиента (viewer).
- Создать услугу:
GET /api/users?email=…; если клиента нет —POST /api/usersсrole: viewer; затемPOST /api/game-serversсplan_id,location_id,owner_user_idиexternal_id. - Найти сервер услуги:
GET /api/game-servers?external_id=…. - Приостановить / возобновить:
POST /api/game-servers/{id}/suspend({"reason": "…"}) /…/unsuspend. - Сменить тариф:
PATCH /api/game-servers/{id}/plan({"plan_id": "…"}). - Удалить услугу:
DELETE /api/game-servers/{id}. - Сменить пароль клиенту:
PATCH /api/users/{id}({"password": "…"}) — его сессии завершатся.
curl -X POST -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \
-d '{"plan_id":"plan_…","location_id":"loc_fra","owner_user_id":"…","external_id":"billing-1042"}' \
https://panel.example.com/api/game-serversexternal_id уникален: повторный POST с тем же номером вернёт 409 и второй сервер не создаст. Поэтому запрос безопасно повторять после таймаута — найдите сервер по external_id и продолжайте. Задаётся номер только при создании.
Ресурсы сервера меняются только через тариф: для особого случая заведите отдельный тариф и переведите сервер на него.
Вебхуки
Вебхук — POST-запрос с JSON о событии на ваш URL. Интеграции → Вебхуки → Новый вебхук: URL получателя и события. Все события включает и те, что появятся в следующих версиях, поэтому получатель должен спокойно пропускать незнакомые типы.
Основные события:
server.created,server.started,server.stopped,server.deleted— что реально произошло на ноде (не момент запроса к API);server.suspended,server.unsuspended— приостановка и возобновление;server.failed— сервер не поднялся после всех попыток, в данных есть текст ошибки;node.offline,node.online— нода пропала или вернулась;backup.completed,backup.failed;schedule.run.completed,schedule.run.failed— запуски расписаний;database.created,database.deleted— базы данных серверов (без пароля).
В данных событий серверов есть external_id и connect_address — по ним удобно найти услугу и отправить клиенту адрес для подключения.
Тело запроса:
{"id": "evt_…", "type": "server.created", "created_at": "…", "data": {"id": "gs_…", "external_id": "billing-1042", "connect_address": "203.0.113.10:27015", "…": "…"}}Проверка подписи
После создания вебхука панель один раз показывает секрет. Каждый запрос подписан заголовком X-Webhook-Signature: t=<unix-время>,v1=<hex>, где v1 — HMAC-SHA256 от строки <t>.<тело запроса> с этим секретом. Считайте подпись по сырому телу, до разбора JSON, и отбрасывайте запросы со старым t:
sig = dict(p.split("=", 1) for p in request.headers["X-Webhook-Signature"].split(","))
expected = hmac.new(secret.encode(), f"{sig['t']}.".encode() + request.body, hashlib.sha256).hexdigest()
ok = hmac.compare_digest(expected, sig["v1"]) and abs(time.time() - int(sig["t"])) < 300Секрет потерян или утёк — Сменить секрет; старый перестаёт действовать сразу.
Повторы и гарантии
- Доставка успешна только при ответе
2xxза 10 секунд. Редиректы считаются ошибкой. - После ошибки панель повторяет отправку с растущей паузой (30 секунд, минута, две… до часа), всего до 10 попыток. Потом доставка получает статус ошибки, и её можно отправить заново кнопкой Повторить в журнале доставок.
- Одно событие может прийти дважды, а порядок не гарантирован. Отбрасывайте повторы по
idсобытия (он же в заголовкеX-Webhook-Id) и не полагайтесь на очерёдность — при сомнении запросите актуальное состояние через API. - Проверить получателя можно кнопкой Отправить тестовое событие; код ответа и текст ошибки видны в Доставках.