Для AI-агентов: markdown этой страницы — /docs-content/infra/servers/create.md индекс документации — /llms.txt

Создать сервер

POST /v1/infra/servers

Создаёт приложение у облачного провайдера. Всегда в режиме Black Hole — iptables блокирует все входящие порты, приложение невидимо из интернета.

На портале есть две модели размещения, и от модели зависит, чего ждать после создания:

  • Отдельная виртуальная машина (модель по умолчанию). Ответ возвращается сразу со статусом provisioning, провижининг занимает 1–3 минуты, после чего нужно опрашивать GET /v1/infra/servers/:id до status: "running" и blackholeStatus: "CONNECTED". Ответ единственный раз содержит SSH-данные ssh.password и ssh.privateKey — они будут нужны при переключении в режим OPEN, сохраните их сразу. Эта страница описывает данную модель в полях запроса, полях ответа и таблице ошибок ниже.
  • Galaxy-приложение — контейнер на общем хосте. Если портал размещает приложения в галактиках, тот же POST /v1/infra/servers создаёт galaxy-приложение, а не виртуальную машину. Признак модели в ответе — поле createdVia равно galaxy. Раздел «Galaxy-приложение» ниже описывает два сценария запуска, полная модель — на странице Galaxy-приложение.

Galaxy-приложение

Если портал размещает приложения в галактиках, POST /v1/infra/servers создаёт galaxy-приложение — контейнер на общем хосте, а не отдельную виртуальную машину. Запрос и порядок вызовов те же, отличается жизненный цикл. Признак модели в ответе — поле createdVia равно galaxy. Полная модель, стоимость и отличия деплоя — на странице Galaxy-приложение.

Не ждите CONNECTED перед загрузкой кода. Galaxy-приложение никогда само не доходит до blackholeStatus: "CONNECTED" — его контейнер собирается при загрузке кода. Если просто опрашивать статус, приложение останется в provisioning, а примерно через 20 минут платформа пометит его как error и запишет в поле provisionError, что код так и не был загружен. Поэтому код загружают сразу — одним из двух сценариев ниже.

Сценарий 1 — прозрачный, рекомендуется

Один запрос. Передайте в POST /v1/infra/servers код приложения в поле source вместе с runtime и start (и при необходимости install, env, port). Сборка пойдёт в фоновом режиме. После ответа опрашивайте GET /v1/infra/servers/:id до status: "running". Отдельный вызов загрузки кода не нужен.

Terminal
curl -X POST https://vibecode.bitrix24.tech/v1/infra/servers \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "my-crm-app",
    "displayName": "Мой CRM-бот",
    "source": { "content": "<base64-архив>" },
    "runtime": "node20",
    "start": "node index.js",
    "install": "npm ci",
    "port": 3000
  }'

Поля source, runtime, start, install, env, port, healthPath совпадают по смыслу с телом POST /:id/deploy:

Поле Тип Обяз. Описание
source object да (для этого сценария) Источник кода. source.content — архив приложения в base64, до 500 МБ на тело запроса
runtime string да (если передан source) ID рантайма: node20, python311, php83, static и другие. Список — GET /v1/infra/runtimes
start string да (если передан source) Команда запуска приложения, одной строкой. Перенос строки вернёт 400
install string нет Команда установки зависимостей, одной строкой
env object нет Переменные окружения { "KEY": "value" }. Подставляются в контейнер при запуске, см. ниже
port number нет Порт, на котором приложение слушает
healthPath string нет Путь, по которому платформа проверяет готовность приложения внутри контейнера. До 500 символов, начинается со /. По умолчанию /. На создании отдельной виртуальной машины игнорируется

Без полей runtime и start поле source вернёт 400 — оба поля обязательны. На портале без режима галактик source запрещён и вернётся 400 SOURCE_AT_CREATE_GALAXY_ONLY. Тот же ответ придёт на бесплатном тарифе Битрикс24, пока у аккаунта нет галактики: новая на бесплатном тарифе не создаётся, поэтому разворачивайте в два шага — создание без source, затем POST /v1/infra/servers/:id/deploy.

Сценарий 2 — в два шага

Создайте приложение без source — ответ придёт со статусом provisioning и подсказкой next: "deploy". Сразу вызовите POST /v1/infra/servers/:id/deploy с кодом в source, рантаймом и командой запуска. Загружайте код сразу, не дожидаясь CONNECTED.

