#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> } } — существующая версия линкуется к deployId
  • POST /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). Раньше personal vibe_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` всё ещё нужен явно

Только три сценария:

  1. Tag a version — пометить manual / published для retention forever
  2. Save without deploy — сохранить WIP для handoff
  3. Rollback prep — снимок baseline перед рискованным изменением

#Когда вызывать

Типовой сценарий для AI-агента (с 2026-05-23 явный вызов POST /sources перед деплоем не требуется — платформа сохраняет автоматически):

  1. Изменил код → POST /v1/infra/servers/:id/deploy — исходники сохраняются автоматически.
  2. POST /v1/apps/:id/publish — публикует приложение в каталоге Битрикс24. Тег published добавляется к снапшоту автоматически.
  3. Повторил цикл при следующем изменении.

Сценарий передачи проекта новому разработчику или новой AI-сессии:

  1. GET /v1/apps/:id/sources — список доступных версий.
  2. GET /v1/apps/:id/sources/:versionId/download — подписанная ссылка на архив.
  3. Скачать архив, распаковать и продолжить работу.

Для MCP-клиентов: инструмент save_sources пакует файловое дерево в tar.gz на стороне клиента и отправляет одним запросом. Инструмент load_sources загружает последнюю версию и распаковывает обратно в дерево файлов. Прямой вызов HTTP-эндпоинта доступен для клиентов, которые самостоятельно упаковывают архив.

#Гарантия наличия снапшота перед публикацией

POST /v1/apps/:id/publish проверяет наличие снапшота не старше 10 минут. Если снапшота нет или он устарел — возвращается 409 SNAPSHOT_REQUIRED с подсказкой, какой вызов нужно сделать перед повторной публикацией.

Проверка работает только если в портале включено сохранение исходников (по умолчанию — включено; владелец портала может отключить, см. раздел «Отключение для портала»).

Пример отказа:

JSON
{
  "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):

  1. Последние 5 версий хранятся всегда, независимо от тегов.
  2. Дневная сетка за 14 дней — одна версия на каждый календарный день (UTC).
  3. Недельная сетка за 4 недели — одна версия на каждые 7 дней.
  4. Теги 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:

JSON
{
  "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 — снапшот не создан:

JSON
{
  "success": true,
  "data": {
    "skipped": true,
    "reason": "DISABLED_GLOBALLY"
  }
}

reason"DISABLED_GLOBALLY" (платформа не включила функцию) или "DISABLED_FOR_PORTAL" (владелец портала отключил для своего портала). При любом из вариантов POST /v1/apps/:id/publish не проверяет наличие снапшота.

#Примеры

#curl — tar.gz

Terminal
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

Terminal
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

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:

JSON
{
  "success": true,
  "data": {
    "versionId": "v3",
    "tags": ["manual"],
    "note": "Финальная версия перед релизом"
  }
}

#Примеры

#curl — добавить тег `manual`

Terminal
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 — очистить комментарий

Terminal
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

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:

JSON
{
  "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

Terminal
curl -H "X-Api-Key: YOUR_APP_KEY" \
  https://vibecode.bitrix24.tech/v1/apps/<APP_ID>/sources

#JavaScript

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:

JSON
{
  "success": true,
  "data": {
    "url": "https://<storage-endpoint>/...",
    "expiresAt": "2026-05-21T10:45:30.000Z"
  }
}

#Примеры

#curl

Terminal
# Получить ссылку
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

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:

JSON
{
  "success": true,
  "data": {
    "versionId": "v3",
    "tags": ["manual"]
  }
}

#Примеры

#curl

Terminal
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

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:

JSON
{
  "success": true,
  "data": { "versionId": "v3" }
}

#Примеры

#curl

Terminal
curl -X DELETE https://vibecode.bitrix24.tech/v1/apps/<APP_ID>/sources/v3 \
  -H "X-Api-Key: YOUR_APP_KEY"

#JavaScript

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:

JSON
{
  "success": true,
  "data": {
    "deletedVersions": ["v2", "v1"]
  }
}

#Примеры

#curl

Terminal
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

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, без отдельной загрузки файла.

JSON
{
  "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:

При включённом сохранении:

JSON
{
  "capabilities": {
    "apps": {
      "sourceStorage": {
        "enabled": true,
        "freshnessWindowMinutes": 10,
        "requiredBeforeDeploy": true,
        "limits": {
          "maxBlobBytes": 209715200
        }
      }
    }
  }
}

При отключённом сохранении:

JSON
{
  "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 — программная проверка состояния.

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