Для AI-агентов: markdown этой страницы — /docs-content/source-storage/publish.md индекс документации — /llms.txt
Снапшот перед публикацией
Публикация приложения требует сохранённого снимка исходников. Здесь — что именно проверяется, что приходит в отказе 409 SNAPSHOT_REQUIRED и как опубликовать исходники, которые автосейв деплоя сохранил у сервера, а не у приложения.
Обзор хранилища и справочник эндпоинтов — Хранилище исходного кода.
Гарантия наличия снапшота перед публикацией
POST /v1/apps/:id/publish проверяет, что у приложения есть сохранённый снапшот. Возраст снапшота на исход проверки не влияет — версия, сохранённая давно, подходит наравне с только что созданной. Отказ 409 SNAPSHOT_REQUIRED приходит, когда пригодного снапшота нет: его не сохраняли либо файлы сохранённой версии удалены. В подсказке сказано, какой вызов нужно сделать перед повторной публикацией.
Проверка работает только если в портале включено сохранение исходников (по умолчанию включено, владелец портала может отключить — Отключение для портала).
Пример отказа:
{
"success": false,
"error": {
"code": "SNAPSHOT_REQUIRED",
"message": "Publish requires a saved source snapshot for this app. Call POST /v1/apps/:id/sources, or publish with sourceServerId set to the server you deployed to — a deploy auto-save is stored on that server, not on the app.",
"hint": {
"requiredAction": "POST /v1/apps/:id/sources",
"reason": "app_snapshot_missing",
"serverKeyedSources": {
"requiredAction": "POST /v1/apps/:id/publish with sourceServerId (the server you deployed to)",
"bodyField": "sourceServerId",
"header": "x-source-server",
"listEndpoint": "GET /v1/infra/servers/:serverId/sources",
"registryEndpoint": "GET /v1/me/sources"
},
"toolName": "save_sources",
"lastSnapshot": null
}
}
}
Блок hint.lastSnapshot приходит непустым, только когда платформа проверяет возраст снапшота. В нём timestamp — время создания версии, presentedAt — время, когда её последний раз предъявили сохранением, а ageMinutes считается от presentedAt: у дедуплицированного сохранения эти два значения расходятся. Поле hint.freshnessWindowMinutes приходит по тому же условию — это длина окна свежести в минутах.
Поле hint.lastSnapshot равно null, когда пригодного снапшота нет — его не сохраняли либо файлы сохранённой версии удалены. Пока возраст снапшота не проверяется, отказ приходит только по этой причине, поэтому поле в нём всегда null.
hint.reason называет причину отказа одним из двух значений: app_snapshot_missing, server_snapshot_missing. Ещё два значения — app_snapshot_stale и server_snapshot_stale — приходят, только когда платформа проверяет возраст снапшота. Блок hint.serverKeyedSources приходит только когда сервер в запросе не указан, — он объясняет, что делать, если исходники сохранил автосейв деплоя.
Публикация исходников с сервера
Автосейв деплоя сохраняет исходники у того сервера, на который вы деплоили. Если сервер принадлежит не OAuth-ключу публикуемого приложения — например, деплой шёл под личным ключом vibe_api_ — снапшот хранится у сервера (appId пустой), и проверка публикации, которая по умолчанию смотрит снапшоты приложения, его не находит. Деплой при этом честно отвечает autoSaved: true: байты сохранены.
Чтобы опубликовать именно их, назовите сервер:
curl -X POST "https://vibecode.bitrix24.tech/v1/apps/$APP_ID/publish" \
-H "X-API-Key: $VIBE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"sourceServerId": "'"$SERVER_ID"'"}'
Тот же смысл у заголовка X-Source-Server: <serverId>.
Что важно знать:
Сервер платформа не подбирает. Идентификатор передаёте вы — это тот же
:id, по которому вы вызывали деплой. Автоподбор «ближайшего» сервера опубликовал бы исходники, которых никто не называл.Права на сервер проверяются отдельно от прав на приложение. Публиковать исходники сервера может ключ-владелец сервера, личный ключ того же пользователя или администратор портала. Сервера нет, он из другого портала или удалён —
404 SERVER_NOT_FOUND. Прав нет —403 NOT_AUTHORIZED.Нумерация версий у сервера своя. Вместе с
sourceServerIdполеsourceVersionIdозначает версию ЭТОГО сервера:v3сервера иv3приложения — разные версии.Версию сервера публикация не помечает. Своей версии приложения публикация ставит тег
publishedи пишет отметку о публикации, а версии сервера — нет. У серверной версии те же поля хранят историю ДЕПЛОЯ (GET /v1/infra/servers/:serverId/sourcesих и показывает), а тегpublished— это бессрочное хранение и запрет на удаление. Публикация одного приложения не меняет ни то, ни другое у версии, которая ему не принадлежит. Что именно опубликовано, видно в журнале действий записью о публикации.У опубликованной версии сервера нет бессрочного хранения. Раз тега на ней нет, она подчиняется обычным правилам очистки: платформа всегда бережёт пять последних версий, плюс по одной версии на день за последние 14 дней и по одной на неделю за последние 4 недели. Новые деплои на тот же сервер вытесняют опубликованную версию из этих правил, и она может быть удалена, пока приложение остаётся опубликованным. Об этом же напоминает строка в
warningsв ответе на публикацию. Чтобы сохранить версию навсегда, пометьте её сами — это ваше решение, потому что бессрочное хранение занимает место и запрещает удаление:curl -X PATCH "https://vibecode.bitrix24.tech/v1/infra/servers/$SERVER_ID/sources/v3" \ -H "X-API-Key: $VIBE_API_KEY" \ -H "Content-Type: application/json" \ -d '{"tags": ["manual"]}'Теги заменяются целиком: если у версии уже есть свои метки, перечислите их в том же массиве рядом с
manual. Готовый список приходит в той самой строкеwarnings.Без
sourceServerIdвсё работает как раньше — проверка ищет сохранённый снапшот приложения, ставит тег и отметку о публикации.Заголовок проверяется так же, как поле, — но только когда поля нет. Если
sourceServerIdпришёл в теле, он и решает: заголовок не читается, и что бы в нём ни лежало, на ответ это не влияет. Если поля в теле нет, заголовок проверяется строго: не в форме UUID или переданный дважды —400 VALIDATION_ERROR. Молча игнорировать его в этом случае нельзя, иначе публикация взяла бы снапшот приложения, пока вы думаете, что назвали сервер.Хранилище исходников выключено для портала — селектор не используется вовсе, публикация проходит, а в
warningsприходит строка о том, что переданныйsourceServerIdне пригодился.
Где взять идентификатор сервера, если он не сохранился: GET /v1/infra/servers/:serverId/sources перечисляет версии конкретного сервера, а GET /v1/me/sources — сводный реестр всех источников, доступных ключу, по серверам и приложениям.
Параметры для повторной публикации той же версии (не самой свежей):
sourceVersionIdв теле — форматv<N>, например"sourceVersionId": "v3".- Заголовок
x-source-version: v3— альтернатива телу.
Коды ошибок
Отказы POST /v1/apps/:id/publish, специфичные для проверки снапшота:
| HTTP | Код | Когда возвращается |
|---|---|---|
| 409 | SNAPSHOT_REQUIRED |
Сохранённого снапшота нет либо файлы сохранённой версии удалены (только при включённом сохранении). В ответе — поле hint с описанием действия. |
Полный справочник кодов — Коды ошибок.