Terminal
# Шаг 1 — создать приложение без кода
curl -X POST https://vibecode.bitrix24.tech/v1/infra/servers \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "provider": "bitrix-cloud", "name": "my-crm-app", "plan": "bc-medium", "region": "ru-central1-a" }'

# Ответ содержит data.id, data.next = "deploy" и data.hint.
# Шаг 2 — сразу загрузить код, не дожидаясь CONNECTED
curl -X POST https://vibecode.bitrix24.tech/v1/infra/servers/SERVER_ID/deploy \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "source": { "content": "<base64-архив>" },
    "runtime": "node20",
    "start": "node index.js",
    "port": 3000
  }'

provider и plan обязательны по схеме для создания без source. region необязателен: без него платформа сама подставит регион по умолчанию для выбранного провайдера. Для galaxy-приложения значения информационны — приложение наследует провайдера, план и регион своего хоста.

Если присланное значение расходится с тем, что приложение реально получило, ответ несёт запись в warnings[] рядом с data: она называет разошедшиеся поля, показывает и присланное, и действующее значение и предупреждает, что повторная отправка ничего не изменит. Совпавшие поля не упоминаются, так что корректный вызов остаётся без предупреждения. Нужна машина, у которой провайдера, план и регион выбираете вы, — создавайте её с placement равным dedicated.

Отдельная виртуальная машина вместо galaxy-приложения

Если портал размещает приложения в галактиках, а конкретному приложению нужен свой сервер — передайте поле placement равным dedicated. Пара provider, plan при этом обязательна, region по-прежнему необязателен. Поле source в таком запросе вернёт 400 SOURCE_AT_CREATE_GALAXY_ONLY: отдельная виртуальная машина разворачивается в два шага, код загружают через POST /:id/deploy.

Terminal
curl -X POST https://vibecode.bitrix24.tech/v1/infra/servers \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "provider": "bitrix-cloud",
    "name": "my-crm-app",
    "plan": "bc-small",
    "region": "ru-central1-b",
    "placement": "dedicated"
  }'

В ответе kind равен STANDALONE. Дальше действует порядок для отдельной виртуальной машины: дождаться status: "running" и blackholeStatus: "CONNECTED", затем деплой.

Диагностика и переменные окружения

  • Поле provisionError. Если сборка или запуск упали, GET /v1/infra/servers/:id вернёт в data.provisionError причину сбоя, а при ошибке сборки образа — ещё и хвост лога сборки Docker. Читайте это поле, чтобы понять причину.
  • Переменные env подставляются при запуске. Значения из env передаются в контейнер во время запуска через docker run --env, а не вшиваются в образ. Вместе с ними платформа передаёт PORT (равен полю port, по умолчанию 3000): ключ PORT зарезервирован, присланный свой перекрывается, и ответ несёт строку в warnings[] — см. POST /v1/infra/servers/:id/deploy. Поэтому ключи API и секреты из env не попадают в слои образа.

Поля запроса (body)

