api v1

Void API — твои домены, серверы и хостинг по REST.

Регистрация и продление доменов, Cloudflare и DNS, заказ и управление VPS-серверами и веб-хостингом — из своего кода, на том же аккаунте и балансе, что и бот.

https://api.void.cd/v1 Bearer void_… JSON · UTC · USD строками

Быстрый старт

пять вызовов от нуля до живого домена
Создай ключ

В Telegram-боте: профиль → API → создать ключ. Показывается один раз — храни в секретах, не в коде.

Проверь имя

GET /domains/check?domain=… — сверка с реестром и точная цена, которую спишем.

Зарегистрируй

POST /domains — списывается баланс, домен встаёт на наши nameservers со страницей парковки.

Подключи Cloudflare

POST /domains/{id}/cloudflare — зона создана, nameservers в реестре переключены за тебя.

Направь куда нужно

POST /domains/{id}/dns — A-запись на твой сервер, через прокси или напрямую.

export VOID_KEY="void_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
API=https://api.void.cd/v1

# 1. свободно ли и за сколько?
curl -s "$API/domains/check?domain=example.xyz" -H "Authorization: Bearer $VOID_KEY"

# 2. регистрация (списывает баланс, возвращает 201 + объект домена)
curl -s -X POST "$API/domains" -H "Authorization: Bearer $VOID_KEY" \
  -H "Content-Type: application/json" -d '{"domain":"example.xyz"}'

# 3. подключить Cloudflare и добавить A-запись
curl -s -X POST "$API/domains/$ID/cloudflare" -H "Authorization: Bearer $VOID_KEY"
curl -s -X POST "$API/domains/$ID/dns" -H "Authorization: Bearer $VOID_KEY" \
  -H "Content-Type: application/json" \
  -d '{"type":"A","name":"@","content":"203.0.113.10","proxied":true}'
const API = "https://api.void.cd/v1";
const headers = { Authorization: `Bearer ${process.env.VOID_KEY}`, "Content-Type": "application/json" };

const call = async (method, path, body) => {
  const r = await fetch(API + path, { method, headers, body: body && JSON.stringify(body) });
  const j = await r.json();
  if (!j.ok) throw new Error(`${j.error.code}: ${j.error.message}`);
  return j.data;
};

const check = await call("GET", "/domains/check?domain=example.xyz");
if (check.available) {
  const domain = await call("POST", "/domains", { domain: "example.xyz" });
  await call("POST", `/domains/${domain.id}/cloudflare`);
  await call("POST", `/domains/${domain.id}/dns`, { type: "A", name: "@", content: "203.0.113.10", proxied: true });
}
import os, requests

API = "https://api.void.cd/v1"
H = {"Authorization": f"Bearer {os.environ['VOID_KEY']}"}

def call(method, path, **body):
    r = requests.request(method, API + path, headers=H, json=body or None, timeout=30).json()
    if not r["ok"]:
        raise RuntimeError(f"{r['error']['code']}: {r['error']['message']}")
    return r["data"]

check = call("GET", "/domains/check?domain=example.xyz")
if check["available"]:
    domain = call("POST", "/domains", domain="example.xyz")
    call("POST", f"/domains/{domain['id']}/cloudflare")
    call("POST", f"/domains/{domain['id']}/dns", type="A", name="@", content="203.0.113.10", proxied=True)

Пример: от покупки до A-записи

все запросы и ответы одного реального сценария

Один домен от начала до конца: проверка → регистрация → подключение Cloudflare → ожидание зоны → записи → SSL. Ответы сокращены только там, где стоит …. Один раз задай H='Authorization: Bearer void_…' — он нужен в каждом вызове.

1 · Свободно?

curl -s "https://api.void.cd/v1/domains/check?domain=steamgaloshi.world" -H "$H"

{ "ok": true, "data": { "domain": "steamgaloshi.world", "available": true, "price": "8.00" } }

2 · Регистрация

curl -s -X POST https://api.void.cd/v1/domains -H "$H" -H "Content-Type: application/json" \
  -d '{"domain":"steamgaloshi.world"}'

HTTP/2 201
{ "ok": true, "data": {
    "id": "6f1c2e0e-3f6a-4a4b-9a1d-8f5e9b2c7d10", "domainName": "steamgaloshi.world", "status": "ACTIVE",
    "nameservers": ["n1.void.cd", "n2.void.cd"],
    "price": "8.00", "renewalPrice": "8.00", "renewalYears": 1, "autoRenew": false,
    "expiresAt": "2027-08-31T19:45:54.000Z", "createdAt": "2026-08-31T19:45:50.714Z", "blockFlags": [] } }
# баланс: 25.07 → 17.07; имя уже резолвится на нашу страницу парковки

3 · Подключаем Cloudflare

ID=6f1c2e0e-3f6a-4a4b-9a1d-8f5e9b2c7d10
curl -s -X POST https://api.void.cd/v1/domains/$ID/cloudflare -H "$H"

