#Хранилище исходного кода

Хранилище исходного кода автоматически сохраняет снимки кода вашего приложения при каждом деплое. Если над приложением начинает работать другой человек или новая AI-сессия, последнюю версию можно скачать и продолжить с того же места — код не потеряется.

#Зачем это нужно

Когда вы дорабатываете приложение, важно не потерять рабочую версию кода. Хранилище делает это автоматически: при каждом успешном деплое платформа сохраняет снимок исходников. Если позже над приложением начнёт работать другой человек или новая AI-сессия — он скачает последнюю версию и продолжит с того же места, без ручной пересылки архивов.

Снимок привязан к приложению, а не к конкретному разработчику: код остаётся у приложения, даже если меняется команда.

#Чем это отличается от Git и GitHub

Это не система контроля версий и не замена Git. Хранилище решает более узкую задачу — страховка и передача проекта.

Хранилище исходного кода Git / GitHub
Что хранит целые снимки архива кода историю изменений построчно
Когда сохраняет автоматически при каждом деплое вручную, по команде разработчика
Нужны ли навыки Git нет да
Ветки, слияния, сравнение версий нет да

Нужна командная разработка с ветками и историей — используйте Git. Нужно просто не терять рабочий код и уметь вернуться к прошлой версии — достаточно хранилища.

#Как это работает

  1. Вы дорабатываете приложение и запускаете деплой.
  2. Платформа сама сохраняет снимок исходников — очередную версию: v1, v2, v3, …
  3. В любой момент смотрите список версий и скачиваете нужную.
  4. Перед публикацией приложения платформа проверяет, что свежий снимок есть.
правка кода  →  деплой  →  снимок vN сохранён автоматически
                                      ↓
        список версий  →  скачать любую  →  продолжить работу

#Основные понятия

  • Снимок (снапшот) — заархивированная копия исходного кода приложения на момент сохранения.
  • Версия vN — порядковый номер снимка (v1, v2, …). Самый свежий считается текущим.
  • Тег — метка manual или published: помечает версию как важную, чтобы её не удалила автоматическая очистка.
  • Дедупликация — если код не изменился, новый снимок не создаётся, возвращается уже существующая версия.

#Доступ и эндпоинты

Базовый URL: https://vibecode.bitrix24.tech/v1
Авторизация: заголовок X-Api-Key. Управлять снапшотами могут ключ авторизации приложения (vibe_app_*), личный ключ автора приложения (vibe_api_*) или администратор портала.
Допустимые форматы архива: 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 платформа автоматически сохраняет байты исходников в хранилище при каждом успешном развёртывании. Это касается:

  • POST /v1/infra/servers/:id/deploy { source: { content: <base64> } } — встроенные байты сохраняются как новая версия
  • POST /v1/infra/servers/:id/deploy { source: { url: <signed URL из хранилища> } } — существующая версия привязывается к идентификатору развёртывания
  • POST /v1/infra/servers/:id/deploy { source: { versionId: 'vN' } } — то же

Исключение: развёртывание с внешним URL (не из хранилища Вайбкод) возвращает 409 SNAPSHOT_REQUIRED. Чтобы обойти — либо сначала загрузите архив через POST /v1/apps/:id/sources и разверните через {source: {versionId: 'vN'}}, либо передайте заголовок X-Skip-Source-Snapshot: <reason> для явного отказа.

#Когда снапшот не создаётся

Если сохранение исходников отключено на портале или источник — внешний URL (не хранилище Вайбкод), снапшот не создаётся, а развёртывание завершается штатно. Чтобы сохранить версию явно, загрузите архив через POST /v1/apps/:id/sources.

#Когда `POST /sources` всё ещё нужен явно

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

  1. Пометить версию — поставить тег manual или published, чтобы версия хранилась бессрочно.
  2. Сохранить без развёртывания — зафиксировать промежуточный результат для передачи другому разработчику.
  3. Подготовка к откату — снимок исходного состояния перед рискованным изменением.

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

Типовой сценарий для 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

Принимает сырые байты архива исходного кода в теле запроса. Формат определяется заголовком Content-Type. Метаданные (теги, комментарий, имя файла) передаются через дополнительные заголовки.

Только бинарная загрузка (контракт v2). Эндпоинт принимает сырые байты архива в теле запроса (--data-binary). multipart/form-data больше не поддерживается — такой запрос возвращает 400 INVALID_CONTENT_TYPE. Ранние версии клиентов, которые слали multipart/form-data с полями формы (-F "tag=manual"), нужно обновить: тело — это сами байты архива, а теги, комментарий и имя файла передаются заголовками X-Tags / X-Note / X-Filename (не полями формы и не query-параметрами). MCP-клиенты используют инструмент save_sources, который сам упаковывает и отправляет архив в правильном формате.

#Параметры пути

Параметр Тип Описание
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-сессии. Группирует снапшоты в манифесте по сессии.