Поле Тип Обяз. Описание
provider string да ID провайдера из GET /v1/infra/providers, например bitrix-cloud. 1–50 символов
name string да Системное имя сервера — технический идентификатор. 2–63 символа, только строчные латинские буквы, цифры и -, первым символом — буква. Паттерн: ^[a-z][a-z0-9-]*$. Используется в URL поддомена (app-<hex> генерируется отдельно), логах, audit-журнале. Неизменяемый после создания. Для отображаемого имени используйте displayName
displayName string нет Отображаемое имя на любом языке (русский, китайский, эмодзи — что угодно). 2–100 символов, без управляющих байтов. Отображается в UI, уведомлениях о падении сервера, security-алертах, биллинг-строках, B24-каталоге. Если не передан — подставится name. Само name остаётся техническим идентификатором, неизменяемым, оно используется в URL поддомена и логах
description string нет Описание приложения для карточки каталога Битрикс24, до 500 символов. Переносы строк и табуляции разрешены, прочие управляющие байты запрещены. Значение обрезается по краям. Если не передать — описание останется пустым, задать его позже можно через PATCH /v1/infra/servers/:id
plan string да ID тарифа из GET /v1/infra/providers/:providerId/plans, например bc-small
region string нет ID региона из GET /v1/infra/providers/:providerId/regions, например ru-central1-b. Без него платформа подставит регион по умолчанию для провайдера
image string нет ID образа ОС из GET /v1/infra/providers/:providerId/images. Если не передан — платформа сама подставит актуальный образ провайдера. Если передаёте — берите актуальный из ответа: ID меняется при обновлении сборки
sshPublicKey string нет Ваш SSH-публичный ключ (ssh-rsa …, ssh-ed25519 …, ecdsa-sha2-nistp256/384/521 …, security-keys). До 8192 символов. Если не передан — платформа сгенерирует приватный ключ и вернёт его один раз в ssh.privateKey
placement string нет Модель размещения: auto (по умолчанию) или dedicated. На портале с режимом «Сначала галактика» (galaxies-only) значение dedicated создаёт отдельную виртуальную машину, а не galaxy-приложение — перенос приложения на собственный сервер. Проходит те же проверки, что и обычное создание сервера: политику serverCreation и квоту серверов на пользователя. При auto поведение прежнее (в режиме «Сначала галактика» создаётся galaxy-приложение)
graduateFrom string нет ID вашего galaxy-приложения (kind=GALAXY_APP), которое платформа удалит сразу после того, как создаст для него выделенный сервер — чтобы старое приложение не осталось висеть в галактике. Ограничен владельцем: тот же ключ, тот же портал, kind=GALAXY_APP. Чужой идентификатор или идентификатор не galaxy-приложения вернёт 404, ничего не удаляя. Имеет смысл только вместе с placement: dedicated. Заголовок Idempotency-Key с graduateFrom для выделенного сервера несовместим — см. Идемпотентность

Примечание: для модели отдельной виртуальной машины параметр runtime в POST /v1/infra/servers не принимается — передача вернёт 400 RUNTIME_PARAM_REMOVED. Рантайм устанавливается на этапе деплоя. См. POST /:id/deploy. Исключение — режим галактик: при передаче поля source параметр runtime обязателен (см. раздел «Galaxy-приложение»), и 400 RUNTIME_PARAM_REMOVED в этом случае не возвращается.

Примеры

curl — личный ключ

Terminal
# name — только строчные латинские буквы, цифры и дефис, первый символ буква (^[a-z][a-z0-9-]*$)
curl -X POST https://vibecode.bitrix24.tech/v1/infra/servers \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "provider": "bitrix-cloud",
    "name": "my-crm-app",
    "displayName": "Мой CRM-бот",
    "plan": "bc-small",
    "region": "ru-central1-b",
    "image": "fd83esfomhq25p2ono90"
  }'

curl — OAuth-приложение

Terminal
# name — только строчные латинские буквы, цифры и дефис, первый символ буква (^[a-z][a-z0-9-]*$)
curl -X POST https://vibecode.bitrix24.tech/v1/infra/servers \
  -H "X-Api-Key: YOUR_APP_KEY" \
  -H "Authorization: Bearer USER_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "provider": "bitrix-cloud",
    "name": "my-crm-app",
    "displayName": "Мой CRM-бот",
    "plan": "bc-small",
    "region": "ru-central1-b",
    "image": "fd83esfomhq25p2ono90"
  }'

JavaScript — личный ключ

javascript
// name — только строчные латинские буквы, цифры и дефис, первый символ буква (^[a-z][a-z0-9-]*$)
const res = await fetch('https://vibecode.bitrix24.tech/v1/infra/servers', {
  method: 'POST',
  headers: {
    'X-Api-Key': 'YOUR_API_KEY',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    provider: 'bitrix-cloud',
    name: 'my-crm-app',
    displayName: 'Мой CRM-бот',
    plan: 'bc-small',
    region: 'ru-central1-b',
    image: 'fd83esfomhq25p2ono90',
  }),
})
const { data } = await res.json()
console.log('Server ID:', data.id, 'Subdomain:', data.subdomain)

// Сохраняем SSH-креды на всякий случай — они возвращаются только один раз
if (data.ssh.privateKey) {
  await saveLocally(`${data.id}.key`, data.ssh.privateKey)
}

JavaScript — OAuth-приложение

javascript
// name — только строчные латинские буквы, цифры и дефис, первый символ буква (^[a-z][a-z0-9-]*$)
const res = await fetch('https://vibecode.bitrix24.tech/v1/infra/servers', {
  method: 'POST',
  headers: {
    'X-Api-Key': 'YOUR_APP_KEY',
    'Authorization': 'Bearer USER_SESSION_TOKEN',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    provider: 'bitrix-cloud',
    name: 'my-crm-app',
    displayName: 'Мой CRM-бот',
    plan: 'bc-small',
    region: 'ru-central1-b',
    image: 'fd83esfomhq25p2ono90',
  }),
})