HTTP/2 201
{ "ok": true, "data": { "connected": true, "domainName": "steamgaloshi.world", "zoneId": "9b3e…",
    "nameservers": ["ada.ns.cloudflare.com", "kip.ns.cloudflare.com"], "zoneStatus": "pending", "nsApplied": true } }
# nsApplied: реестр уже смотрит на Cloudflare; zoneStatus станет "active", когда Cloudflare это увидит

4 · Записи можно добавлять сразу (ждать "active" не нужно)

curl -s -X POST https://api.void.cd/v1/domains/$ID/dns -H "$H" -H "Content-Type: application/json" \
  -d '{"type":"A","name":"@","content":"203.0.113.10","proxied":true}'

HTTP/2 201
{ "ok": true, "data": { "id": "a1f0…", "type": "A", "name": "steamgaloshi.world", "content": "203.0.113.10", "proxied": true, "priority": null } }

curl -s -X POST https://api.void.cd/v1/domains/$ID/dns -H "$H" -H "Content-Type: application/json" \
  -d '{"type":"CNAME","name":"www","content":"steamgaloshi.world","proxied":true}'

HTTP/2 201
{ "ok": true, "data": { "id": "b7c2…", "type": "CNAME", "name": "www.steamgaloshi.world", "content": "steamgaloshi.world", "proxied": true, "priority": null } }

5 · Ждём, пока зона станет активной

until curl -s -X POST https://api.void.cd/v1/domains/$ID/cloudflare/refresh -H "$H" | grep -q '"zoneStatus":"active"'; do sleep 60; done

{ "ok": true, "data": { "connected": true, "domainName": "steamgaloshi.world", "zoneId": "9b3e…",
    "nameservers": ["ada.ns.cloudflare.com", "kip.ns.cloudflare.com"], "zoneStatus": "active", "nsApplied": true } }
# обычно 2–20 минут после шага 3

6 · SSL и переключатели

# на сервере ещё нет сертификата → flexible; когда появится — переключи на "full"/"strict"
curl -s -X PATCH https://api.void.cd/v1/domains/$ID/cloudflare/settings -H "$H" -H "Content-Type: application/json" \
  -d '{"ssl":"flexible","botFightMode":true,"alwaysOnline":true}'

{ "ok": true, "data": { "ssl": "flexible", "developmentMode": false, "underAttack": false, "alwaysOnline": true, "botFightMode": true, "regionBlock": false, "bypassCache": false } }

7 · Проверяем результат

curl -s https://api.void.cd/v1/domains/$ID/dns -H "$H"

{ "ok": true, "data": [
    { "id": "a1f0…", "type": "A",     "name": "steamgaloshi.world",     "content": "203.0.113.10",       "proxied": true, "priority": null },
    { "id": "b7c2…", "type": "CNAME", "name": "www.steamgaloshi.world", "content": "steamgaloshi.world", "proxied": true, "priority": null } ] }

dig +short steamgaloshi.world        # IP Cloudflare — исходный сервер скрыт

Что может пойти не так: шаг 2 → 409 DOMAIN_UNAVAILABLE (кто-то успел раньше) или 402 INSUFFICIENT_FUNDS; шаг 3 → 503 CLOUDFLARE_NO_ACCOUNTS (повтори через пару минут); шаг 6 до активации зоны → 409 CLOUDFLARE_ZONE_PENDING. Ничего откатывать руками не нужно: неудавшиеся платные вызовы возвращают деньги сами.

Аутентификация

Каждый вызов несёт API-ключ как bearer-токен. Ключ принадлежит аккаунту, а не человеку: кто им владеет, тот может тратить баланс и менять домены. До 5 ключей на аккаунт, каждый отзывается отдельно — по ключу на интеграцию будет правильно.

Authorization: Bearer void_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Жизненный цикл

  • Создать — бот → профиль → API → создать ключ. Открытый текст показывается один раз и у нас не хранится (только хеш).
  • Посмотреть — там же список ключей по префиксу (void_cT4dmZf…) с датой создания и последнего вызова.
  • Отозвать — удалить на том же экране; со следующего запроса ключ отвечает 401.

Ключ не нужен для GET /v1 (индекс ресурсов) и документации. Всё остальное без ключа отвечает 401 UNAUTHORIZED. Только HTTPS — HTTP редиректится до того, как дойдёт до API.

Соглашения

одни правила везде

Конверт

{ "ok": true,  "data": … }
{ "ok": false, "error": { "code": "…", "message": "…", "details": … } }

HTTP-статусы

200  ок
201  создано (POST /domains, /servers, /hosting, cloudflare, dns, redirects, правила файрвола)
202  принято, выполняется в фоне (действия и апгрейд сервера, тест вебхука)
4xx  на твоей стороне — смотри код ошибки
5xx  наша или внешняя — можно повторить позже