#Тело запроса

Сырые байты архива, не multipart/form-data. Максимальный размер — 200 МБ.

#Ответ при успешном сохранении

HTTP 201:

JSON
{
  "success": true,
  "data": {
    "versionId": "v3",
    "id": "b7c1e2a4-9f5d-4c3a-8e21-0a1b2c3d4e5f",
    "filename": "2026-05-21T10-15-30-000Z-v3.tar.gz",
    "contentType": "application/gzip",
    "sha256": "a3f5d8b2c1e9f4...",
    "size": 184320,
    "timestamp": "2026-05-21T10:15:30.000Z",
    "tags": [],
    "note": null,
    "deduplicated": false
  }
}

#Поля ответа

Поле Тип Описание
data.versionId string Идентификатор версии вида v<N>.
data.id string Внутренний идентификатор записи снапшота (UUID).
data.filename string Имя файла архива в хранилище.
data.contentType string Тип архива: application/gzip, application/zip, application/x-tar или application/octet-stream.
data.sha256 string SHA-256 содержимого архива. Используется для дедупликации.
data.size number Размер архива в байтах.
data.timestamp string Время сохранения (ISO 8601, UTC).
data.tags string[] Активные теги версии: manual, published.
data.note string | null Комментарий из заголовка X-Note или null.
data.deduplicated boolean true, если архив с тем же содержимым уже был сохранён — возвращена существующая версия.

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

Повторное сохранение архива с тем же содержимым даёт тот же 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 не входит в список допустимых форматов архива. Формат multipart/form-data не принимается — отправляйте сырые байты архива.
400 INVALID_BLOB Тело запроса не является бинарным буфером (например, передан текст).
400 INVALID_FILENAME Заголовок X-Filename содержит символы вне набора a-zA-Z0-9._-.
400 INVALID_TAGS Тег из заголовка X-Tags содержит недопустимые символы (разрешены a-zA-Z0-9_-).
403 NOT_AUTHORIZED Только автор приложения, OAuth-ключ приложения или администратор портала могут управлять снапшотами.
404 APP_NOT_FOUND Приложение не существует, удалено или принадлежит другому порталу.
413 Размер архива превышает лимит в 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 Только автор приложения, OAuth-ключ приложения или администратор портала могут управлять снапшотами.
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 Только автор приложения, OAuth-ключ приложения или администратор портала могут управлять снапшотами.
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 Только автор приложения, OAuth-ключ приложения или администратор портала могут управлять снапшотами.
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 Только автор приложения, OAuth-ключ приложения или администратор портала могут управлять снапшотами.
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 — теги без защитных меток, которые стоит сохранить при снятии защиты.

#Параметры пути

Параметр Тип Описание
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 Только автор приложения, OAuth-ключ приложения или администратор портала могут управлять снапшотами.
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 Только автор приложения, OAuth-ключ приложения или администратор портала могут управлять снапшотами.
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 у снапшота обновляется автоматически.

Ограничение: серверу должен соответствовать ключ авторизации приложения (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,
        "requiredBeforeDeploy": false,
        "automaticOnDeploy": true,
        "freshnessWindowMinutes": 10,
        "limits": {
          "maxBlobBytes": 209715200
        }
      }
    }
  }
}

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

JSON
{
  "capabilities": {
    "apps": {
      "sourceStorage": {
        "enabled": false,
        "requiredBeforeDeploy": false,
        "automaticOnDeploy": false,
        "disabledBy": "platform",
        "readOperations": "available",
        "reactivation": {
          "scope": "platform",
          "permission": "platform admin"
        }
      }
    }
  }
}

Поле disabledBy"platform" (функция не включена на уровне платформы) или "portal" (владелец портала отключил). Поле readOperations: "available" означает, что список версий и скачивание остаются доступными даже при отключённом сохранении. Блок reactivation указывает, на каком уровне (платформа или портал) включается функция и какие права для этого нужны.

#Отключение для портала

Владелец портала может отключить сохранение исходников через раздел администрирования (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 перед развёртыванием не требуется — платформа сохраняет байты автоматически. save_sources остаётся нужным только для трёх явных сценариев (раздел «Сохранение исходников» выше).

Каналы, через которые модель узнаёт о сохранении исходников:

  • Описание MCP-инструментов save_sources и load_sources — видно при первом обращении.
  • Подсказка в ответе 409 SNAPSHOT_REQUIRED (развёртывание с внешним URL) — предлагает загрузить через POST /sources или передать X-Skip-Source-Snapshot.
  • Подсказка в ответе 409 SNAPSHOT_REQUIRED (публикация без свежего снапшота) — направляет в цикл publish → save_sources → publish (актуально, если источник — внешний URL и автосохранение было пропущено).
  • Блок capabilities.apps.sourceStorage в ответе GET /v1/me — программная проверка состояния.

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