Поля ответа

Поле Тип Описание
success boolean Всегда true при успехе
data.id string (UUID) Уникальный идентификатор сервера
data.status string "provisioning" сразу после создания нового сервера — ждите "running" через 1–3 минуты. В reuse-ответе (reused: true) это статус существующего сервера и он может быть любым, в том числе сразу "running" — тогда ожидание "running" ничего не проверяет
data.provider string Эхо переданного provider. Исключение — galaxy-приложение (createdVia: "galaxy"): возвращается провайдер хоста-носителя, а не запрошенный
data.name string Системное имя сервера. Эхо переданного name при создании нового. В reuse-ответе — имя существующего сервера, а запрошенное вами лежит в data.requestedName
data.kind string Тип сервера, всегда: STANDALONE или GALAXY_APP
data.galaxyId string Только для galaxy-ветки — ID хоста-галактики. В standalone-ответе ключ отсутствует
data.displayName string | null Отображаемое имя. Если при создании не передавали displayName — будет равно name
data.description string | null Эхо переданного description. Ключ есть только в ответе для отдельной виртуальной машины. Для galaxy-приложения, где createdVia равно galaxy, ключа в ответе нет, хотя описание сохраняется — прочитайте его через GET /v1/infra/servers/:id
data.ip string | null Публичный IP. null сразу после создания, заполнится когда виртуальная машина запустится
data.ssh.user string Пользователь для SSH: root для новых серверов
data.ssh.port number Порт SSH: 22
data.ssh.password string | null Одноразово! Пароль root. Сохраняйте сразу — позже не вернётся. Нужен при переключении в OPEN-режим
data.ssh.privateKey string | null Одноразово! Приватный ключ в формате OpenSSH (если sshPublicKey не передавали при создании). Сохраняйте сразу
data.plan string Эхо переданного plan. Исключение — galaxy-приложение (createdVia: "galaxy"): возвращается тариф хоста-носителя, а не запрошенный
data.region string Регион, в который сервер реально попал. Может отличаться от запрошенного при переключении на запасную зону (см. «Известные особенности»). Для galaxy-приложения — регион хоста-носителя
data.image string Эхо переданного image
data.mode string Всегда "BLACKHOLE" сразу после создания
data.createdVia string "api" для вызовов через v1 API, "ui" для вызовов из личного кабинета. В reuse-ответе — значение существующего сервера, поэтому на ответ v1-создания может прийти "ui"
data.subdomain string Субдомен для приложения, например app-92fb1c34. Используется в appUrl
data.blackholeStatus string Состояние туннеля агента. Сразу после создания — "NONE", затем проходит через WAITING и завершается CONNECTED
data.accessPolicy string Политика доступа к приложению. По умолчанию "OWNER_ONLY" — только владелец ключа
data.runtimeId string | null Всегда null при создании. Заполняется при деплое с runtime
data.runtimeStatus string | null Устаревшее поле, оставлено для совместимости. Для серверов, созданных после 2026-04-25, всегда null. Готовность рантайма после деплоя определяет успех шага runtime в ответе POST /:id/deploy, а не это поле
data.appUrl string | null HTTPS-адрес приложения: https://{subdomain}.vibecode.bitrix24.tech
data.createdAt string (ISO 8601) Момент создания

Пример ответа

Отдельная виртуальная машина:

JSON
{
  "success": true,
  "data": {
    "id": "db008c84-91a5-4e15-b9d5-6c6aa2838448",
    "status": "provisioning",
    "provider": "bitrix-cloud",
    "name": "docs-test-temp",
    "kind": "STANDALONE",
    "ip": null,
    "ssh": {
      "user": "root",
      "port": 22,
      "password": "0FPIIR9EfO2OAeSSMK6bKA",
      "privateKey": "-----BEGIN OPENSSH PRIVATE KEY-----\nb3BlbnNzaC1rZXktdjEAAAAABG5vbmU…\n-----END OPENSSH PRIVATE KEY-----\n"
    },
    "plan": "bc-agent",
    "region": "ru-central1-b",
    "image": "fd83esfomhq25p2ono90",
    "mode": "BLACKHOLE",
    "createdVia": "api",
    "subdomain": "app-92fb1c34",
    "blackholeStatus": "NONE",
    "accessPolicy": "OWNER_ONLY",
    "runtimeId": null,
    "runtimeStatus": null,
    "appUrl": "https://app-92fb1c34.vibecode.bitrix24.tech",
    "createdAt": "2026-04-22T10:50:11.477Z"
  }
}