Типы

чтоформатпример
деньгидесятичная строка, USD, 2 знака — никогда не float"14.50"
времяISO 8601, UTC"2027-08-28T17:04:38.000Z"
идентификаторыUUID v4 для наших объектов; собственные id Cloudflare для DNS-записей и редиректов"0a797218-8ec5-…"
имена доменовнормализуются к регистрируемой форме, нижний регистр, без www.Example.COM → example.com
спискиoffset / limit (макс. 100; серверы и хостинг отдают не больше 50 на страницу) в query; items + total в ответе?offset=20&limit=20

Вызовы, которые списывают деньги

Заказы (POST /domains, /servers, /hosting), продления (POST …/renew), апгрейд сервера и смена тарифа хостинга списывают баланс сразу. Если реестр или панель хостинга затем откажут, списание отменяется автоматически; сервер, который не удалось создать, переходит в FAILED с возвратом первого срока. Апгрейд и смена тарифа принимают maxCharge — цену из квоты — и скорее откажут, чем спишут больше. Смена срока продления ничего не списывает. Баланс пополняется в боте; в API деньги только на чтение.

Повторы и идемпотентность

Чтение можно повторять свободно. Повторный POST /domains с тем же именем или POST /hosting с тем же доменом после успешного первого вернёт 409 DOMAIN_UNAVAILABLE, так что дважды не купить. POST /servers и продления не идемпотентны: каждый успешный вызов — ещё один сервер или ещё один срок. После таймаута соответствующий список (GET /domains, /servers, /hosting) или сам объект покажет, прошёл ли вызов, прежде чем повторять.

Ошибки

Коды ошибок — стабильные строки, на которые можно завязываться; message для людей и может меняться. details присутствует, когда есть что сказать структурно (ошибки валидации, недостающая сумма, новая квота). Отказы по бизнес-правилам — размер сервера вне пределов, апгрейд с уменьшением, кривое правило файрвола — тоже приходят как 400 VALIDATION_ERROR, без разбора по полям.

HTTPкодзначение
400VALIDATION_ERRORкривой запрос; в details[] — path и message по каждому полю
400DOMAIN_INVALIDэто не доменное имя
400SERVER_NOT_ACTIVE · HOSTING_NOT_ACTIVEуслуга в состоянии, которое не позволяет операцию (ещё не готова, сбой, удалена)
400NO_CAPACITY · NO_IP_AVAILABLE · NO_TEMPLATE · PROXY_NO_CAPACITY · PLAN_INACTIVEсейчас нет места под этот размер, образ, прокси или тариф; ничего не списано
400UPGRADE_PRICE_CHANGEDцена выше твоего maxCharge; ничего не списано, новая цена — в details
401UNAUTHORIZEDключ отсутствует, неизвестен или отозван
402INSUFFICIENT_FUNDSне хватает баланса; details = { required, balance, missing }
403USER_BANNEDаккаунт заблокирован
404DOMAIN_NOT_FOUND · SERVER_NOT_FOUND · HOSTING_NOT_FOUND · PLAN_NOT_FOUND · DNS_NOT_FOUND · NOT_FOUNDнет на этом аккаунте / нет такого эндпоинта
409DOMAIN_UNAVAILABLEзанят, премиум или зона, которую мы не продаём; для хостинга — домен уже на хостинг-аккаунте
409SERVER_BUSYна сервере уже идёт или ждёт другая операция — повтори, когда она закончится
409CLOUDFLARE_ALREADY · CLOUDFLARE_NOT_CONNECTED · CLOUDFLARE_ZONE_PENDINGсостояние зоны не позволяет операцию
409DNS_EXISTS · REDIRECT_EXISTS · REDIRECT_LIMITдубликат или достигнут лимит
429RATE_LIMITEDпревышен лимит на ключ — сбавь темп
502REGISTRAR_ERROR · CLOUDFLARE_ERRORвнешний сервис упал; повтори позже, деньги не списаны
503CLOUDFLARE_UNAVAILABLE · CLOUDFLARE_NO_ACCOUNTSвременно нет мощности
5xxPROVIDER_ERROR · PROVIDER_DISABLEDгипервизор или панель хостинга дали сбой либо продажи хостинга выключены; оплаченный заказ возвращается
{ "ok": false, "error": { "code": "INSUFFICIENT_FUNDS", "message": "account balance is too low — top up in the bot",
    "details": { "required": "16.00", "balance": "5.50", "missing": "10.50" } } }

Лимиты

120 запросов / мин на ключ20 / мин для check · search · заказа домена, сервера или хостингаскользящее окно

Корзина 20 / мин считается отдельно от 120. При превышении — 429 RATE_LIMITED; подожди несколько секунд и повтори. Проверки доступности идут в реестр в реальном времени — кешируй ответы у себя на несколько минут вместо опроса и предпочитай /domains/search (один вызов, все зоны) множеству одиночных проверок.