Void API — твои домены, серверы и хостинг по REST.
Регистрация и продление доменов, Cloudflare и DNS, заказ и управление VPS-серверами и веб-хостингом — из своего кода, на том же аккаунте и балансе, что и бот.
Серверы
KVM VPS: заказ, питание, оплата, файрволТе же серверы, что и в боте, на том же балансе. Заказ списывает первый срок и ставит создание в очередь: ответ — 201 с сервером в статусе PENDING; опрашивай GET /servers/{id}, пока он не станет ACTIVE (около минуты для Linux; Windows дольше и проходит через PROVISIONING). Секреты — пароль root / Administrator и логин предустановленного приложения — никогда не попадают в списки, детали и ответ на заказ: читай их только через GET /servers/{id}/credentials.
Питание, переустановка, апгрейд и файрвол требуют сервер в статусе ACTIVE (иначе 400 SERVER_NOT_ACTIVE) и выполняются по одной: пока другая операция в очереди или идёт, сервер отвечает 409 SERVER_BUSY.
# 1. что можно заказать прямо сейчас: образы, приложения, регионы, лимиты конфигуратора
curl -s "$API/servers/catalogue" -H "Authorization: Bearer $VOID_KEY"
# 2. заказ (списывает первый срок, возвращает 201 + сервер в PENDING)
curl -s -X POST "$API/servers" -H "Authorization: Bearer $VOID_KEY" \
-H "Content-Type: application/json" \
-d '{"spec":{"vcpu":2,"ramGb":4,"diskGb":80},"os":"ubuntu-24","period":"MONTH"}'
# 3. опрашиваем до "status":"ACTIVE", затем читаем пароль root
curl -s "$API/servers/$ID" -H "Authorization: Bearer $VOID_KEY"
curl -s "$API/servers/$ID/credentials" -H "Authorization: Bearer $VOID_KEY"// call() — как в быстром старте
const server = await call("POST", "/servers", {
spec: { vcpu: 2, ramGb: 4, diskGb: 80 }, os: "ubuntu-24", period: "MONTH",
});
let s = server;
while (s.status === "PENDING" || s.status === "PROVISIONING") {
await new Promise((r) => setTimeout(r, 15_000));
s = await call("GET", `/servers/${server.id}`);
}
if (s.status !== "ACTIVE") throw new Error(`provisioning ended in ${s.status}`);
const { ip, login, password } = await call("GET", `/servers/${server.id}/credentials`);# call() — как в быстром старте
import time
server = call("POST", "/servers", spec={"vcpu": 2, "ramGb": 4, "diskGb": 80}, os="ubuntu-24", period="MONTH")
s = server
while s["status"] in ("PENDING", "PROVISIONING"):
time.sleep(15)
s = call("GET", f"/servers/{server['id']}")
if s["status"] != "ACTIVE":
raise RuntimeError(f"provisioning ended in {s['status']}")
creds = call("GET", f"/servers/{server['id']}/credentials") # ip, login, password, appЗаказ и управление
Что можно заказать прямо сейчас. os[].code и apps[].code идут в заказ; в custom — лимиты конфигуратора, причём max уже учитывает, сколько ещё может взять самый свободный узел; periods — множители цены относительно месяца (скидки за срок уже внутри); proxy — можно ли сейчас заказать фронт-прокси. plans — фиксированные тарифы бота, для справки: API заказывает по spec.
{ "ok": true, "data": {
"plans": [ { "code": "…", "nameEn": "…", "nameRu": "…", "vcpu": 1, "ramMb": 1024, "diskGb": 20, "trafficGb": null, "bandwidthMbps": …,
"prices": { "MONTH": "…", "QUARTER": "…", "HALF_YEAR": "…", "YEAR": "…" } }, … ],
"os": [ { "code": "ubuntu-24", "name": "Ubuntu 24.04" }, { "code": "windows-2022", "name": "Windows Server 2022" }, … ],
"apps": [ { "code": "3x-ui", "name": "3x-ui", "descEn": "…", "descRu": "…", "baseOs": "ubuntu-24", "minRamMb": 1024, "minutes": 2 }, … ],
"regions": ["PL"],
"custom": { "cpu": { "min": 1, "max": 16, "step": 1, "monthly": 1.25 },
"ramGb": { "min": 1, "max": 64, "step": 1, "monthly": 1.5 },
"diskGb": { "min": 20, "max": 1000, "step": 20, "monthlyPer10Gb": 0.25 },
"base": { "monthly": 3 }, "defaults": { "vcpu": 2, "ramGb": 4, "diskGb": 80 } },
"periods": { "MONTH": 1, "QUARTER": 2.925, "HALF_YEAR": 5.7, "YEAR": 11.1 },
"proxy": { "available": true, "monthly": 3, "free": 12, "total": 16 } } }Ставки в custom и proxy — обычные числа, USD в месяц; значения выше — пример, бери их из ответа. Цена в месяц = base + vcpu × cpu.monthly + ramGb × ramGb.monthly + diskGb / 10 × monthlyPer10Gb (+ proxy.monthly с прокси); цена срока = месячная × periods[срок], с округлением до центов. Точная оплаченная сумма — periodPrice в ответе на заказ.
Твои серверы, новые первыми, без удалённых. Не больше 50 на страницу.
{ "ok": true, "data": { "items": [ Server, … ], "total": 2, "offset": 0, "limit": 20 } }Списывает первый срок, резервирует мощность и IPv4-адрес и ставит создание в очередь. Возвращает сервер в PENDING. Считается в лимит 20 / мин.
| тело | тип | |
|---|---|---|
specобязательно | object | { "vcpu", "ramGb", "diskGb" }, целые числа в пределах и с шагом из custom каталога (диск — шагами по 20 ГБ) |
os | string | код образа из catalogue.os; обязателен, если не указан app |
app | string | предустановленное приложение из catalogue.apps; задаёт образ baseOs (os игнорируется) и требует не меньше minRamMb памяти |
periodобязательно | MONTH QUARTER HALF_YEAR YEAR | оплачиваемый срок; продления идут на тот же срок, пока ты его не сменишь |
region | string | один из catalogue.regions; не указан — любой регион, где есть место |
proxy | boolean | фронт-прокси: веб-трафик через отдельный внешний адрес; его цена добавляется к каждому сроку. Только пока catalogue.proxy.available |
sshKeyIds | uuid[] · до 10 | SSH-ключи, сохранённые на аккаунте (управляются в боте), ставятся при первой загрузке; чужие id игнорируются |
{ "spec": { "vcpu": 2, "ramGb": 4, "diskGb": 80 }, "os": "ubuntu-24", "period": "QUARTER", "region": "PL" }
{ "spec": { "vcpu": 2, "ramGb": 2, "diskGb": 40 }, "app": "3x-ui", "period": "MONTH" }Не идемпотентно: каждый успешный вызов — ещё один оплаченный сервер. После таймаута сначала посмотри GET /servers, потом повторяй. Если создать сервер так и не удалось, он переходит в FAILED, а первый срок возвращается на баланс.
Один сервер — см. Server. У предустановленного приложения app.state идёт PENDING → INSTALLING → READY (или FAILED) уже после того, как сам сервер стал ACTIVE; app.url и app.note появляются, когда оно готово. power обновляется периодически и может немного отставать от действия.
Единственное место, где отдаются секреты. login — root, на Windows — Administrator; app добавляет адрес панели, логин и пароль предустановленного приложения, когда оно READY. Обращайся с ответом как с самим API-ключом — не пиши его в логи.
{ "ok": true, "data": { "ip": "203.0.113.25", "login": "root", "password": "…", "app": null } }
// с приложением:
{ "ok": true, "data": { "ip": "203.0.113.25", "login": "root", "password": "…",
"app": { "code": "3x-ui", "name": "3x-ui", "state": "READY", "url": "https://…", "login": "…", "password": "…" } } }Передавай только то, что меняешь; возвращает сервер.
| тело | тип | |
|---|---|---|
autoRenew | boolean | в момент истечения продлить на текущий срок с баланса; если денег не хватает, сервер приостанавливается. Только для ACTIVE / SUSPENDED |
alias | string ≤ 32 · null | твоё имя сервера; null или "" сбрасывает |
{ "autoRenew": true, "alias": "api-prod" }Питание и переустановка
Ставит действие в очередь и сразу отвечает { "queued": true }; результат смотри в GET /servers/{id}.
| тело | тип | |
|---|---|---|
actionобязательно | start stop reboot reinstall | |
os | string | только reinstall: перейти на другой образ из каталога |
app | string | только reinstall: переустановить с предустановленным приложением (его базовый образ подразумевается; минимум памяти действует) |
{ "action": "reboot" }
{ "action": "reinstall", "os": "ubuntu-24" }reinstall стирает диск. Сервер заново клонируется из чистого образа — того же, если не передан ни os, ни app. IP-адрес, размер и пароль сохраняются (образу Windows может понадобиться пароль сложнее — перечитай /credentials), правила файрвола применяются заново. Выбрать можно только образы, доступные на узле сервера, иначе 400 NO_TEMPLATE.
Продление, срок и апгрейд
Сразу списывает periodPrice и продлевает expiresAt на один срок — от более поздней из дат: текущего истечения или сейчас, так что раннее продление не съедает оплаченное время. Приостановленный (SUSPENDED) сервер запускается снова. Каждый успешный вызов добавляет ещё один срок. Возвращает сервер.
Все сроки продления с ценой по сегодняшним ставкам; current отмечает текущий срок сервера.
{ "ok": true, "data": [ { "period": "MONTH", "price": "13.50", "current": true }, { "period": "QUARTER", "price": "39.49", "current": false },
{ "period": "HALF_YEAR", "price": "76.95", "current": false }, { "period": "YEAR", "price": "149.85", "current": false } ] }{ "period": "YEAR" } — меняет срок продления. Сейчас ничего не списывается, и текущий expiresAt не сдвигается: следующее продление, автоматическое или ручное, спишет цену нового срока и продлит на новый срок. Возвращает тот же список, что и GET. Только для ACTIVE / SUSPENDED.
{ "spec": { "vcpu", "ramGb", "diskGb" } } — сколько стоит апгрейд до этого размера. Апгрейд только вверх: каждое значение не меньше текущего, хотя бы одно больше, в пределах конфигуратора (уменьшение — через поддержку). charge списывается сейчас: разница цены срока пропорционально остатку оплаченного срока — (новая periodPrice − старая periodPrice) × остаток / длина срока. periodPrice — сколько будет стоить каждое продление после апгрейда.
{ "spec": { "vcpu": 4, "ramGb": 8, "diskGb": 80 } }
{ "ok": true, "data": { "serverId": "3b0c…", "from": { "vcpu": 2, "ramGb": 4, "diskGb": 80 }, "to": { "vcpu": 4, "ramGb": 8, "diskGb": 80 },
"charge": "5.48", "periodPrice": "22.00", "period": "MONTH", "expiresAt": "2026-12-01T10:00:00.000Z" } }Сразу списывает сумму из квоты и ставит изменение размера в очередь; возвращает применённую квоту. Новые CPU и память вступают в силу через перезапуск — работающий сервер перезагружается, будь готов к короткому простою. Виртуальный диск увеличивается на месте; если гостевая ОС не расширяет раздел при загрузке, расширь его сам.
| тело | тип | |
|---|---|---|
specобязательно | object | целевой размер, как в квоте |
maxChargeобязательно | строка-сумма | максимум, который ты готов заплатить сейчас, — передай charge из квоты |
{ "spec": { "vcpu": 4, "ramGb": 8, "diskGb": 80 }, "maxCharge": "5.48" }
// цена стала выше maxCharge: ничего не списано, свежая квота — в details
{ "ok": false, "error": { "code": "UPGRADE_PRICE_CHANGED", "message": "…",
"details": { "serverId": "3b0c…", "from": { … }, "to": { … }, "charge": "5.60", "periodPrice": "22.00", "period": "MONTH", "expiresAt": "…" } } }Файрвол
Входящий allow-list на гипервизоре, перед гостевой ОС. Выключен (по умолчанию): весь входящий трафик открыт. Включён: входящий трафик отбрасывается, кроме разрешённого твоими правилами. Уже установленные соединения переживают переключение. Изоляция от других клиентов и антиспуфинг работают в любом состоянии и в этот список не входят. При включённом прокси порты 80/443 с внешнего адреса сервера (proxySource) разрешаются автоматически, чтобы прокси продолжал работать. Изменения сразу записываются на гипервизор; если запись не удалась, остаётся прежний набор, а вызов отвечает PROVIDER_ERROR.
Синтаксис правил
| правило | разрешает |
|---|---|
22 | TCP-порт 22 откуда угодно |
80,443 | несколько портов (до 10) |
8000:8100/udp | диапазон портов; /udp или /tcp (по умолчанию TCP) |
3306 from 1.2.3.4 | только с одного IPv4-адреса или из сети: from 10.0.0.0/8 (/8…/32) |
ping · icmp | ICMP; тоже принимает from … |
До 20 правил. Пока ты ничего не менял, rules показывает набор по умолчанию, который применится при включении: SSH (22, на Windows — 3389 RDP), 80,443 и ping.
{ "ok": true, "data": { "enabled": false,
"rules": [ { "proto": "tcp", "port": "22" }, { "proto": "tcp", "port": "80,443" }, { "proto": "icmp" } ],
"proxySource": null } }
// правило с источником: { "proto": "tcp", "port": "3306", "source": "1.2.3.4" }{ "enabled": true | false } — включить или выключить allow-list; правила сохраняются в обоих случаях. Возвращает файрвол.
{ "rule": "3306 from 1.2.3.4" } — добавляет одно правило в синтаксисе выше и возвращает весь файрвол. Если такое правило уже есть, ничего не меняется.
Удаляет правило на этой позиции в rules (с нуля) и возвращает файрвол. После удаления позиции сдвигаются — перечитай список, прежде чем удалять следующее. 404 NOT_FOUND, если такой позиции нет.