Galaxy-приложение по сценарию 1 — source передан, сборка идёт в фоне. Полей ssh и next нет:

JSON
{
  "success": true,
  "data": {
    "id": "7c2b1f08-3a4d-4e91-9b6c-2f5e8a1d0c33",
    "status": "provisioning",
    "provider": "bitrix-cloud",
    "name": "my-crm-app",
    "kind": "GALAXY_APP",
    "galaxyId": "<galaxy-host-id>",
    "displayName": "Мой CRM-бот",
    "ip": null,
    "ssh": null,
    "plan": "bc-medium",
    "region": "ru-central1-a",
    "image": "ubuntu-2404-lts",
    "mode": "BLACKHOLE",
    "createdVia": "galaxy",
    "subdomain": "app-7c2b1f08",
    "blackholeStatus": "NONE",
    "accessPolicy": "OWNER_ONLY",
    "runtimeId": null,
    "runtimeStatus": null,
    "appUrl": "https://app-7c2b1f08.vibecode.bitrix24.tech",
    "createdAt": "2026-04-22T10:50:11.477Z"
  }
}

Galaxy-приложение по сценарию 2 — source не передан, появляются next и hint:

JSON
{
  "success": true,
  "data": {
    "id": "7c2b1f08-3a4d-4e91-9b6c-2f5e8a1d0c33",
    "status": "provisioning",
    "provider": "bitrix-cloud",
    "name": "my-crm-app",
    "kind": "GALAXY_APP",
    "galaxyId": "<galaxy-host-id>",
    "displayName": "Мой CRM-бот",
    "ip": null,
    "ssh": null,
    "plan": "bc-medium",
    "region": "ru-central1-a",
    "image": "ubuntu-2404-lts",
    "mode": "BLACKHOLE",
    "createdVia": "galaxy",
    "subdomain": "app-7c2b1f08",
    "blackholeStatus": "NONE",
    "accessPolicy": "OWNER_ONLY",
    "runtimeId": null,
    "runtimeStatus": null,
    "appUrl": "https://app-7c2b1f08.vibecode.bitrix24.tech",
    "createdAt": "2026-04-22T10:50:11.477Z",
    "next": "deploy",
    "hint": "This is a galaxy app — POST /v1/infra/servers/:id/deploy with source.content now; it will not reach \"running\" on its own."
  }
}

Если сборка galaxy-приложения упала, GET /v1/infra/servers/:id вернёт причину в data.provisionError:

JSON
{
  "success": true,
  "data": {
    "id": "7c2b1f08-3a4d-4e91-9b6c-2f5e8a1d0c33",
    "status": "error",
    "createdVia": "galaxy",
    "provisionError": "Docker build failed: npm ci exited with code 1"
  }
}

Пример ответа при ошибке

400 — нарушена валидация (имя начинается с заглавной):

JSON
{
  "success": false,
  "error": {
    "code": "INVALID_REQUEST",
    "message": "name: Name must start with a letter and contain only lowercase letters, digits, and hyphens"
  }
}

Ошибки

