Стадия жизненного цикла модуля: Общедоступная версия
У модуля есть требования для установки
Для Deckhouse Commander доступно технологическое превью активно развивающегося Terraform-провайдера, который работает через API интеграции, описанный в этом разделе. Провайдер в настоящее время поддерживает создание, удаление, изменение кластеров и шаблонов кластеров в одном рабочем пространстве, поэтому используйте с ним API-ключ сервисного аккаунта рабочего пространства: такому ключу не нужен контекст рабочего пространства.
Обзор
Deckhouse Commander предоставляет внешний API для сценариев автоматизации и интеграции — CI/CD, self-service-порталов, IDP-платформ. Интерактивная документация OpenAPI (Swagger) встроена в веб-интерфейс и всегда соответствует версии запущенного бэкенда; чтобы её открыть, нажмите на иконку в виде шестерёнки в верхней панели:
- на странице вне рабочих пространств, например в разделе «Рабочие пространства», пункт «Документация API» открывает
справочник глобального API по адресу
/documentation; - на странице рабочего пространства пункт «Документация API» открывает справочник API рабочего пространства по адресу
/workspaces/<slug-рабочего-пространства>/auth-tokens/documentation.
Каждый запрос должен содержать API-ключ в HTTP-заголовке X-Auth-Token.
Все пути указываются относительно https://$COMMANDER_HOST;
в примерах используется переменная окружения $COMMANDER_HOST для адреса сервера и $COMMANDER_GLOBAL_TOKEN /
$COMMANDER_TOKEN — для ключей.
GET /api/v1/workspaces HTTP/1.1
Host: $COMMANDER_HOST
X-Auth-Token: $COMMANDER_GLOBAL_TOKEN
Accept: application/jsonAPI-ключ принадлежит сервисному аккаунту — идентичности, от имени которой выполняется запрос. Права даёт не ключ, а роли, назначенные аккаунту: все ключи одного аккаунта действуют с одинаковыми правами, а отзыв ключа права не меняет. Ключи выпускаются на вкладке «Сервисные аккаунты» раздела «Пользователи и права» (полная процедура — в разделе API интеграции и сервисные аккаунты руководства пользователя).
Есть два вида сервисных аккаунтов, и вид аккаунта определяет, какие методы API доступны его ключу:
- Глобальный сервисный аккаунт — управляется в глобальном разделе «Пользователи и права» (пункт верхнего меню на страницах вне рабочих пространств). При наличии глобального назначения вызывает глобальные методы: рабочие пространства, глобальные роли, глобальные назначения ролей, назначения в рабочих пространствах и биллинг. Он также вызывает методы рабочего пространства в каждом РП, где у него есть назначение, — с явным контекстом рабочего пространства, см. Контекст рабочего пространства;
- Сервисный аккаунт рабочего пространства — управляется в разделе «Пользователи и права» своего РП.
Вызывает методы только этого рабочего пространства: кластеры, шаблоны кластеров, каталоги инвентаря, записи каталогов,
хранилища образов контейнеров и проекты. Глобальные методы и любое другое рабочее пространство возвращают
403 Forbidden.
Запросы без ключа, а также с отозванным, просроченным или неизвестным ключом возвращают 401 Unauthorized.
Права на перечисленные ниже ресурсы проверяются так, как описано в разделе
Права доступа. Сервисные аккаунты и их ключи управляются только в веб-интерфейсе и не
входят в API интеграции, поэтому аккаунт не может выпустить ключ сам себе.
За исчерпывающими схемами запросов и ответов, перечислениями и HTTP-кодами для каждого метода всегда обращайтесь к
Swagger UI.
Контекст рабочего пространства
Сервисному аккаунту рабочего пространства контекст не нужен: рабочее пространство определяется самим аккаунтом.
Заголовок X-Commander-Workspace-ID принимается только тогда, когда он повторяет это же рабочее пространство.
Глобальный сервисный аккаунт указывает рабочее пространство в заголовке X-Commander-Workspace-ID,
в котором передаётся UUID рабочего пространства:
GET /api/v1/clusters HTTP/1.1
Host: $COMMANDER_HOST
X-Auth-Token: $COMMANDER_GLOBAL_TOKEN
X-Commander-Workspace-ID: 7f3b9e6a-2c4d-4f8a-9b1e-5d7c6a8f0b2c
Accept: application/jsonПравила одинаковы для всех методов:
| Сервисный аккаунт | Методы | X-Commander-Workspace-ID |
Результат |
|---|---|---|---|
| Глобальный | Глобальные | Отсутствует | Права из глобальных назначений аккаунта |
| Глобальный | Глобальные | Передан | 400 invalid_workspace_context |
| Глобальный | Рабочего пространства | Корректный ID рабочего пространства | Права аккаунта в выбранном рабочем пространстве |
| Глобальный | Рабочего пространства | Отсутствует или некорректен | 400 invalid_workspace_context |
| Рабочего пространства | Своего рабочего пространства | Отсутствует или равен этому РП | Права из назначений этого рабочего пространства |
| Рабочего пространства | Другого РП или глобальные | Любой | 403 Forbidden |
Объект другого рабочего пространства, объект несуществующего рабочего пространства и несуществующий объект возвращают
403 Forbidden: API не раскрывает, существует ли недоступный объект.
Доступные глобальные методы
Доступны глобальному сервисному аккаунту с соответствующими глобальными назначениями. Методы биллинга тоже относятся к глобальным и перечислены в разделе операции биллинга.
- Управление глобальными ролями:
GET /api/v1/global_roles;POST /api/v1/global_roles;GET /api/v1/global_roles/:id;PUT /api/v1/global_roles/:id;DELETE /api/v1/global_roles/:id.
- Управление глобальными назначениями ролей:
GET /api/v1/global_role_bindings;POST /api/v1/global_role_bindings;GET /api/v1/global_role_bindings/:id;PUT /api/v1/global_role_bindings/:id;DELETE /api/v1/global_role_bindings/:id.
- Управление рабочими пространствами:
GET /api/v1/workspaces;POST /api/v1/workspaces;PUT /api/v1/workspaces/:id;DELETE /api/v1/workspaces/:id.
- Управление назначениями в рабочих пространствах:
GET /api/v1/workspace_role_bindings;POST /api/v1/workspace_role_bindings;GET /api/v1/workspace_role_bindings/:id;PUT /api/v1/workspace_role_bindings/:id;DELETE /api/v1/workspace_role_bindings/:id.
Операции биллинга
API интеграции позволяет получать рассчитанную стоимость, управлять лицевыми счетами и тарифами, читать и создавать финансовые операции и классы ресурсов. Готовые запросы собраны в разделе примеры биллинга.
Методы биллинга относятся к глобальному API, и условия доступа к ним отличаются от остальных методов:
- биллинг должен быть включён;
- вызывающий должен быть глобальным сервисным аккаунтом, а вызов выполняется в глобальном контексте, а не в контексте рабочего пространства;
- роль
billing-adminпокрывает все методы биллинга, а ограниченный доступ задаётся глобальной ролью с нужными ресурсами и действиями — подробности в разделе права доступа к биллингу.
Полные схемы запросов и ответов, фильтры, заголовки CSV и ошибки доступны в глобальном Swagger UI
по адресу /documentation. Глобальный API предоставляет следующие операции биллинга:
| Операция | Метод и путь |
|---|---|
| Детализация рассчитанной стоимости | GET /api/v1/billing_costs |
| Список или создание счетов | GET /api/v1/billing_accounts, POST /api/v1/billing_accounts |
| Чтение, изменение или архивация счёта | GET /api/v1/billing_accounts/:id, PUT /api/v1/billing_accounts/:id, DELETE /api/v1/billing_accounts/:id |
| Список транзакций или финансовая операция | GET /api/v1/billing_accounts/:id/transactions, POST /api/v1/billing_accounts/:id/transactions |
| Чтение одной транзакции | GET /api/v1/billing_accounts/:id/transactions/:transaction_id |
| Список или создание тарифов | GET /api/v1/billing_tariffs, POST /api/v1/billing_tariffs |
| Чтение, изменение или удаление тарифа | GET /api/v1/billing_tariffs/:id, PUT /api/v1/billing_tariffs/:id, DELETE /api/v1/billing_tariffs/:id |
| Импорт новых тарифов | POST /api/v1/billing_tariffs/import |
| Список или создание вычислительных классов | GET /api/v1/billing_resources/compute_classes, POST /api/v1/billing_resources/compute_classes |
| Список или создание классов хранилища | GET /api/v1/billing_resources/storage_classes, POST /api/v1/billing_resources/storage_classes |
| Список сформированных отчётов | GET /api/v1/billing_reports |
Стоимость и экспорт данных
Стоимость рассчитывается синхронно, по тем же правилам, что и на дашборде; доступные группировки и фильтры
перечислены в Swagger. Параметры from и to передаются как время в UTC, соответствующее границам календарных
суток в часовом поясе Europe/Moscow, причём граница to в период не входит.
По умолчанию API возвращает JSON. Выгрузка в CSV и разбиение на страницы устроены по-разному у детализации и у списков:
| Данные | CSV | Пагинация |
|---|---|---|
| Детализация стоимости | response_format=csv |
Нет, результат возвращается целиком |
| Списки счетов, транзакций и тарифов | response_format=csv |
Курсорная, per_page от 1 до 9999 (по умолчанию 20) |
Следующая страница запрашивается возвращённым курсором: в JSON он приходит в поле cursor, в CSV — в заголовке
X-Next-Cursor. Последнюю страницу обозначает null в JSON или пустой заголовок X-Next-Cursor в CSV.
Набор расчётных величин по счёту зависит от того, каким методом его читают:
- список счетов содержит проведённый баланс и
paid_until, но текущий расход и прогноз не рассчитывает; - чтение одного счёта их добавляет;
- поля
forecast_daysиforecast_untilостаются пустыми, пока доступный остаток не положителен или потребления нет.
Успешный ответ не подтверждает, что метрики покрывают весь запрошенный период. При отсутствии метрик или наличии
пропусков API возвращает частичный либо пустой результат с кодом 200, и признака покрытия метриками в ответе нет.
Поэтому отсутствие строк или нулевой итог не доказывает отсутствие потребления.
Классы с положительным потреблением и нулевой рассчитанной стоимостью учитываются отдельно, если для периода действует тариф:
partial_tariff_unpriced_rows— общее количество, аunpriced_compute_classesиunpriced_storage_classes— разбивка по видам классов;- в CSV те же величины приходят в заголовках
X-Unpriced-Classes,X-Unpriced-Compute-ClassesиX-Unpriced-Storage-Classes.
Ненулевое значение может означать отсутствие цены класса или явно заданную нулевую цену. Периоды без действующего тарифа не формируют строки и не учитываются в этих счётчиках.
Таймаут каждого запроса к источнику метрик — 120 секунд, при этом один запрос API может выполнить несколько таких
запросов, поэтому учитывайте это при настройке таймаутов клиента и прокси. Если источник метрик недоступен, API
возвращает ошибку 503 в JSON с кодом error_code: prometheus_unavailable и без частичных данных. Повторите запрос
позже, а при таймауте уменьшите период или сузьте выборку фильтрами.
Счета и финансовые операции
Лицевой счёт — то, на что относится потребление: он хранит баланс, привязанные рабочие пространства, держателей и историю пополнений и списаний. Удаление счёта архивирует его — привязки к рабочим пространствам и держателям снимаются, история транзакций сохраняется, а изменять архивный счёт и проводить по нему новые финансовые операции нельзя.
Пополнение и списание требуют заголовка Idempotency-Key. Ключ связан с неизменяемым ID сервисного аккаунта,
поэтому ротация API-ключа не меняет область идемпотентности. Результат повтора зависит от тела запроса:
Запрос с заголовком Idempotency-Key |
Результат |
|---|---|
| Первый с этим ключом | 201 и созданная транзакция |
| Повтор с тем же телом | 200 и та же транзакция, второй раз ничего не проводится |
| Повтор с изменённым телом | 409 idempotency_conflict |
Два свойства методов счетов стоит учесть заранее:
- ответ с транзакцией не содержит баланс счёта — при необходимости запросите счёт отдельно;
- чтение одного счёта и первое списание по счёту обращаются к источнику метрик, поэтому при его недоступности оба
возвращают
503.
Изменение и импорт тарифов
Тариф задаёт цену класса ресурсов начиная с указанной даты, а стоимость периода всегда считается по тарифам,
действовавшим в этом периоде. Неизменяемых версий тарифа API не хранит, поэтому изменение или удаление тарифа меняет
и результат повторного расчёта за период его действия. Чтобы изменить цены для будущего периода, создайте новый тариф
с более поздним значением start_date, а не правьте действующий или исторический.
Импорт принимает JSON или CSV в UTF-8 и только создаёт тарифы — существующие он не изменяет. Точные поля и обязательные столбцы CSV перечислены в Swagger. Права для импорта нужны такие:
billingtariffs:createиbillingresources:get— всегда;billingresources:create— дополнительно, если указанного класса ещё нет.
Импорт идёт в два шага, и от того, на каком из них случилась ошибка, зависит состояние после неё:
| Шаг | Что делает | Ошибка на этом шаге |
|---|---|---|
| Подготовка классов | Создаёт недостающие вычислительные классы и классы хранилища | Останавливает импорт до создания тарифов; уже созданные классы сохраняются |
| Создание тарифов | Обрабатывает каждый тариф независимо | Остальные тарифы обрабатываются; у каждого в ответе стоит created или error |
Доступные методы рабочего пространства
Все методы работают с одним рабочим пространством: для сервисного аккаунта рабочего пространства — с его РП,
для глобального — с РП из заголовка X-Commander-Workspace-ID.
Методы, помеченные (архивируемые), принимают query-параметр archived=true, чтобы включить архивные сущности в ответ;
по умолчанию они из ответа исключаются.
- Управление шаблонами кластеров:
GET /api/v1/cluster_templates(архивируемые);POST /api/v1/cluster_templates;GET /api/v1/cluster_templates/:id;PUT /api/v1/cluster_templates/:id;DELETE /api/v1/cluster_templates/:id;DELETE /api/v1/cluster_templates/:id/permanent.
- Управление каталогами инвентаря:
GET /api/v1/catalogs(архивируемые);POST /api/v1/catalogs;GET /api/v1/catalogs/:id;PUT /api/v1/catalogs/:id;DELETE /api/v1/catalogs/:id.
- Управление записями каталогов:
POST /api/v1/records;GET /api/v1/records(архивируемые);GET /api/v1/records/:id;PUT /api/v1/records/:id;DELETE /api/v1/records/:id.
- Управление кластерами:
POST /api/v1/clusters;GET /api/v1/clusters(архивируемые);GET /api/v1/clusters/:id;GET /api/v1/clusters/:id/status;PUT /api/v1/clusters/:id;DELETE /api/v1/clusters/:id;DELETE /api/v1/clusters/:id/force;DELETE /api/v1/clusters/:id/permanent.
- Управление хранилищами образов контейнеров:
POST /api/v1/registries;GET /api/v1/registries;GET /api/v1/registries/:id;DELETE /api/v1/registries/:id.
- Управление проектами:
GET /api/v1/projects;POST /api/v1/projects;GET /api/v1/projects/:id;PUT /api/v1/projects/:id;DELETE /api/v1/projects/:id.
- Одобрение запросов на изменение кластера:
GET /api/v1/cluster_change_requests;GET /api/v1/cluster_change_requests/:id;POST /api/v1/cluster_change_requests/:id/approve.
- Чтение задач кластера и их логов:
GET /api/v1/cluster_tasks(архивируемые);GET /api/v1/cluster_task_logs(архивируемые).
Исключение — запросы, созданные механизмом
согласования создания кластеров: решения по ним принимаются только в
веб-интерфейсе. Такой запрос помечен в ответе полем approval_flow: true, и
POST /api/v1/cluster_change_requests/:id/approve возвращает для него 409 approval_flow_not_supported. Для
подтверждения деструктивных изменений инфраструктуры эндпоинт /approve продолжает работать.
Ошибки
Ошибки приложения возвращаются в едином JSON-конверте:
{
"error": "Человекочитаемое сообщение",
"error_code": "invalid",
"errors": { "name": ["can't be blank"] }
}Опирайтесь в автоматизации на error_code для ошибок приложения — это стабильный контракт;
сообщение error локализовано и может меняться. Ответы о превышении лимита могут формироваться ingress-контроллером до
передачи запроса в приложение, поэтому для 429 Too Many Requests используйте HTTP-код и заголовок Retry-After,
а не тело ответа. Используются стандартные HTTP-коды; полный список для каждого метода — в Swagger UI.
Наиболее частые случаи:
400 invalid_workspace_context— заголовокX-Commander-Workspace-IDотсутствует или некорректен там, где он нужен запросу, либо передан там, где он не допускается. См. Контекст рабочего пространства;401 no_token/invalid_token— заголовокX-Auth-Tokenотсутствует, либо его ключ отозван, просрочен или не распознан. Ключ также отклоняется после удаления его сервисного аккаунта;403 forbidden— у сервисного аккаунта нет прав на операцию либо запрос обращается к ресурсу вне доступного ему рабочего пространства;409 stale_objectприPUT— ресурс изменён параллельным запросом; перечитайте его, слейте изменения и повторите запрос с новымcurrent_revision;409 conflictна/cluster_change_requests/:id/approve(already_approved,already_completed,cluster_cannot_be_convergedи аналогичные) — не повторяйте одобрение: перечитайте запрос на изменение и выберите дальнейшее действие;409 approval_flow_not_supportedна/cluster_change_requests/:id/approve— запрос создан механизмом согласования создания кластеров (approval_flow: true), решение по нему принимается только в веб-интерфейсе; повторные попытки не помогут;422 invalid— ошибка валидации; детали по полям — в полеdata;422при создании кластера сregistry_modeили изменении режима (direct_mode_unsupported,registry_mode_direct_prerequisites,registry_mode_switch_requires_direct,init_configuration_after_install,registry_mode_change_blocked,registry_mode_attached_without_template) — режим хранилища образов или переход недоступны в текущем состоянии кластера; сообщениеerrorобъясняет условие: «{Direct|Unmanaged} недоступен: разрешённая версия … (нужна 1.75+)», «Переход в режим Direct или Unmanaged (через ModuleConfig) доступен для кластеров на DKP 1.72+ с containerd v2», «При смене хранилища кластер переводится в режим Direct», «Режим «InitConfiguration (deprecated)» доступен только при создании кластера, до начала установки», «Режим хранилища образов можно изменить после подключения шаблона». Правила переходов — в разделе «Режим хранилища образов кластера» руководства пользователя;429 Too Many Requests— превышен лимит частоты запросов; перед повторной попыткой подождите количество секунд из заголовка ответаRetry-After. Тело ответа не является стабильным контрактом;500 internal_server_error— неожиданная ошибка бэкенда; безопасно повторять идемпотентные запросы с экспоненциальным backoff.
При опросе кластера учитывайте одновременно status и render_errors:
ошибка рендеринга шаблона оставляет кластер в нетерминальном статусе, но заполняет render_errors.
Примеры
Во всех примерах предполагается, что заданы переменные окружения:
export COMMANDER_HOST="commander.example.com"
export COMMANDER_GLOBAL_TOKEN="..." # API-ключ глобального сервисного аккаунта.
export COMMANDER_TOKEN="..." # API-ключ сервисного аккаунта рабочего пространства.Создание рабочего пространства
curl -X "POST" \
"https://$COMMANDER_HOST/api/v1/workspaces" \
-H "accept: application/json" \
-H "X-Auth-Token: $COMMANDER_GLOBAL_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"Рабочее пространство из API"}'В ответ приходят данные рабочего пространства; id понадобится для последующих запросов.
{
"id": "088b8e03-e7c0-4655-aae7-d11b7609c579",
"slug": "sftpp",
"name": "Рабочее пространство из API",
"comment": null,
"main": false,
"current_revision": 0,
"created_at": "2026-01-15T12:19:57.002+03:00",
"updated_at": "2026-01-15T12:19:57.002+03:00"
}Получение глобальной роли
Чтобы назначить глобальную роль пользователю или группе, получите id роли:
curl -s -X "GET" \
"https://$COMMANDER_HOST/api/v1/global_roles" \
-H "accept: application/json" \
-H "X-Auth-Token: $COMMANDER_GLOBAL_TOKEN"В ответе будут объекты ролей; нужное поле — id:
[
{
"id": "818c3814-4973-452e-9985-d75fbe35eb4c",
"name": "commander-admin",
"comment": null,
"labels": {
"commander.deckhouse.io/owned-by": "commander"
},
"current_revision": 0,
"commander_rules": [
{
"id": "455ccf46-f34c-4c75-970d-4dbc361adc68",
"verbs": ["*"],
"resources": ["*"],
"resource_names": null,
"created_at": "2025-04-30T23:46:18.616+03:00",
"updated_at": "2025-04-30T23:46:18.616+03:00"
}
],
"kubernetes_rules": [
{
"id": "9713527d-69df-4ecb-9db2-35df194e18b5",
"verbs": ["*"],
"resources": ["*"],
"resource_names": null,
"api_groups": ["*"],
"created_at": "2025-06-06T16:56:32.208+03:00",
"updated_at": "2025-06-06T16:56:32.208+03:00"
}
],
"created_at": "2025-04-30T23:46:18.567+03:00",
"updated_at": "2025-04-30T23:46:18.567+03:00"
}
]Создание назначения
PAYLOAD="$(jq -nc '{
"workspace_id": "088b8e03-e7c0-4655-aae7-d11b7609c579",
"name": "workspace-admins",
"comment": "Назначение из API",
"role": {
"type": "GlobalRole",
"id": "818c3814-4973-452e-9985-d75fbe35eb4c"
},
"subjects": [
{
"kind": "User",
"name": "commander-admin@deckhouse.io"
}
]
}')"
curl -X "POST" \
"https://$COMMANDER_HOST/api/v1/workspace_role_bindings" \
-H "accept: application/json" \
-H "X-Auth-Token: $COMMANDER_GLOBAL_TOKEN" \
-H "Content-Type: application/json" \
-d "$PAYLOAD"Успешный ответ:
{
"id": "05b3abdf-20a5-447b-823b-8e1a57b46158",
"name": "workspace-admins",
"comment": "Назначение из API",
"current_revision": 0,
"role_type": "GlobalRole",
"role": {
"id": "818c3814-4973-452e-9985-d75fbe35eb4c",
"name": "commander-admin",
"comment": null,
"labels": {
"commander.deckhouse.io/owned-by": "commander"
},
"current_revision": 0,
"created_at": "2025-04-30T23:46:18.567+03:00",
"updated_at": "2025-04-30T23:46:18.567+03:00"
},
"subjects": [
{
"kind": "User",
"name": "commander-admin@deckhouse.io",
"created_at": "2026-01-15T14:01:01.858+03:00",
"updated_at": "2026-01-15T14:01:01.858+03:00"
}
],
"created_at": "2026-01-15T14:01:01.821+03:00",
"updated_at": "2026-01-15T14:01:01.821+03:00"
}Добавление группы пользователей в назначение
PUT /api/v1/workspace_role_bindings/:id целиком заменяет список subjects и требует current_revision для
оптимистичной блокировки.
PAYLOAD="$(jq -nc '{
"current_revision": 0,
"subjects": [
{
"kind": "User",
"name": "commander-admin@deckhouse.io"
},
{
"kind": "Group",
"name": "Administrators"
}
]
}')"
curl -X "PUT" \
"https://$COMMANDER_HOST/api/v1/workspace_role_bindings/05b3abdf-20a5-447b-823b-8e1a57b46158" \
-H "accept: application/json" \
-H "X-Auth-Token: $COMMANDER_GLOBAL_TOKEN" \
-H "Content-Type: application/json" \
-d "$PAYLOAD"Получение актуальной версии шаблона
TEMPLATE_ID="fb999a72-efe7-4db7-af53-11b17bc0a687"
curl -s -X "GET" \
"https://$COMMANDER_HOST/api/v1/cluster_templates/$TEMPLATE_ID?without_archived=true" \
-H "accept: application/json" \
-H "X-Auth-Token: $COMMANDER_TOKEN" |
jq "del(.cluster_template_versions)"Нужное поле — current_cluster_template_version_id:
{
"id": "fb999a72-efe7-4db7-af53-11b17bc0a687",
"name": "YC Dev",
"current_cluster_template_version_id": "8e75210a-f05c-421d-84b3-fc0697814d6d",
"comment": "Канал обновлений и версия K8s задаются параметрами",
"current_revision": 12,
"immutable": false,
"created_at": "2024-02-05T17:35:44.318+03:00",
"updated_at": "2024-04-10T18:00:57.835+03:00",
"archived_at": null,
"archive_number": null
}Сохраним его для последующих шагов:
TEMPLATE_VERSION_ID="$(curl -s -X "GET" \
"https://$COMMANDER_HOST/api/v1/cluster_templates/$TEMPLATE_ID?without_archived=true" \
-H "accept: application/json" \
-H "X-Auth-Token: $COMMANDER_TOKEN" |
jq -r ".current_cluster_template_version_id")"Поиск неиспользованной записи в каталоге
Посмотрим схему входных параметров выбранной версии шаблона:
curl -s -X "GET" \
"https://$COMMANDER_HOST/api/v1/cluster_templates/$TEMPLATE_ID?without_archived=true" \
-H "accept: application/json" \
-H "X-Auth-Token: $COMMANDER_TOKEN" |
jq -r --arg ver "$TEMPLATE_VERSION_ID" '
.cluster_template_versions[]
| select(.id == $ver)
| .params'Схема предусматривает три обязательных параметра; один из них — запись из каталога yandex-cloud-slot (поле catalog):
[
{ "header": "Параметры кластера" },
{
"key": "slot",
"span": 4,
"title": "Слот для кластера в Yandex Cloud",
"catalog": "yandex-cloud-slot",
"immutable": true
},
{
"key": "releaseChannel",
"enum": ["Alpha", "Beta", "EarlyAccess", "Stable", "RockSolid"],
"span": 1,
"title": "Канал обновлений",
"default": "EarlyAccess"
},
{
"key": "kubeVersion",
"enum": ["Automatic", "1.25", "1.26", "1.27", "1.28", "1.29"],
"span": 1,
"title": "Версия Kubernetes",
"default": "Automatic"
}
]Мультиселект с возможностью ввода произвольных значений требует type: "array" и
items.suggestions в виде массива (может быть пустым). Значение поля — массив строк; перечисленные
suggestions — опции в UI, допустимы и произвольные строки:
{
"key": "kubeVersions",
"type": "array",
"title": "Версии Kubernetes",
"items": {
"type": "string",
"suggestions": ["1.32", "1.33", "1.34"]
}
}{
"key": "tags",
"type": "array",
"title": "Теги",
"items": {
"type": "string",
"suggestions": []
}
}Строгий мультиселект задаётся через type: "array" и items.enum. Допустимы только
перечисленные строки (скалярная строка отклоняется):
{
"key": "harddrive",
"type": "array",
"title": "Выбор диска",
"items": {
"enum": ["sdk1", "sdk2"]
}
}Найдём каталог по его slug:
CATALOG_ID="$(curl -s -X "GET" \
"https://$COMMANDER_HOST/api/v1/catalogs?without_archived=true" \
-H "accept: application/json" \
-H "X-Auth-Token: $COMMANDER_TOKEN" |
jq -r '.[] | select(.slug == "yandex-cloud-slot") | .id')"Возьмём первую запись из этого каталога, не занятую другим кластером (cluster_id == null),
и подготовим её к использованию: в значения записи добавим специальное поле x-commander-record-id со ссылкой на ID
записи:
SLOT_VALUES="$(curl -s -X "GET" \
"https://$COMMANDER_HOST/api/v1/records?without_archived=true" \
-H "accept: application/json" \
-H "X-Auth-Token: $COMMANDER_TOKEN" |
jq -c --arg cid "$CATALOG_ID" '
[ .[]
| select(.catalog_id == $cid and .cluster_id == null) ][0]
| .values + { "x-commander-record-id": .id }')"
echo "$SLOT_VALUES"Результат:
{
"ip": "158.166.177.188",
"name": "x",
"x-commander-record-id": "5f6727e7-630c-4b18-bcf0-868ea96a27ee"
}Имя ключа x-commander-record-id выбрано так, чтобы не конфликтовать с пользовательскими полями id,
которые могут присутствовать в самих записях.
Создание кластера
PAYLOAD="$(jq -nc --argjson slot "$SLOT_VALUES" --arg ver "$TEMPLATE_VERSION_ID" '{
"name": "Кластер из API",
"cluster_template_version_id": $ver,
"values": {
"kubeVersion": "1.29",
"releaseChannel": "EarlyAccess",
"slot": $slot
}
}')"
curl -X "POST" \
"https://$COMMANDER_HOST/api/v1/clusters" \
-H "accept: application/json" \
-H "X-Auth-Token: $COMMANDER_TOKEN" \
-H "Content-Type: application/json" \
-d "$PAYLOAD"В ответ придут данные кластера. Часть полей (отрендеренная конфигурация, timestamps) в примере ниже опущена для краткости:
{
"id": "5436e6ef-d811-472f-9c9c-46cb9c6321d9",
"name": "Кластер из API",
"values": {
"slot": {
"ip": "158.166.177.188",
"name": "x",
"x-commander-record-id": "5f6727e7-630c-4b18-bcf0-868ea96a27ee"
},
"kubeVersion": "1.29",
"releaseChannel": "EarlyAccess"
},
"cluster_template_version_id": "8e75210a-f05c-421d-84b3-fc0697814d6d",
"was_created": false,
"status": "new"
}Изменение кластера
PUT /api/v1/clusters/:id заменяет параметры кластера целиком: поля, которых нет в запросе,
сбрасываются. Поэтому соберите тело запроса из текущего состояния кластера и измените в нём нужное
значение:
CLUSTER_ID="5436e6ef-d811-472f-9c9c-46cb9c6321d9"
PAYLOAD="$(curl -sS -X "GET" \
"https://$COMMANDER_HOST/api/v1/clusters/$CLUSTER_ID" \
-H "accept: application/json" \
-H "X-Auth-Token: $COMMANDER_TOKEN" |
jq -c '{name, current_revision, cluster_template_version_id, registry_id, registry_mode, values}
| .values.releaseChannel = "Stable"')"
curl -X "PUT" \
"https://$COMMANDER_HOST/api/v1/clusters/$CLUSTER_ID" \
-H "accept: application/json" \
-H "X-Auth-Token: $COMMANDER_TOKEN" \
-H "Content-Type: application/json" \
-d "$PAYLOAD"Если между чтением и записью кластер изменился, PUT вернёт 409 stale_object: перечитайте кластер
и повторите запрос с новым current_revision.
Создание managed-кластера
Kubeconfig целевого кластера передаётся в values как параметр шаблона. В предустановленном
шаблоне «Managed Kubernetes» это kubeconfig. Здесь TEMPLATE_VERSION_ID — текущая версия этого
шаблона (как её получить, описано в разделе
«Получение актуальной версии шаблона»), а
REGISTRY_ID — идентификатор хранилища образов из GET /api/v1/registries:
PAYLOAD="$(jq -nc --arg ver "$TEMPLATE_VERSION_ID" --arg registry "$REGISTRY_ID" \
--arg kubeconfig "$(cat kubeconfig.yaml)" '{
"name": "Managed-кластер из API",
"cluster_template_version_id": $ver,
"registry_id": $registry,
"values": { "kubeconfig": $kubeconfig }
}')"
curl -X "POST" \
"https://$COMMANDER_HOST/api/v1/clusters" \
-H "accept: application/json" \
-H "X-Auth-Token: $COMMANDER_TOKEN" \
-H "Content-Type: application/json" \
-d "$PAYLOAD"Чтобы заменить kubeconfig, измените values.kubeconfig, как описано в разделе
Изменение кластера.
Создание кластера с режимом хранилища образов
Параметр registry_mode задаёт режим хранилища образов кластера. Допустимые значения:
| Значение | Название в интерфейсе | Конфигурация хранилища образов |
|---|---|---|
Direct |
Direct | ModuleConfig deckhouse, модуль registry в режиме Direct |
ModuleUnmanaged |
Unmanaged | ModuleConfig deckhouse, модуль registry в режиме Unmanaged |
Manual |
Вручную (YAML) | секцию хранилища образов описывает шаблон |
Unmanaged |
InitConfiguration (deprecated) | InitConfiguration; доступен только при создании кластера, до начала установки |
Параметр нужен, когда автоматика не может определить версию DKP — например, для хранилища образов с dev-веткой, собранной на базе DKP ниже 1.75:
curl -X "POST" \
"https://$COMMANDER_HOST/api/v1/clusters" \
-H "accept: application/json" \
-H "X-Auth-Token: $COMMANDER_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"name\": \"legacy\", \"cluster_template_version_id\": \"$TEMPLATE_VERSION_ID\",
\"registry_id\": \"$REGISTRY_ID\", \"registry_mode\": \"Unmanaged\"}"В ответе поле registry_mode содержит выбранный режим (Direct, ModuleUnmanaged, Manual или Unmanaged), а поле
registry_module_mode — фактический режим модуля registry в кластере по данным последней сверки (null, пока
модуль его не сообщил, и у кластера без модуля — DKP до 1.72). Если параметр не передан, режим выбирается автоматически по тем же правилам, что и в
веб-интерфейсе: если из хранилища образов разрешена версия 1.75 или выше — Direct, ниже 1.75 — Unmanaged
(InitConfiguration); автоматика никогда не выбирает ModuleUnmanaged и Manual. Режимы Direct и ModuleUnmanaged
отклоняются с кодом 422 (direct_mode_unsupported), если разрешённая версия ниже 1.75.
Изменение режима через PUT /api/v1/clusters/:id проверяется по тем же правилам, что и в веб-интерфейсе;
недопустимый переход отклоняется с кодом 422. При смене хранилища образов у установленного кластера кластер в
Unmanaged (InitConfiguration) переводится в Direct, а кластер в Direct, ModuleUnmanaged или Manual
остаётся в своём режиме. Переход установленного кластера в Direct или ModuleUnmanaged, в том числе при смене
хранилища, требует DKP 1.72+ с containerd v2; иначе запрос отклоняется с кодом 422
(registry_mode_direct_prerequisites), а хранилище у такого кластера меняется только в режиме Manual. Переход в
Unmanaged (InitConfiguration) после запуска установки невозможен. Режим присоединённого
кластера без шаблона не меняется до подключения шаблона (registry_mode_attached_without_template).
Опрос статуса готовности кластера
После создания или обновления кластера нужно опрашивать GET /api/v1/clusters/:id до достижения статуса
status: "in_sync". Учитывайте поле render_errors: непустой список означает,
что шаблон не отрендерился и операция не продвинется, пока входные значения не будут исправлены через
PUT /api/v1/clusters/:id.
CLUSTER_ID="5436e6ef-d811-472f-9c9c-46cb9c6321d9"
DEADLINE=$(( $(date +%s) + 60 * 60 )) # таймаут 1 час
while : ; do
RESP="$(curl -sS -X "GET" \
"https://$COMMANDER_HOST/api/v1/clusters/$CLUSTER_ID" \
-H "accept: application/json" \
-H "X-Auth-Token: $COMMANDER_TOKEN")"
STATUS="$(jq -r ".status" <<<"$RESP")"
RENDER_ERRORS="$(jq -r ".render_errors // [] | length" <<<"$RESP")"
echo "status=$STATUS render_errors=$RENDER_ERRORS"
if [ "$STATUS" = "in_sync" ]; then
echo "кластер готов"
break
fi
if [ "$RENDER_ERRORS" != "0" ]; then
echo "ошибка рендеринга шаблона, см. render_errors:"
jq ".render_errors" <<<"$RESP"
exit 1
fi
if [ "$(date +%s)" -gt "$DEADLINE" ]; then
echo "таймаут ожидания in_sync"
exit 1
fi
sleep 10
doneТипичная последовательность значений status: new → creating → in_sync.
Застревание в creating вместе с непустым render_errors указывает на проблему с шаблоном или входными значениями;
после обновления кластера через PUT опрос возобновится.
Пауза и возобновление кластера
Пауза останавливает сверку и изменения кластера со стороны Deckhouse Commander (см. раздел «Пауза обработки кластера» руководства пользователя):
curl -X "POST" "https://$COMMANDER_HOST/api/v1/clusters/$CLUSTER_ID/pause" \
-H "accept: application/json" \
-H "X-Auth-Token: $COMMANDER_TOKEN"В ответе — данные кластера с признаком паузы:
{
"id": "5436e6ef-d811-472f-9c9c-46cb9c6321d9",
"paused": true,
"paused_at": "2026-09-26T10:00:00.000+03:00",
"paused_by": "terraform"
}Возобновление снимает паузу и сразу запускает сверку:
curl -X "POST" "https://$COMMANDER_HOST/api/v1/clusters/$CLUSTER_ID/resume" \
-H "accept: application/json" \
-H "X-Auth-Token: $COMMANDER_TOKEN"Обе операции идемпотентны: пауза приостановленного кластера и возобновление активного возвращают 200 и ничего не
меняют. Пока кластер на паузе, PUT /api/v1/clusters/:id сохраняет конфигурацию без создания задач. Признак
паузы (paused, paused_at, paused_by) есть и в ответе GET /api/v1/clusters/:id, и в облегчённом статусе.
Выполняемая сверка или изменение прерывается паузой так же, как по «Остановить изменение». Ошибка
422 not_available — пауза недоступна (кластер не установлен, в архиве, удаляется или отсоединяется).
Получение облегчённого статуса кластера
Для частого опроса и дашбордов используйте GET /api/v1/clusters/:id/status.
Эндпоинт возвращает компактный snapshot — текущий статус, активные алерты,
сводку Upmeter и данные для подключения к master-узлу — и не содержит чувствительных полей,
таких как отрендеренная конфигурация и API-ключ агента.
curl -s -X "GET" \
"https://$COMMANDER_HOST/api/v1/clusters/$CLUSTER_ID/status" \
-H "accept: application/json" \
-H "X-Auth-Token: $COMMANDER_TOKEN" | jq .{
"id": "5436e6ef-d811-472f-9c9c-46cb9c6321d9",
"status": "in_sync",
"active_alerts": [
{ "name": "KubeletDown", "severity_level": 4 }
],
"upmeter": {
"status": "up",
"components": [
{ "name": "control-plane", "status": "up" },
{ "name": "synthetic", "status": "up" }
]
},
"master_node": {
"host": "203.0.113.10",
"user": "ubuntu"
}
}active_alerts может быть пустым; upmeter и master_node могут быть null,
пока агент кластера ещё не собрал данные.
Удаление кластера и ожидание его полного уничтожения
DELETE /api/v1/clusters/:id только инициирует удаление — в ответе возвращается кластер со статусом status: "delete"
и флагом is_locked: true. Полное удаление подтверждается тем, что последующий GET возвращает 404 not_found.
CLUSTER_ID="5436e6ef-d811-472f-9c9c-46cb9c6321d9"
curl -sS -X "DELETE" \
"https://$COMMANDER_HOST/api/v1/clusters/$CLUSTER_ID" \
-H "accept: application/json" \
-H "X-Auth-Token: $COMMANDER_TOKEN" | jq "{id, status, is_locked}"{
"id": "5436e6ef-d811-472f-9c9c-46cb9c6321d9",
"status": "delete",
"is_locked": true
}Опрос до полного удаления:
DEADLINE=$(( $(date +%s) + 60 * 60 ))
while : ; do
HTTP_CODE="$(curl -sS -o /dev/null -w "%{http_code}" -X "GET" \
"https://$COMMANDER_HOST/api/v1/clusters/$CLUSTER_ID" \
-H "accept: application/json" \
-H "X-Auth-Token: $COMMANDER_TOKEN")"
echo "http_code=$HTTP_CODE"
if [ "$HTTP_CODE" = "404" ]; then
echo "кластер полностью удалён"
break
fi
if [ "$(date +%s)" -gt "$DEADLINE" ]; then
echo "таймаут ожидания удаления"
exit 1
fi
sleep 10
doneЕсли кластер заблокирован другой операцией (например, идёт converge), DELETE вернёт 409 Conflict:
{
"error": "Cluster is locked",
"error_code": "conflict",
"errors": null
}Повторите DELETE после завершения блокирующей операции или проверьте ожидающие запросы на изменение кластера через
GET /api/v1/cluster_change_requests?cluster_id=$CLUSTER_ID и одобрите/отмените их.
Deckhouse Commander переносит managed-кластер в архив сразу, без задания на удаление: ответ на
DELETE уже содержит status: "deleted", ждать завершения удаления не нужно. Сам кластер и установленный в
нём Deckhouse при этом не удаляются.
Принудительное удаление кластера
Используйте DELETE /api/v1/clusters/:id/force, если инфраструктура уже уничтожена вне Deckhouse Commander либо кластер
находится в состоянии, из которого обычный DELETE /api/v1/clusters/:id не может восстановиться.
Принудительное удаление убирает запись кластера из Deckhouse Commander без запуска dhctl destroy и не затрагивает
внешние ресурсы — очистите инфраструктуру самостоятельно.
curl -sS -X "DELETE" \
"https://$COMMANDER_HOST/api/v1/clusters/$CLUSTER_ID/force" \
-H "accept: application/json" \
-H "X-Auth-Token: $COMMANDER_TOKEN" | jq "{id, status, is_locked}"Для штатного удаления используйте DELETE /api/v1/clusters/:id;
принудительное удаление оставьте как инструмент ручного восстановления
Безвозвратное удаление архивного кластера
Используйте DELETE /api/v1/clusters/:id/permanent, чтобы необратимо удалить кластер, который уже находится в архиве.
При успехе метод возвращает 204 No Content. Для кластера не из архива возвращается 409 Conflict с
error_code: not_archived.
curl -sS -X "DELETE" \
"https://$COMMANDER_HOST/api/v1/clusters/$CLUSTER_ID/permanent" \
-H "accept: application/json" \
-H "X-Auth-Token: $COMMANDER_TOKEN" \
-w "\n%{http_code}\n"Безвозвратное удаление убирает запись кластера из Deckhouse Commander и не запускает dhctl destroy.
История изменений кластера сохраняется.
Безвозвратное удаление архивного шаблона кластера
Используйте DELETE /api/v1/cluster_templates/:id/permanent, чтобы необратимо удалить шаблон кластера,
который уже находится в архиве и на который не ссылается ни один кластер. При успехе метод возвращает 204 No Content.
curl -sS -X "DELETE" \
"https://$COMMANDER_HOST/api/v1/cluster_templates/$CLUSTER_TEMPLATE_ID/permanent" \
-H "accept: application/json" \
-H "X-Auth-Token: $COMMANDER_TOKEN" \
-w "\n%{http_code}\n"Если шаблон не в архиве, API возвращает 409 Conflict с error_code: not_archived.
Если на шаблон ещё ссылаются кластеры, API возвращает 409 Conflict с error_code: used_by_clusters.
Управление проектами
API позволяет получать список проектов, создавать, изменять и удалять проекты типа commander.
Проекты типа deckhouse — это проекты DKP (ресурсы Project модуля
multitenancy-manager), созданные непосредственно в прикладном кластере и не управляемые Deckhouse Commander;
Deckhouse Commander их обнаруживает и отображает, но изменение и удаление таких проектов через API запрещено — попытка
возвращает 403. Подробнее о типах проектов, статусах и коллизиях имён — раздел Проекты
руководства пользователя.
Все примеры используют токен рабочего пространства (заголовок X-Auth-Token: $COMMANDER_TOKEN).
Получение списка проектов
curl -s -X 'GET' \
"https://$COMMANDER_HOST/api/v1/projects" \
-H 'accept: application/json' \
-H "X-Auth-Token: $COMMANDER_TOKEN"В ответе возвращается массив проектов рабочего пространства. Ниже приведён сокращённый пример (часть полей опущена для краткости):
[
{
"id": "1a0c4f1b-8f24-4d0a-9a21-0c2d7e1a2b3c",
"name": "web-frontend",
"kind": "commander",
"description": "Публичный сайт команды",
"resource_quota": {
"requests": {
"cpu": "2",
"memory": "4Gi",
"storage": "50Gi"
}
},
"cumulative_status": "deployed",
"status_data": {},
"kubernetes_project_id": "b7c1d1d8-9d0e-4cbb-8d9e-2b77b6b9f9c1",
"cluster_id": "5436e6ef-d811-472f-9c9c-46cb9c6321d9",
"cluster_name": "prod-eu-1",
"administrators": [
{ "kind": "User", "name": "alice@example.com" },
{ "kind": "Group", "name": "platform-admins" }
],
"error_messages": [],
"created_at": "2026-04-10T12:00:00Z",
"synced_at": "2026-04-23T18:15:32Z"
}
]Ключевые поля ответа:
id— идентификатор проекта в Deckhouse Commander;name— имя проекта (совпадает с именем namespace в прикладном кластере);kind— тип проекта:commanderилиdeckhouse;cluster_id,cluster_name— прикладной кластер, в котором создан проект;cumulative_status— итоговый статус проекта (Статусы проекта);kubernetes_project_id— идентификатор объекта Project в прикладном кластере; заполняется после успешного применения агентом;resource_quota— установленные ресурсные квоты проекта;administrators— пользователи и группы, назначенные администраторами проекта;error_messages— сообщения об ошибках применения проекта, если они есть.
Создание проекта
curl -s -X 'POST' \
"https://$COMMANDER_HOST/api/v1/projects" \
-H 'accept: application/json' \
-H "X-Auth-Token: $COMMANDER_TOKEN" \
-H 'Content-Type: application/json' \
-d '{
"name": "web-frontend",
"cluster_id": "5436e6ef-d811-472f-9c9c-46cb9c6321d9",
"description": "Публичный сайт команды",
"resource_quota": {
"requests": {
"cpu": "2",
"memory": "4Gi",
"storage": "50Gi"
}
},
"administrators": [
{ "kind": "User", "name": "alice@example.com" },
{ "kind": "Group", "name": "platform-admins" }
]
}'Параметры запроса:
name(обязательный) — имя проекта. Должно соответствовать правилам именованияnamespaceв Kubernetes, зарезервированные системные имена (в частности,defaultиdeckhouse) использовать нельзя, уникальность проверяется в пределах кластера.cluster_id(обязательный) — идентификатор прикладного кластера. После создания проекта менять нельзя.administrators(обязательный, минимум один элемент) — массив объектов{"kind": "User"|"Group", "name": "..."}. Значениеkindпринимает толькоUserилиGroup.description— произвольное описание проекта.resource_quota— ресурсные квоты; поддерживаются ключиrequests.cpu,requests.memory,requests.storage. Ограничения (limits) в текущей итерации не задаются.
В успешном ответе 201 Created возвращается тело объекта проекта,
идентичное формату в получении списка.
Сразу после создания cumulative_status обычно равен initialized и со временем переходит в synchronization,
а затем в deployed.
При нарушении правил валидации возвращается 422 Unprocessable Entity,
например при попытке сохранить проект без администраторов:
{
"errors": {
"administrators": ["В проекте должен быть назначен хотя бы один администратор"]
}
}Изменение проекта
curl -s -X 'PUT' \
"https://$COMMANDER_HOST/api/v1/projects/1a0c4f1b-8f24-4d0a-9a21-0c2d7e1a2b3c" \
-H 'accept: application/json' \
-H "X-Auth-Token: $COMMANDER_TOKEN" \
-H 'Content-Type: application/json' \
-d '{
"name": "web-frontend",
"description": "Публичный сайт команды (прод)",
"resource_quota": {
"requests": {
"cpu": "4",
"memory": "8Gi",
"storage": "100Gi"
}
},
"administrators": [
{ "kind": "User", "name": "alice@example.com" },
{ "kind": "User", "name": "bob@example.com" },
{ "kind": "Group", "name": "platform-admins" }
]
}'Тело запроса такое же, как при создании, за исключением поля cluster_id — изменить прикладной кластер у существующего
проекта нельзя. В ответе возвращается обновлённый объект проекта.
Попытка изменить проект типа deckhouse возвращает 403 Forbidden.
Удаление проекта
Удаление проекта через API — необратимая операция. Вместе с проектом в прикладном кластере удаляются все созданные в нём ресурсы (namespace, рабочие нагрузки, конфигурации и секреты). Отменить удаление нельзя, и ни сам проект, ни его ресурсы восстановлению не подлежат. Перед выполнением DELETE-запроса убедитесь, что проект и все его данные больше не нужны — в интеграционном сценарии требуйте явное подтверждение намерения удалить проект на стороне вызывающей системы.
curl -s -X 'DELETE' \
"https://$COMMANDER_HOST/api/v1/projects/1a0c4f1b-8f24-4d0a-9a21-0c2d7e1a2b3c" \
-H 'accept: application/json' \
-H "X-Auth-Token: $COMMANDER_TOKEN"Deckhouse Commander снимает с проекта служебные лейблы и запрашивает у агента удаление ресурса Project в прикладном кластере. В ответе возвращается тело удаляемого проекта с обновлённым статусом.
Удаление проектов типа deckhouse через API недоступно и возвращает 403 Forbidden;
такие проекты удаляются непосредственно в Deckhouse с помощью модуля
multitenancy-manager.
Управление участниками проекта (ресурсы ProjectRoleBinding) и пользовательскими шаблонами проектов через API интеграции в текущем релизе не поддерживаются и появятся в следующих релизах.
Примеры биллинга
Все примеры используют глобальный токен (заголовок X-Auth-Token: $COMMANDER_GLOBAL_TOKEN) и не передают контекст
рабочего пространства.
Получение детализации стоимости за период
Запрос возвращает стоимость CPU за 1 сентября 2026 года с группировкой по кластерам:
$ curl -s -G "https://$COMMANDER_HOST/api/v1/billing_costs" \
--data-urlencode "from=2026-08-31T21:00:00Z" \
--data-urlencode "to=2026-09-01T21:00:00Z" \
--data-urlencode "resources[]=cpu" \
--data-urlencode "group_by=cluster_id" \
-H "accept: application/json" \
-H "X-Auth-Token: $COMMANDER_GLOBAL_TOKEN"Каждая строка содержит измеренный объём, цену из действовавшего тарифа и получившуюся стоимость:
{
"from": "2026-08-31T21:00:00Z",
"to": "2026-09-01T21:00:00Z",
"group_by": "cluster_id",
"entity_type": "pods",
"total": 120.0,
"partial_tariff_unpriced_rows": 0,
"unpriced_compute_classes": 0,
"unpriced_storage_classes": 0,
"items": [
{
"from": "2026-08-31T21:00:00Z",
"to": "2026-09-01T21:00:00Z",
"resource": "cpu",
"entity_type": "pod",
"metrics_kind": "usage",
"class_kind": "compute",
"unit": "core*hour",
"class_name": "standard",
"quantity": 96.0,
"price": 1.25,
"cost": 120.0,
"tariff": {
"id": "8f2b1c4e-5a9d-4f13-8c6b-2d7e0a935114",
"name": "Стандартный 2026",
"start_date": "2026-01-01"
},
"group": {
"cluster_id": "3c81a2f7-6b54-4e29-9d10-7a5f8c2b40e6"
}
}
]
}Выгрузка детализации и постраничное чтение списка
Для выгрузки этой детализации в CSV добавьте response_format=csv:
$ curl -s -D - -o billing_costs.csv -G "https://$COMMANDER_HOST/api/v1/billing_costs" \
--data-urlencode "from=2026-08-31T21:00:00Z" \
--data-urlencode "to=2026-09-01T21:00:00Z" \
--data-urlencode "resources[]=cpu" \
--data-urlencode "response_format=csv" \
-H "X-Auth-Token: $COMMANDER_GLOBAL_TOKEN"HTTP/1.1 200 OK
Content-Type: text/csv; charset=utf-8
Content-Disposition: attachment; filename="billing_costs.csv"
X-Total: 120.0
X-Billing-From: 2026-08-31T21:00:00Z
X-Billing-To: 2026-09-01T21:00:00Z
X-Billing-Group-By: cluster_id
X-Billing-Entity-Type: pods
X-Unpriced-Classes: 0
X-Unpriced-Compute-Classes: 0
X-Unpriced-Storage-Classes: 0Пример следующей страницы истории транзакций с одной записью:
$ curl -s -G "https://$COMMANDER_HOST/api/v1/billing_accounts/$ACCOUNT_ID/transactions" \
--data-urlencode "per_page=1" \
--data-urlencode "cursor=6d1f0b83-9c2a-4e57-b840-1f93c7de5a20" \
-H "accept: application/json" \
-H "X-Auth-Token: $COMMANDER_GLOBAL_TOKEN"{
"data": [
{
"id": "b47c9e02-3d18-4a6f-95be-0c2a71d84f35",
"account_id": "5e90d7a1-8c34-4b26-a1df-93f70e6c2b58",
"kind": "deduction",
"source": "auto",
"amount": "-181.44",
"period_from": "2026-09-01T00:00:00+03:00",
"period_to": "2026-09-02T00:00:00+03:00",
"description": "Списание за сентябрь",
"idempotency_key": null,
"created_at": "2026-09-02T09:15:44.318+03:00"
}
],
"per_page": 1,
"cursor": "b47c9e02-3d18-4a6f-95be-0c2a71d84f35",
"total": 37
}Пополнение счёта и безопасный повтор
Используйте уникальный идентификатор исходной операции в качестве Idempotency-Key, например номер счёта на оплату:
$ curl -s -X "POST" \
"https://$COMMANDER_HOST/api/v1/billing_accounts/$ACCOUNT_ID/transactions" \
-H "accept: application/json" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: invoice-2026-0917" \
-H "X-Auth-Token: $COMMANDER_GLOBAL_TOKEN" \
-d '{"kind": "top_up", "amount": "100.00", "description": "Оплата по счёту"}'Ответ 201:
{
"id": "1a6f2d70-4c85-4b19-9e33-8d02fb71c9a4",
"account_id": "5e90d7a1-8c34-4b26-a1df-93f70e6c2b58",
"kind": "top_up",
"source": "integration",
"amount": "100.0",
"period_from": null,
"period_to": null,
"description": "Оплата по счёту",
"idempotency_key": "invoice-2026-0917",
"created_at": "2026-09-17T12:04:03.921+03:00"
}Если изменить amount на 250.00, сохранив прежний ключ, API вернёт 409 idempotency_conflict.
Импорт тарифов
Права доступа и обработка ошибок описаны в разделе изменение и импорт тарифов. Пример создания тарифа из JSON:
$ curl -s -X "POST" \
"https://$COMMANDER_HOST/api/v1/billing_tariffs/import" \
-H "accept: application/json" \
-H "Content-Type: application/json" \
-H "X-Auth-Token: $COMMANDER_GLOBAL_TOKEN" \
-d '{
"tariffs": [
{
"name": "Стандартный 2027",
"start_date": "2027-01-01",
"aggregation": "average",
"metrics_kind": "usage",
"tariff_bindings": [
{"label_kind": "compute", "label_name": "standard", "cpu_price": "1.25", "ram_price": "2.50"}
]
}
]
}'Сокращённый пример успешного ответа (200):
[
{
"index": 0,
"name": "Стандартный 2027",
"status": "created",
"tariff": {
"id": "c0a4e18b-77d3-4f52-b6ac-9e1d3820f4a7",
"name": "Стандартный 2027",
"start_date": "2027-01-01"
}
}
]В CSV строки с одинаковым tariff_key объединяются в один тариф с несколькими привязками.
Сохраните следующий пример в tariffs.csv:
tariff_key,name,start_date,aggregation,metrics_kind,label_kind,label_name,cpu_price,ram_price,storage_price
standard-2027,Стандартный 2027,2027-01-01,average,usage,compute,standard,1.25,2.50,
standard-2027,Стандартный 2027,2027-01-01,average,usage,storage_class,fast-ssd,,,0.40$ curl -s -X "POST" \
"https://$COMMANDER_HOST/api/v1/billing_tariffs/import" \
-H "accept: application/json" \
-H "Content-Type: text/csv" \
-H "X-Auth-Token: $COMMANDER_GLOBAL_TOKEN" \
--data-binary @tariffs.csv