#Source Storage API
Сохранение исходного кода приложения для непрерывности бизнеса. Каждый снапшот — версионированный blob, привязанный к приложению (не к разработчику). Если разработчик уходит или AI-сессия заменяется, новый сотрудник загружает последнюю версию и продолжает работу.
Базовый URL: https://vibecode.bitrix24.tech/v1
Авторизация: заголовок X-Api-Key с ключом авторизации приложения (vibe_app_*) — владельцем целевого приложения.
Допустимые форматы архива: application/gzip, application/x-tar, application/zip, application/octet-stream.
Лимит тела: 200 МБ на один запрос.
Эндпоинты:
| Метод | Путь | Действие |
|---|---|---|
POST |
/v1/apps/:id/sources |
Сохранить снапшот |
GET |
/v1/apps/:id/sources |
Список версий |
GET |
/v1/apps/:id/sources/:versionId/download |
Подписанная ссылка на скачивание |
PATCH |
/v1/apps/:id/sources/:versionId |
Обновить теги / комментарий |
POST |
/v1/apps/:id/sources/:versionId/tag |
Добавить или снять тег |
DELETE |
/v1/apps/:id/sources/:versionId |
Удалить версию |
POST |
/v1/apps/:id/sources/cleanup |
Массовая очистка старых версий |
#Сохранение исходников
С версии 2026-05-23 платформа автоматически сохраняет байты исходников в depot при каждом успешном деплое. Это касается:
POST /v1/infra/servers/:id/deploy { source: { content: <base64> } }— встроенные байты сохраняются как новая версияPOST /v1/infra/servers/:id/deploy { source: { url: <signed URL из depot> } }— существующая версия линкуется к deployIdPOST /v1/infra/servers/:id/deploy { source: { versionId: 'vN' } }— то же
Исключение: deploy с external URL (не наш bucket) возвращает 409 SNAPSHOT_REQUIRED. Чтобы обойти — либо сначала загрузите в depot через POST /v1/apps/:id/sources и деплойте через {source: {versionId: 'vN'}}, либо передайте header X-Skip-Source-Snapshot: <reason> для явного отказа.
#Когда auto-save НЕ срабатывает (skippedReason values)
Deploy всегда возвращает поле source.skippedReason когда autoSaved=false — это явно объясняет почему. Возможные значения:
'no-linked-app'— РЕТАЙРЕН в A5 Phase 2 (2026-05-26). Раньше personalvibe_api_*ключи без привязки к application получали этот skip. С Phase 2 personal-key серверы автосейвят через server-keyed write path ({appId: null, serverId: <id>}в строке snapshot'а). Существующие audit-log записи с этим значением сохраняются для исторического рендеринга, но новые deploys больше не эмитят его.'feature-disabled-platform'—PlatformSettings.sourceStorageEnabled = false. Обратитесь к platform admin.'feature-disabled-portal'—Portal.sourceStorageEnabled = false. Portal admin может включить в/admin/settings.'save-failed'— storage error (5xx от хранилища, etc). Deploy всё равно вернул 200 — но snapshot не сохранён.'external-url-or-toggles-off'—{url}deploy с our-bucket URL, гдеtryBackfillSourceFromUrlне смог resolver snapshot (cross-tenant / soft-deleted / malformed URL).'no-source-or-unknown-variant'— source отсутствует или передан в неизвестном формате.<user-supplied string>— caller передалX-Skip-Source-Snapshot: <reason>header (Branch A audit-emitted path).
Если skippedReason: null И autoSaved: false — это БАГ, отрепортите.
#Когда `POST /sources` всё ещё нужен явно
Только три сценария:
- Tag a version — пометить
manual/publishedдля retention forever - Save without deploy — сохранить WIP для handoff
- Rollback prep — снимок baseline перед рискованным изменением
#Когда вызывать
Типовой сценарий для AI-агента (с 2026-05-23 явный вызов POST /sources перед деплоем не требуется — платформа сохраняет автоматически):
- Изменил код →
POST /v1/infra/servers/:id/deploy— исходники сохраняются автоматически. POST /v1/apps/:id/publish— публикует приложение в каталоге Битрикс24. Тегpublishedдобавляется к снапшоту автоматически.- Повторил цикл при следующем изменении.
Сценарий передачи проекта новому разработчику или новой AI-сессии:
GET /v1/apps/:id/sources— список доступных версий.GET /v1/apps/:id/sources/:versionId/download— подписанная ссылка на архив.- Скачать архив, распаковать и продолжить работу.
Для MCP-клиентов: инструмент save_sources пакует файловое дерево в tar.gz на стороне клиента и отправляет одним запросом. Инструмент load_sources загружает последнюю версию и распаковывает обратно в дерево файлов. Прямой вызов HTTP-эндпоинта доступен для клиентов, которые самостоятельно упаковывают архив.
#Гарантия наличия снапшота перед публикацией
POST /v1/apps/:id/publish проверяет наличие снапшота не старше 10 минут. Если снапшота нет или он устарел — возвращается 409 SNAPSHOT_REQUIRED с подсказкой, какой вызов нужно сделать перед повторной публикацией.
Проверка работает только если в портале включено сохранение исходников (по умолчанию — включено; владелец портала может отключить, см. раздел «Отключение для портала»).
Пример отказа:
{
"success": false,
"error": {
"code": "SNAPSHOT_REQUIRED",
"message": "Deploy requires a recent source snapshot. Call POST /v1/apps/:id/sources first.",
"hint": {
"requiredAction": "POST /v1/apps/:id/sources",
"toolName": "save_sources",
"freshnessWindowMinutes": 10,
"lastSnapshot": {
"versionId": "v3",
"timestamp": "2026-05-21T09:42:11.000Z",
"ageMinutes": 23
}
}
}
}
Поле hint.lastSnapshot равно null, если для приложения ещё нет ни одного снапшота.
Параметры для повторной публикации той же версии (не самой свежей):
sourceVersionIdв теле — форматv<N>, например"sourceVersionId": "v3".- Заголовок
x-source-version: v3— альтернатива телу.
#Срок жизни версий (политика хранения)
После сохранения версия проходит через автоматическую очистку по графику «дед-отец-сын» (Grandfather-Father-Son):
- Последние 5 версий хранятся всегда, независимо от тегов.
- Дневная сетка за 14 дней — одна версия на каждый календарный день (UTC).
- Недельная сетка за 4 недели — одна версия на каждые 7 дней.
- Теги
publishedиmanual— хранятся бессрочно, никогда не удаляются автоматически.
Для пользовательской очистки на запрос — POST /v1/apps/:id/sources/cleanup. Версии с тегами published и manual исключаются и из неё.
#Сохранение снапшота
POST /v1/apps/:id/sources
Принимает raw blob — архив исходного кода. Формат определяется заголовком Content-Type. Метаданные (теги, комментарий) передаются через дополнительные заголовки.
#Параметры пути
| Параметр | Тип | Описание |
|---|---|---|
id (path) |
UUID | Идентификатор приложения. Получить — `GET /v1/apps`. |
#Заголовки запроса
| Заголовок | Обязательный | Описание |
|---|---|---|
Content-Type |
да | Формат архива. Допустимые значения: application/gzip, application/x-tar, application/zip, application/octet-stream. Любое другое значение → 400 INVALID_CONTENT_TYPE. |
X-Filename |
нет | Произвольное имя файла для отображения (например, app-v1.tar.gz). Если не указан — имя выводится из Content-Type (например, source.tar.gz для application/gzip). |
X-Tags |
нет | Теги через запятую. Распознаются manual и published — они защищают версию от автоматической очистки. |
X-Note |
нет | Произвольный комментарий, который сохраняется в записи о версии. |
X-AI-Session-Id |
нет | Идентификатор AI-сессии. Группирует снапшоты в манифесте по сессии. |
#Тело запроса
Сырые байты архива (raw blob). Не multipart. Максимальный размер — 200 МБ.
#Ответ при успешном сохранении
HTTP 201:
{
"success": true,
"data": {
"versionId": "v3",
"filename": "2026-05-21T10-15-30-000Z-v3.tar.gz",
"contentType": "application/gzip",
"timestamp": "2026-05-21T10:15:30.000Z",
"sha256": "a3f5d8b2c1e9f4...",
"size": 184320,
"deduplicated": false,
"tags": []
}
}
#Идемпотентность
Повторное сохранение архива с тем же содержимым даёт тот же sha256 и не создаёт новую версию. Эндпоинт возвращает HTTP 201 с deduplicated: true и versionId уже существующей версии. Если при повторном сохранении переданы новые теги или комментарий — они добавляются к существующей версии (теги объединяются, комментарий перезаписывается).
В обоих случаях обновляется метка App.lastSourceSavedAt — проверка свежести перед POST /v1/apps/:id/publish принимает дедуплицированное сохранение наравне с настоящим.
#Ответ при отключённом сохранении
HTTP 200 — снапшот не создан:
{
"success": true,
"data": {
"skipped": true,
"reason": "DISABLED_GLOBALLY"
}
}
reason — "DISABLED_GLOBALLY" (платформа не включила функцию) или "DISABLED_FOR_PORTAL" (владелец портала отключил для своего портала). При любом из вариантов POST /v1/apps/:id/publish не проверяет наличие снапшота.
#Примеры
#curl — tar.gz
curl -X POST https://vibecode.bitrix24.tech/v1/apps/<APP_ID>/sources \
-H "X-Api-Key: YOUR_APP_KEY" \
-H "Content-Type: application/gzip" \
-H "X-Note: Добавил OAuth-флоу" \
-H "X-Tags: manual" \
--data-binary @app-sources.tar.gz
#curl — zip
curl -X POST https://vibecode.bitrix24.tech/v1/apps/<APP_ID>/sources \
-H "X-Api-Key: YOUR_APP_KEY" \
-H "Content-Type: application/zip" \
--data-binary @app-sources.zip
#JavaScript
const archive = await fs.readFile('app-sources.tar.gz')
const res = await fetch(
`https://vibecode.bitrix24.tech/v1/apps/${appId}/sources`,
{
method: 'POST',
headers: {
'X-Api-Key': process.env.VIBE_APP_KEY,
'Content-Type': 'application/gzip',
'X-Note': 'Добавил OAuth-флоу',
},
body: archive,
},
)
const json = await res.json()
console.log(json.data.versionId, 'deduplicated:', json.data.deduplicated)
#Коды ошибок
| HTTP | Код | Когда возвращается |
|---|---|---|
| 400 | INVALID_CONTENT_TYPE |
Content-Type не входит в список допустимых форматов архива. |
| 400 | INVALID_BLOB |
Тело запроса не является корректным бинарным буфером. |
| 403 | NOT_AUTHORIZED |
Ключ не является владельцем приложения (личный или менеджмент-ключ; ключ авторизации другого приложения). |
| 404 | APP_NOT_FOUND |
Приложение не существует, удалено или принадлежит другому порталу. |
| 413 | SOURCE_BLOB_TOO_LARGE |
Размер blob превышает 200 МБ. |
| 500 | SOURCE_STORAGE_ERROR |
Внутренняя ошибка хранилища. |
#Обновление метаданных версии
PATCH /v1/apps/:id/sources/:versionId
Обновляет теги и/или комментарий существующей версии без повторной загрузки архива. Удобно, если нужно добавить тег или скорректировать комментарий задним числом.
#Параметры пути
| Параметр | Тип | Описание |
|---|---|---|
id (path) |
UUID | Идентификатор приложения. |
versionId (path) |
string | Идентификатор версии вида v<N>. |
#Поля тела
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
tags |
string[] | нет | Новый полный список тегов. Заменяет существующие теги целиком. Если поле отсутствует — теги не изменяются. |
note |
string | null | нет | Комментарий. Строка — перезаписывает текущий. null — очищает комментарий. Если поле отсутствует — комментарий не изменяется. |
Можно передать только tags, только note или оба поля одновременно.
#Ответ
HTTP 200:
{
"success": true,
"data": {
"versionId": "v3",
"tags": ["manual"],
"note": "Финальная версия перед релизом"
}
}
#Примеры
#curl — добавить тег `manual`
curl -X PATCH https://vibecode.bitrix24.tech/v1/apps/<APP_ID>/sources/v3 \
-H "X-Api-Key: YOUR_APP_KEY" \
-H "Content-Type: application/json" \
-d '{ "tags": ["manual"] }'
#curl — очистить комментарий
curl -X PATCH https://vibecode.bitrix24.tech/v1/apps/<APP_ID>/sources/v3 \
-H "X-Api-Key: YOUR_APP_KEY" \
-H "Content-Type: application/json" \
-d '{ "note": null }'
#JavaScript
await fetch(
`https://vibecode.bitrix24.tech/v1/apps/${appId}/sources/v3`,
{
method: 'PATCH',
headers: {
'X-Api-Key': process.env.VIBE_APP_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({ tags: ['manual'], note: 'Финальная версия' }),
},
)
#Коды ошибок
| HTTP | Код | Когда возвращается |
|---|---|---|
| 400 | INVALID_METADATA |
Тело не прошло валидацию (некорректные теги или типы полей). |
| 400 | INVALID_VERSION_ID |
Формат versionId не соответствует v<целое неотрицательное число>. |
| 403 | NOT_AUTHORIZED |
Ключ не является владельцем приложения. |
| 404 | APP_NOT_FOUND |
Приложение не существует, удалено или принадлежит другому порталу. |
| 404 | VERSION_NOT_FOUND |
Версия с таким versionId не существует или была удалена. |
#Список версий
GET /v1/apps/:id/sources
Возвращает все актуальные (не удалённые) версии в порядке убывания времени сохранения — самая свежая первой.
#Параметры пути
| Параметр | Тип | Описание |
|---|---|---|
id (path) |
UUID | Идентификатор приложения. |
#Ответ
HTTP 200:
{
"success": true,
"data": {
"totalVersions": 3,
"currentVersionId": "v3",
"totalSizeBytes": 552960,
"versions": [
{
"versionId": "v3",
"filename": "2026-05-21T10-15-30-000Z-v3.tar.gz",
"contentType": "application/gzip",
"timestamp": "2026-05-21T10:15:30.000Z",
"size": 184320,
"sha256": "a3f5d8b2c1e9f4...",
"tags": [],
"savedBy": {
"userId": "8f1a2b3c-...",
"session": "claude-session-2026-05-21"
},
"linkedDeployId": null,
"deployStatus": null,
"note": "Добавил OAuth-флоу"
},
{
"versionId": "v2",
"filename": "2026-05-20T18-02-11-000Z-v2-published.tar.gz",
"contentType": "application/gzip",
"timestamp": "2026-05-20T18:02:11.000Z",
"size": 184320,
"sha256": "9e7c4b2a1f8d3...",
"tags": ["published"],
"savedBy": {
"userId": "8f1a2b3c-...",
"session": null
},
"linkedDeployId": "publish:2026-05-20T18:05:42.000Z",
"deployStatus": "success",
"note": null
},
{
"versionId": "v1",
"filename": "2026-05-20T09-44-50-000Z-v1.tar.gz",
"contentType": "application/gzip",
"timestamp": "2026-05-20T09:44:50.000Z",
"size": 184320,
"sha256": "5a8d3e2f1c4b9...",
"tags": [],
"savedBy": { "userId": "8f1a2b3c-...", "session": null },
"linkedDeployId": null,
"deployStatus": null,
"note": null
}
]
}
}
#Поля ответа
| Поле | Тип | Описание |
|---|---|---|
data.totalVersions |
number | Общее количество актуальных версий. |
data.currentVersionId |
string | null | Идентификатор самой свежей версии (v<N>). null, если версий нет. |
data.totalSizeBytes |
number | Суммарный размер всех архивов в байтах. |
data.versions[].versionId |
string | Идентификатор версии вида v<N>. |
data.versions[].filename |
string | Имя файла в хранилище. Содержит суффикс -published или -manual, если есть соответствующий тег. |
data.versions[].contentType |
string | null | Тип архива (application/gzip, application/zip и т.д.). |
data.versions[].timestamp |
string | Время сохранения (ISO 8601, UTC). |
data.versions[].size |
number | Размер архива в байтах. |
data.versions[].sha256 |
string | SHA-256 содержимого архива. Используется для дедупликации. |
data.versions[].tags |
string[] | Активные теги: manual, published. |
data.versions[].savedBy.userId |
string | null | Идентификатор пользователя Вайбкод. |
data.versions[].savedBy.session |
string | null | Идентификатор AI-сессии (из заголовка X-AI-Session-Id). |
data.versions[].linkedDeployId |
string | null | Идентификатор публикации (заполняется после POST /v1/apps/:id/publish). |
data.versions[].deployStatus |
string | null | Статус публикации: success или failed. |
data.versions[].note |
string | null | Комментарий из поля X-Note при сохранении или из PATCH. |
#Примеры
#curl
curl -H "X-Api-Key: YOUR_APP_KEY" \
https://vibecode.bitrix24.tech/v1/apps/<APP_ID>/sources
#JavaScript
const res = await fetch(
`https://vibecode.bitrix24.tech/v1/apps/${appId}/sources`,
{ headers: { 'X-Api-Key': process.env.VIBE_APP_KEY } },
)
const { data } = await res.json()
console.log(`Версий: ${data.totalVersions}, последняя: ${data.currentVersionId}`)
#Коды ошибок
| HTTP | Код | Когда возвращается |
|---|---|---|
| 403 | NOT_AUTHORIZED |
Ключ не является владельцем приложения. |
| 404 | APP_NOT_FOUND |
Приложение не существует, удалено или принадлежит другому порталу. |
#Скачивание архива
GET /v1/apps/:id/sources/:versionId/download
Возвращает подписанную ссылку на архив версии. Ссылка действует 30 минут.
#Параметры пути
| Параметр | Тип | Описание |
|---|---|---|
id (path) |
UUID | Идентификатор приложения. |
versionId (path) |
string | Идентификатор версии вида v<N>. |
#Ответ
HTTP 200:
{
"success": true,
"data": {
"url": "https://<storage-endpoint>/...",
"expiresAt": "2026-05-21T10:45:30.000Z"
}
}
#Примеры
#curl
# Получить ссылку
curl -H "X-Api-Key: YOUR_APP_KEY" \
https://vibecode.bitrix24.tech/v1/apps/<APP_ID>/sources/v3/download
# Скачать архив по полученной ссылке (без дополнительных заголовков)
curl -o source-v3.tar.gz "<url из ответа выше>"
#JavaScript
const { data } = await fetch(
`https://vibecode.bitrix24.tech/v1/apps/${appId}/sources/v3/download`,
{ headers: { 'X-Api-Key': process.env.VIBE_APP_KEY } },
).then((r) => r.json())
const archive = await fetch(data.url)
const buffer = await archive.arrayBuffer()
// Дальше — распаковка архива (tar.gz: tar library; zip: JSZip или аналог)
#Коды ошибок
| HTTP | Код | Когда возвращается |
|---|---|---|
| 400 | INVALID_VERSION_ID |
Формат versionId не соответствует v<целое неотрицательное число>. |
| 403 | NOT_AUTHORIZED |
Ключ не является владельцем приложения. |
| 404 | APP_NOT_FOUND |
Приложение не существует, удалено или принадлежит другому порталу. |
| 404 | VERSION_NOT_FOUND |
Версия с таким versionId не существует или была удалена. |
| 502 | SOURCE_DOWNLOAD_URL_FAILED |
Хранилище временно недоступно — повторите запрос. |
#Постановка и снятие тега
POST /v1/apps/:id/sources/:versionId/tag
Распознаются два тега, оба защищают версию от автоматической очистки:
manual— версия закреплена оператором вручную.published— версия зафиксирована как опубликованная (этот тег также проставляется автоматически послеPOST /v1/apps/:id/publish).
#Параметры пути
| Параметр | Тип | Описание |
|---|---|---|
id (path) |
UUID | Идентификатор приложения. |
versionId (path) |
string | Идентификатор версии вида v<N>. |
#Поля тела
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
tag |
string | да | manual или published. |
action |
string | да | add — добавить тег; remove — снять. |
#Ответ
HTTP 200:
{
"success": true,
"data": {
"versionId": "v3",
"tags": ["manual"]
}
}
#Примеры
#curl
curl -X POST https://vibecode.bitrix24.tech/v1/apps/<APP_ID>/sources/v3/tag \
-H "X-Api-Key: YOUR_APP_KEY" \
-H "Content-Type: application/json" \
-d '{ "tag": "manual", "action": "add" }'
#JavaScript
await fetch(
`https://vibecode.bitrix24.tech/v1/apps/${appId}/sources/v3/tag`,
{
method: 'POST',
headers: {
'X-Api-Key': process.env.VIBE_APP_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({ tag: 'manual', action: 'add' }),
},
)
#Коды ошибок
| HTTP | Код | Когда возвращается |
|---|---|---|
| 400 | INVALID_TAG |
Тег не входит в набор manual, published. |
| 400 | INVALID_ACTION |
Действие не равно add или remove. |
| 400 | INVALID_VERSION_ID |
Формат versionId не соответствует v<целое неотрицательное число>. |
| 403 | NOT_AUTHORIZED |
Ключ не является владельцем приложения. |
| 404 | APP_NOT_FOUND |
Приложение не существует, удалено или принадлежит другому порталу. |
| 404 | VERSION_NOT_FOUND |
Версия с таким versionId не существует или была удалена. |
#Удаление версии
DELETE /v1/apps/:id/sources/:versionId
Помечает версию как удалённую. Восстановить версию через API нельзя. Версия с тегом published или manual защищена от удаления — сначала снимите тег через PATCH /v1/apps/:id/sources/:versionId с телом {"tags": []} (или с сохранением других тегов). Ответ включает hint.preservedTags — список тегов без retention-маркеров для безопасного bypass.
#Параметры пути
| Параметр | Тип | Описание |
|---|---|---|
id (path) |
UUID | Идентификатор приложения. |
versionId (path) |
string | Идентификатор версии вида v<N>. |
#Ответ
HTTP 200:
{
"success": true,
"data": { "versionId": "v3" }
}
#Примеры
#curl
curl -X DELETE https://vibecode.bitrix24.tech/v1/apps/<APP_ID>/sources/v3 \
-H "X-Api-Key: YOUR_APP_KEY"
#JavaScript
await fetch(
`https://vibecode.bitrix24.tech/v1/apps/${appId}/sources/v3`,
{
method: 'DELETE',
headers: { 'X-Api-Key': process.env.VIBE_APP_KEY },
},
)
#Коды ошибок
| HTTP | Код | Когда возвращается |
|---|---|---|
| 400 | INVALID_VERSION_ID |
Формат versionId не соответствует v<целое неотрицательное число>. |
| 403 | NOT_AUTHORIZED |
Ключ не является владельцем приложения. |
| 404 | APP_NOT_FOUND |
Приложение не существует, удалено или принадлежит другому порталу. |
| 404 | VERSION_NOT_FOUND |
Версия с таким versionId не существует или уже удалена. |
| 409 | PROTECTED_BY_TAG |
Версия отмечена тегом published или manual. Сначала снимите тег через PATCH /v1/apps/:id/sources/:versionId. |
#Массовая очистка
POST /v1/apps/:id/sources/cleanup
Удаляет старые версии, оставляя keepLatest самых свежих (по умолчанию — 5). Версии с тегами manual и published исключаются из очистки независимо от keepLatest.
#Параметры пути
| Параметр | Тип | Описание |
|---|---|---|
id (path) |
UUID | Идентификатор приложения. |
#Поля тела
| Поле | Тип | Обязательное | По умолчанию | Описание |
|---|---|---|---|---|
keepLatest |
number | нет | 5 |
Сколько последних версий оставить. Целое неотрицательное число. 0 оставит только версии с тегами manual и published. |
Тело можно опустить — применяется значение по умолчанию.
#Ответ
HTTP 200:
{
"success": true,
"data": {
"deletedVersions": ["v2", "v1"]
}
}
#Примеры
#curl
curl -X POST https://vibecode.bitrix24.tech/v1/apps/<APP_ID>/sources/cleanup \
-H "X-Api-Key: YOUR_APP_KEY" \
-H "Content-Type: application/json" \
-d '{ "keepLatest": 3 }'
#JavaScript
const res = await fetch(
`https://vibecode.bitrix24.tech/v1/apps/${appId}/sources/cleanup`,
{
method: 'POST',
headers: {
'X-Api-Key': process.env.VIBE_APP_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({ keepLatest: 3 }),
},
)
const { data } = await res.json()
console.log('Удалено версий:', data.deletedVersions.length)
#Коды ошибок
| HTTP | Код | Когда возвращается |
|---|---|---|
| 400 | INVALID_KEEP_LATEST |
Значение keepLatest не является целым неотрицательным числом. |
| 403 | NOT_AUTHORIZED |
Ключ не является владельцем приложения. |
| 404 | APP_NOT_FOUND |
Приложение не существует, удалено или принадлежит другому порталу. |
#Деплой из снапшота
POST /v1/infra/servers/:id/deploy принимает поле source.versionId — альтернатива source.url и source.content. Позволяет задеплоить на сервер ровно ту версию, которая была сохранена через save_sources, без отдельной загрузки файла.
{
"source": {
"versionId": "v3"
},
"start": "node server.js"
}
Сервер разрешает versionId в подписанную ссылку на архив и выполняет деплой. После успешного или неуспешного деплоя поле linkedDeployId у снапшота обновляется автоматически.
Ограничение: серверу должен соответствовать ключ авторизации типа OAuth-App (vibe_app_*). Личные и менеджмент-ключи без привязанного appId не могут разрешить снапшот — вернётся 400 SOURCE_VERSION_REQUIRES_APP.
#Поведение перед публикацией
Описано выше в разделе «Гарантия наличия снапшота перед публикацией». Справочник кодов ошибок POST /v1/apps/:id/publish, специфичных для проверки снапшота:
| HTTP | Код | Когда возвращается |
|---|---|---|
| 409 | SNAPSHOT_REQUIRED |
Снапшота нет или он старше 10 минут (только при включённом сохранении). В ответе — поле hint с описанием действия. |
#Проверка состояния портала
GET /v1/me возвращает блок data.capabilities.apps.sourceStorage:
При включённом сохранении:
{
"capabilities": {
"apps": {
"sourceStorage": {
"enabled": true,
"freshnessWindowMinutes": 10,
"requiredBeforeDeploy": true,
"limits": {
"maxBlobBytes": 209715200
}
}
}
}
}
При отключённом сохранении:
{
"capabilities": {
"apps": {
"sourceStorage": {
"enabled": false,
"requiredBeforeDeploy": false,
"disabledBy": "platform"
}
}
}
}
Поле disabledBy — "platform" (платформа ещё не включила функцию) или "portal" (владелец портала отключил). Это помогает агентам понять, к кому обращаться за включением.
#Отключение для портала
Владелец портала может отключить сохранение исходников через раздел администрирования (PATCH /api/admin/source-storage с телом { "sourceStorageEnabled": false }, требует сессии администратора). При отключении:
POST /v1/apps/:id/sourcesвозвращает200 { skipped: true, reason: "DISABLED_FOR_PORTAL" }.POST /v1/apps/:id/publishне проверяет наличие снапшота.GET /v1/apps/:id/sourcesпродолжает возвращать ранее сохранённые версии.GET /v1/apps/:id/sources/:versionId/downloadостаётся доступным.
История сохранений переживает повторное включение — ранее созданные версии остаются доступными.
#Поведение для AI-моделей
MCP-инструмент save_sources обёртывает POST /v1/apps/:id/sources. Инструмент load_sources скачивает и распаковывает последний снапшот в дерево файлов.
С 2026-05-23 явный вызов save_sources перед деплоем не требуется — deploy-handler сохраняет байты автоматически. save_sources остаётся нужным только для трёх явных сценариев (раздел «Сохранение исходников» выше).
Каналы, через которые модель узнаёт о сохранении исходников:
- Описание MCP-инструментов
save_sourcesиload_sources— видно при первом обращении. - Подсказка в ответе
409 SNAPSHOT_REQUIRED(deploy с external URL) — предлагает загрузить черезPOST /sourcesили передатьX-Skip-Source-Snapshot. - Подсказка в ответе
409 SNAPSHOT_REQUIRED(publish без свежего снапшота) — направляет в циклpublish → save_sources → publish(актуально если источник — external URL и auto-save был пропущен). - Блок
capabilities.apps.sourceStorageв ответеGET /v1/me— программная проверка состояния.
#Смотрите также
- Ключи и авторизация — типы ключей и заголовки.
- Коды ошибок — общий справочник ошибок API.
- MCP для AI — список MCP-инструментов, включая
save_sourcesиload_sources. - Деплой приложения — параметр
source.versionIdдля деплоя из снапшота.