HTTP Код Описание
400 INVALID_REQUEST Нарушена валидация полей (неверный формат имени, отсутствует обязательное поле, неверный SSH-ключ и т. д.). Поле message содержит конкретную причину. Если на портале с размещением в галактике не передано ни source, ни тройка provider + plan + region — добавляется объект error.hint (см. ниже)
400 SOURCE_AT_CREATE_GALAXY_ONLY Поле source передано на портале без режима галактик либо вместе с placement: "dedicated". Загружайте код через POST /:id/deploy после создания
400 GALAXY_SOURCE_URL_NOT_ALLOWED Ссылка в source.url не прошла проверку до отправки на хост галактики. Три причины: значение не является абсолютным URL, схема не http и не https, адрес ведёт не в публичную сеть — на локальный, внутренний или служебный узел. Текст ответа называет сработавшее правило. Та же проверка стоит на выкладке кода
400 RUNTIME_PARAM_REMOVED Параметр runtime передан без source. Указывайте рантайм в POST /:id/deploy либо передайте runtime, start и source вместе в этом же запросе. На портале с размещением в галактике добавляется объект error.hint (см. ниже)
400 UNKNOWN_PARAM В теле есть неизвестное поле (например deployMode вместо placement). details.unknownFields перечисляет лишние поля, details.suggestions подсказывает правильное имя, details.validParams — полный список допустимых полей
400 INVALID_PLAN Тариф из поля plan отсутствует в каталоге провайдера. Список — GET /v1/infra/providers/:providerId/plans
400 INVALID_REGION Присланный region отсутствует в каталоге провайдера. Список — GET /v1/infra/providers/:providerId/regions. Второй случай: region не передан, а у провайдера нет ни одного региона — тогда message называет провайдера, и подставлять значение по умолчанию платформе не из чего
401 MISSING_API_KEY Не передан заголовок X-Api-Key
401 INVALID_API_KEY Неверный или просроченный API-ключ
402 COMMERCIAL_PLAN_REQUIRED Бесплатный тариф Битрикс24 и пробный период недоступны (пробный уже использован). См. раздел «Тариф и доступ»
402 TRIAL_EXPIRED Trial использован и завершился
402 TRIAL_PORTAL_LIMIT Превышена квота серверов портала на trial
402 TRIAL_USER_LIMIT Превышена квота серверов на пользователя на trial
402 PLAN_NOT_ALLOWED_ON_TRIAL На trial разрешён только тариф bc-micro
402 ACCOUNT_FROZEN Баланс Вайбкод заморожен, нужно пополнить
403 INFRA_NOT_PERMITTED Инфраструктура отключена на платформе или на портале
403 SERVER_CREATION_DISABLED Создание серверов запрещено политикой портала
403 MAX_SERVERS_REACHED Превышен лимит серверов на API-ключ. Удалите ненужные через DELETE или создавайте новый API-ключ
403 WRITE_BLOCKED_READONLY_KEY У ключа режим «только чтение», а создание сервера — операция записи. Переключите ключ на чтение и запись, порядок — Режим доступа
403 INFRA_SCOPE_REQUIRED У ключа нет скоупа vibe:infra — управление инфраструктурой недоступно. Добавьте скоуп или используйте ключ с правами на инфраструктуру
403 INFRA_FORBIDDEN_FOR_COWORK_KEY Вызов сделан ключом Cowork. Такой ключ работает только с данными и не управляет серверами. В error.details.requiredAction приходит порядок получения ключа с правами на деплой
404 NO_CREDENTIALS Провайдер provider не сконфигурирован на платформе
502 PROVIDER_ERROR Облачный провайдер вернул ошибку при создании виртуальной машины. Запись сервера помечается как удалённая, квота не расходуется — можно сразу повторить попытку. Поле message содержит локализованное описание сбоя и код инцидента — сырой ответ провайдера в тело не попадает, он остаётся в логах платформы. Назовите код инцидента поддержке
429 RATE_LIMITED Превышен общий лимит запросов платформы
429 DEPLOY_BACKEND_BUSY Запрос нёс архив в source.content, а на бэкенде уже идёт предельное число таких же тяжёлых запросов (общий счётчик с POST /:id/deploy и POST /:id/upload). В ответе заголовок Retry-After: 30 — повторите через полминуты. Чтобы не зависеть от очереди, создайте сервер без source, а код загрузите отдельным запросом со ссылкой {source: {url: ...}} — у этого пути ограничения на одновременность нет
503 POOL_EXHAUSTED Сервис временно перегружен — исчерпан пул соединений с базой данных. В ответе retryAfter и заголовок Retry-After, повторите через несколько секунд

Полный список общих ошибок API — Ошибки.

Подсказка `error.hint`

На портале с размещением в галактике ошибки INVALID_REQUEST и RUNTIME_PARAM_REMOVED дополняются объектом error.hint. Он называет причину отказа и даёт готовое тело запроса. Условие у двух кодов разное. У INVALID_REQUEST подсказка приходит, когда в теле нет ни source, ни полной тройки provider + plan + region. У RUNTIME_PARAM_REMOVED — когда передан runtime без source, причём тройка при этом может быть заполнена полностью. На портале с отдельными виртуальными машинами подсказки нет ни в одном из случаев.

Не путайте с data.hint из успешного ответа: там строка про следующий шаг после создания приложения по сценарию 2, здесь — объект внутри error.

Поле Тип Описание
error.hint.reason string Почему запрос отклонён
error.hint.recovery string Что изменить в запросе, чтобы он прошёл
error.hint.example object Готовое тело запроса, которое можно взять за основу
JSON
{
  "success": false,
  "error": {
    "code": "INVALID_REQUEST",
    "message": "provider: Required; plan: Required",
    "hint": {
      "reason": "This portal places new apps on shared galaxy hosts, and the request lacked `source` and the full provider/plan/region tuple.",
      "recovery": "RECOMMENDED: create-and-deploy in ONE call — POST /v1/infra/servers { name, source: { content }, runtime, start }; OMIT provider/plan/region. Two-step also works: pass provider/plan/region (informational for a galaxy — values from GET /v1/infra/providers catalogs) to create an empty slot, then POST /v1/infra/servers/:id/deploy with the source. See GET /v1/me -> deployment.galaxyApp.checklist. For a deliberate dedicated standalone VM pass placement: \"dedicated\" together with provider/plan/region.",
      "example": {
        "name": "<slug>",
        "source": { "content": "<base64 gzip-tar of the app>" },
        "runtime": "node20",
        "start": "node server.js",
        "port": 3000
      }
    }
  }
}

Если в теле передан placement: "dedicated", подсказка другая: она предлагает добавить provider, plan и region, сохранив выделенный сервер, а не переводить приложение в контейнер галактики.

Переиспользование сервера приложения

Ключ вызова принадлежит приложению из раздела «Приложения», и приложению можно привязать ровно один сервер. Если сервер уже привязан и работает, POST /v1/infra/servers не создаёт второй, а возвращает 201 с уже существующим сервером. Имя, которое вы передали в name, в этом решении не участвует — повтор платформа определяет по вызывающему ключу, а не по имени.

В reuse-ответе (reused: true) все поля data описывают существующий привязанный сервер, а не ваш запросstatus, provider, plan, region, image, createdVia, createdAt относятся к нему. Статус может быть любым, поэтому «дождаться running» не является проверкой того, что сервер ваш. Проверка — это reused и пара namerequestedName.

Ответ на такой запрос всегда несёт data.reused: true и рядом с data — массив warnings минимум с одной записью. Сравните data.name с data.requestedName: они различаются, если сервер приложения был создан раньше и под другим именем.

Поле Тип Когда
data.reused boolean Всегда на таком ответе. Новый сервер не создан
data.reusedReason string APPLICATION_ALREADY_HAS_SERVER
data.requestedName string Эхо вашего name — всегда, даже если совпадает с именем существующего сервера
data.sourceIgnored boolean Переданный source не развёрнут и отброшен
data.deploying boolean Переданный source собирается на контейнер приложения прямо сейчас
data.metaIgnored boolean Переданные displayName/description не применены
warnings array Рядом с data, не внутри. Минимум одна запись

Что делать перед деплоем. Запросите GET /v1/infra/servers/:id и посмотрите, что на сервере уже развёрнуто. Деплой заменяет работающий код, он не добавляет к нему. Если это не тот сервер, который вы имели в виду, остановитесь и спросите пользователя, с каким приложением должен работать этот ключ.

Про переданный source. Одношаговое создание разворачивает исходники в переиспользуемое galaxy-приложение только тогда, когда контейнера ещё нет (ни разу не деплоилось, либо прошлая сборка упала) — в ответе будет deploying: true, опрашивайте GET /v1/infra/servers/:id до running. Во всех остальных случаях, включая переиспользованную отдельную виртуальную машину, архив отбрасывается и в ответе появляется sourceIgnored: true — разверните его явно через POST /v1/infra/servers/:id/deploy, когда убедитесь, что сервер тот.

Про переданные displayName и description. Переиспользование никогда не переименовывает сервер, который отдаёт: у него остаются собственные имя и описание, а переданные вами значения отбрасываются — в ответе это видно по data.metaIgnored: true и отдельному предупреждению. Переименуйте осознанно через PATCH /v1/infra/servers/:id, убедившись, что сервер тот.

Про data.next. В reuse-ответе поле приходит только тогда, когда на возвращённом сервере нечего перезаписывать — это galaxy-приложение без контейнера, прикреплённое к своему galaxy-хосту. Если контейнера нет, но приложение откреплено от хоста, деплоить некуда — next тоже не придёт. Если сервер может выполнять код, next в ответе отсутствует намеренно — отсутствие рекомендации и есть сигнал: сначала разберитесь, тот ли это сервер. Не трактуйте отсутствие next как ошибку.

Как получить для приложения другой сервер. Отдельного вызова, меняющего привязку, нет. Если нужна отдельная виртуальная машина и привязку трогать не надо — создайте её с placement: "dedicated": переиспользование не сработает, но и к приложению новый сервер привязан не будет. Если нужно именно сменить привязанный сервер — удалите текущий через DELETE /v1/infra/servers/:id, и следующий POST /v1/infra/servers тем же ключом снимет устаревшую привязку и привяжет вновь созданный сервер. Удаление необратимо и уничтожает всё, что развёрнуто на сервере, — сначала убедитесь по GET /v1/infra/servers/:id, что это не чужая работа.

Создание с placement: "dedicated" переиспользование не затрагивает.

Идемпотентность

Передайте необязательный заголовок Idempotency-Key, чтобы безопасно повторять создание отдельного сервера (standalone). Если ответ на первый запрос потерялся (обрыв сети, таймаут балансировщика), повтор с тем же ключом не создаст второй сервер — вернётся тот же самый сервер, что и в первый раз, со статусом 201 и заголовком ответа Idempotent-Replayed: true.

  • Ключ — строка 1–255 символов из набора [A-Za-z0-9_.:-]. Область действия — ваш API-ключ.
  • При повторе одноразовые SSH-данные не выдаются повторно. В теле ответа ssh.privateKey и ssh.password будут null, а поле note пояснит это. Сохраняйте данные из ответа первого создания.
  • Заголовок работает только для standalone-серверов. На порталах с размещением в галактике корректный ключ игнорируется без ошибки, и защита от повторного создания на этот путь не распространяется.
  • Заголовок несовместим с graduateFrom для выделенного сервера — вернётся 400 IDEMPOTENCY_UNSUPPORTED_WITH_GRADUATION.
  • Ключ не защищает от повторной сборки на переиспользованном сервере. Ответ с reused: true ключом не помечается, поэтому повтор запроса с source на переиспользованное galaxy-приложение, у которого ещё нет контейнера, запустит сборку заново. Сначала опросите GET /v1/infra/servers/:id.
Код code Когда
400 INVALID_IDEMPOTENCY_KEY Ключ не проходит валидацию (длина или недопустимые символы)
400 IDEMPOTENCY_UNSUPPORTED_WITH_GRADUATION Ключ вместе с graduateFrom для выделенного сервера
409 IDEMPOTENCY_KEY_ALREADY_USED Ключ уже использован для сервера, который затем был удалён — возьмите новый ключ
409 IDEMPOTENCY_CONCURRENT_RETRY Параллельный запрос с тем же ключом ещё выполняется — повторите чуть позже

Известные особенности

  • Примеры корректных и некорректных имён по паттерну ^[a-z][a-z0-9-]*$:
    • Правильно: my-app, bot-1, crm-dashboard.
    • Неправильно: My-App (заглавная), bot_1 (подчёркивание), my.app (точка), кириллица.
  • sshPublicKey vs автогенерация. Если передаёте свой публичный ключ — платформа не генерирует приватный, и ssh.privateKey в ответе будет null. Пароль в ssh.password всё равно возвращается.
  • Когда region в ответе отличается от запрошенного. В запрошенной зоне могли кончиться IP-адреса (Address space exhausted) — тогда платформа автоматически пробует следующие зоны в порядке из GET /v1/infra/providers/:providerId/regions, и фиксирует это в аудит-логе событием SERVER_ZONE_FALLBACK.
  • Таймаут провижининга — 15 минут. Если status держится в provisioning дольше, платформа переводит сервер в error и заполняет provisionError и provisionErrorCode. Дальше остаётся вызвать DELETE и создать новый.
  • Рантайм устанавливается на этапе деплоя. Параметр runtime был удалён из POST /v1/infra/servers (вернёт 400 RUNTIME_PARAM_REMOVED). Указывайте runtime в теле POST /:id/deploy — он установится между извлечением архива и запуском приложения.
  • Кириллица в displayName и description из Windows PowerShell. Отправленная без явной сериализации в UTF-8, она сохраняется знаками вопроса (?): байты теряются на стороне клиента, до отправки запроса. Готовый вызов с UTF8.GetBytesWindows / PowerShell и UTF-8.

Смотрите также