Для AI-агентов: markdown этой страницы — /docs-content/storage.md индекс документации — /llms.txt

Хранилище

Хранилище файлов для Битрикс24-приложений: загружайте, управляйте видимостью и скачивайте по временным ссылкам.

Скоуп: vibe:storage | Базовый URL: https://vibecode.bitrix24.tech/v1 | Авторизация: API-ключ (X-Api-Key)

Авторизация · Жизненный цикл · Пути загрузки · Видимость · Ключ объекта · Лимиты · Тарификация · Быстрый старт · Полный пример · Эндпоинты · Ошибки

Когда использовать

Подходит для:

  • Аватаров пользователей и логотипов приложения
  • Вложений в формах: фотографии, сканы, чеки
  • Экспортов и сгенерированных отчётов (CSV, PDF)
  • Видеовложений в CRM-сделках
  • Резервных копий конфигурации приложения
  • Промежуточных файлов в цепочке обработки данных

Не подходит для:

  • Публикации файлов в Диск Битрикс24 портала — для этого предназначен раздел Диск. Хранилище изолировано по приложению и не входит в дисковое дерево портала
  • Хранения секретов и ключей — используйте переменные окружения сервера
  • Журналирования и аудита приложения
  • Сценариев без привязки к приложению — хранилище всегда изолировано по ключу

Разделы документации

Раздел Описание
Загрузка Три пути загрузки: прямая форма (≤10 МБ), предподписанный PUT (временно отключён), составные части (≤5 ТБ)
Объекты Список, получение, метаданные, удаление объектов и публичный доступ

Авторизация

Все запросы к /v1/storage/* требуют API-ключ в заголовке X-Api-Key со скоупом vibe:storage.

В форме создания ключа скоуп vibe:storage отмечен заранее. Если у ключа его нет — добавьте скоуп в разделе API-ключи личного кабинета.

Отсутствие скоупа возвращает 403 STORAGE_SCOPE_REQUIRED на любом вызове /v1/storage/*.

Типы ключей

Тип ключа Доступ Область хранилища
vibe_api_* — личный API-ключ да пространство имён владельца ключа на портале
vibe_app_* — ключ приложения да пространство имён приложения, к которому привязан ключ

Менеджмент-ключи (vibe_live_*) к /v1/storage/* не подходят — у них нет привязки к порталу. Любой вызов вернёт 403 STORAGE_REQUIRES_PORTAL_BINDING.

Изоляция. Объекты изолированы по владельцу ключа: ключ одного портала не получит доступ к объектам другого портала — такой запрос вернёт 404 STORAGE_OBJECT_NOT_FOUND, а не 403, чтобы не раскрывать существование чужих ресурсов.

Жизненный цикл объекта

PENDING      — создан, ожидает загрузки файла (первичная загрузка путями B и C)
    │
    ▼ (путь A → сразу COMPLETED)
COMPLETED    — файл загружен, доступен для чтения; app-bound путь C может готовить замену в той же строке
    │
    ▼ (DELETE)
deletedAt    — мягкое удаление: чтение → 410; данные хранятся ещё 30 суток
    │
    ├──► (путь A либо личный путь C после DELETE, новые байты) → COMPLETED с тем же id и visibility
    │
    ▼ (+30 суток, планировщик)
удалён       — данные стёрты из хранилища

Прямая загрузка (путь A) создаёт объект сразу в состоянии COMPLETED. Первичная загрузка путями B и C создаёт строку PENDING до вызова /complete. При замене app-bound объекта путь C сохраняет строку COMPLETED: пока /multipart/complete не опубликовал новые байты, чтение отдаёт старое содержимое. Успешный ответ complete подтверждает новую версию; 502 STORAGE_BUCKET_ERROR может означать, что публикация уже состоялась, но её результат не подтверждён. Подтверждённая отмена до публикации сохраняет старую версию. Автоматическая очистка освобождает подтверждённо незавершённые части или завершает зафиксированную публикацию новой версии, а неподтверждённый исход удерживает сессию для сверки.

Подходящий мягко удалённый объект можно переиспользовать для новых байтов: строка должна иметь статус COMPLETED, не содержать multipart-сессии и принадлежать той же owner tuple. Путь A поддерживает app-bound и личные ключи до 10 МБ. Путь C поддерживает только личную строку после явного DELETE и подходит для крупного файла. Оба пути сохраняют id, createdAt, key, физический адрес и visibility, а deletedAt очищают только после успешной загрузки. Это не восстановление прежнего содержимого. Для пути C операция неатомарна: объект недоступен между DELETE и успешным complete. Путь B отключён и отвечает 503 STORAGE_PRESIGNED_UPLOAD_DISABLED.

Через 24 часа фоновый планировщик начинает очистку незавершённых объектов PENDING. Для multipart-сессии объект и его части удаляются только после подтверждённой провайдером отмены; при неподтверждённой отмене сессия удерживается, а освобождение требует повторной попытки отмены либо участия оператора.

Пути загрузки

Путь Размер файла Шагов Когда применять
A — прямая загрузка до 10 МБ 1 Новые объекты, замена живого содержимого и оживление подходящего тумбстоуна с теми же id и visibility
B — предподписанный PUT до 5 ГБ 3 Временно отключён: оба шага отвечают 503 STORAGE_PRESIGNED_UPLOAD_DISABLED без URL. Удалённые объекты не оживляет
C — составная загрузка до 5 ТБ 3 + N частей Крупные новые файлы, замена живого app-bound объекта и повторная загрузка личного ключа после явного DELETE. Последняя операция неатомарна: объект недоступен до complete

Для прямых загрузок путём A и всех загрузок путём C конкурентные запросы к одному физическому адресу выполняются по очереди. Запрос ждёт свою очередь не более 60 секунд; если безопасно получить право записи не удалось, сервер возвращает 409 STORAGE_KEY_CONFLICT. Для пути A такой конфликт возникает до записи в объектное хранилище. Повторите весь запрос. На пути C ответ 409 от complete или abort возможен после того, как части уже заняли место в хранилище; незавершённые части освобождаются подтверждённой отменой, а зажурналированный ответ complete означает, что провайдер уже собрал объект. Путь A распространяется и на личный ключ (appId = null); путь C заменяет живой объект только для app-bound ключа, а личный принимает после явного DELETE. При этом путь B исключён полностью.

Подробное описание каждого пути — Загрузка.

Видимость и доступ

Каждый объект имеет атрибут visibility:

  • PRIVATE (по умолчанию) — скачивание через авторизованный запрос GET /v1/storage/objects/:key, который возвращает перенаправление 302 на предподписанный URL, действующий 10 минут.
  • PUBLIC — после завершения загрузки (COMPLETED) дополнительно доступен через постоянный URL вида https://vibecode.bitrix24.tech/v1/public-storage/:portalId/:objectId без авторизации. Пока объект находится в PENDING, публичный URL возвращает 404 STORAGE_OBJECT_NOT_FOUND.

Для PUBLIC-объектов заблокирована отдача файлов с типами содержимого text/html, application/javascript, application/x-javascript, image/svg+xml. Попытка загрузить файл с таким типом при visibility=PUBLIC возвращает 415 STORAGE_FORBIDDEN_CONTENT_TYPE.

Анонимный доступ через public-storage включается на стороне портала и по умолчанию выключен. Пока он выключен, запросы к публичному URL возвращают 503 STORAGE_PUBLIC_GET_DISABLED_FOR_PORTAL.

Постоянный источник для внешних инструментов. Предподписанный URL из GET /v1/storage/objects/:key действует 10 минут и запрашивается с заголовком X-Api-Key, поэтому как постоянный источник для инструментов, которые не отправляют заголовки авторизации — импорт данных в электронные таблицы, планировщики отчётов, внешние дашборды — он не подходит. Для такого сценария завершите загрузку PUBLIC-объекта и используйте его постоянный URL https://vibecode.bitrix24.tech/v1/public-storage/:portalId/:objectId — он отдаётся без авторизации, пока анонимный доступ включён на стороне портала. Для PUBLIC-отчётов подходят типы содержимого text/csv, application/json и подобные. Типы text/html, application/javascript и image/svg+xml заблокированы.

Ключ объекта

Ключ объекта — логическое имя файла. Оно уникально в пределах ВЛАДЕЛЬЦА: личное пространство владельца ключа, общее пространство приложения либо персональное пространство сотрудника портала. Поэтому два сотрудника одного приложения занимают одно и то же имя независимо друг от друга и получают два разных объекта, а общий файл приложения и персональный файл сотрудника делят одно пространство имён и на одно имя не встают. Отсюда следует, что имя НЕ адресует объект однозначно: для точного обращения используйте идентификатор объекта. Правила формата:

  • Длина: 1–1024 символа.
  • Допустимые символы: a-z, A-Z, 0-9, ., _, /, -.
  • Не может начинаться с / или ..
  • Не может содержать последовательность ...
  • Символ / — разделитель пути, используется для группировки по префиксу.

В параметрах пути URL слеши в ключе необходимо кодировать: encodeURIComponent('users/42/report.csv') возвращает 'users%2F42%2Freport.csv'.

Снимки исходного кода. Платформа сохраняет исходники приложения в это же хранилище — они видны в GET /v1/storage/objects по префиксу source/. Личный ключ видит все объекты своего владельца на портале. Ключ приложения без пользовательского токена перечисляет только общие объекты приложения, а с пользовательским токеном — только объекты соответствующего сотрудника Битрикс24. Убирают снимки операциями с исходниками: DELETE /v1/infra/servers/:id/sources/:versionId для одной версии и POST /v1/infra/servers/:id/sources/cleanup для массовой очистки, у приложений — те же операции с префиксом /v1/apps/:id/sources. Что лежит под этим префиксом и почему один и тот же ключ встречается в выдаче несколько раз — Хранилище исходного кода.

Лимиты

Параметр Значение
Максимальный размер файла (путь A) 10 МБ
Максимальный размер файла (путь B, временно отключён) 5 ГБ
Максимальный размер файла (путь C) 5 ТБ
Длина ключа объекта 1–1024 символа
Срок действия предподписанного URL для скачивания 10 минут
Период мягкого удаления 30 суток
Объекты в состоянии PENDING через 24 часа планировщик начинает очистку; multipart удаляется только после подтверждённой отмены
Лимит запросов к публичному URL 60 запросов/мин с одного IP, суммарно по платформе. Точное значение — в заголовке x-ratelimit-limit (потолок делится на реплики)

Тарификация

Хранилище работает по схеме оплаты по факту использования. Плата берётся за три ресурса.

Ресурс Единица Поле в GET /v1/me
Хранение Ꝟ за ГБ в месяц costRateGbMonthVibes
Исходящий трафик Ꝟ за ГБ costRateGbEgressVibes
Операции записи Ꝟ за 1 000 операций costRateOps1kVibes

Ставки задаёт платформа, и они различаются от инстанса к инстансу. Актуальные значения возвращает блок storage ответа GET /v1/me.

При нулевом балансе запросы на запись блокируются на 24 часа. Чтение объектов продолжает работать. Пополнение баланса разблокирует запись немедленно.

Текущее использование и прогноз расходов — в блоке storage ответа GET /v1/me. Владелец портала также видит сводный расход по приложениям и может выгрузить отчёт на странице Хранилище в админ-панели портала.

Использование в AI-агентах

AI-моделям и агентам хранилище доступно без чтения этой страницы:

  • Блок storage в ответе GET /v1/me — состояние хранилища, ставки, пути загрузки и список эндпоинтов.
  • Инструменты vibe_storage_* MCP-сервера (загрузка, предподписанные ссылки, список, удаление, контроль расхода) — см. MCP для AI.

Быстрый старт

Загрузить файл (путь A):

Terminal
curl -X POST https://vibecode.bitrix24.tech/v1/storage/objects/upload \
  -H "X-Api-Key: YOUR_API_KEY" \
  -F "key=reports/march.csv" \
  -F "visibility=PRIVATE" \
  -F "file=@march.csv;type=text/csv"

Получить ссылку для скачивания:

Terminal
curl -I \
  -H "X-Api-Key: YOUR_API_KEY" \
  "https://vibecode.bitrix24.tech/v1/storage/objects/reports%2Fmarch.csv"
# HTTP/1.1 302 Found
# Location: <временная ссылка на объект, действует 10 минут>

Полный пример

Полный цикл: загрузка → список → получение ссылки → удаление.

javascript
const KEY = 'YOUR_API_KEY'
const BASE = 'https://vibecode.bitrix24.tech/v1'

const form = new FormData()
form.append('key', 'users/42/report.csv')
form.append('visibility', 'PRIVATE')
form.append('file', new Blob(['id,name\n1,Alice\n2,Bob'], { type: 'text/csv' }), 'report.csv')

const uploadRes = await fetch(`${BASE}/storage/objects/upload`, {
  method: 'POST',
  headers: { 'X-Api-Key': KEY },
  body: form,
})
const { object } = await uploadRes.json()
console.log('Загружен:', object.key, '| uploadStatus:', object.uploadStatus)

const listRes = await fetch(`${BASE}/storage/objects?prefix=users/42&withTotal=true`, {
  headers: { 'X-Api-Key': KEY },
})
const { data, total } = await listRes.json()
console.log('Объектов:', total, '| первый:', data[0]?.key)

const getRes = await fetch(`${BASE}/storage/objects/${encodeURIComponent(object.key)}`, {
  headers: { 'X-Api-Key': KEY },
  redirect: 'manual',
})
const downloadUrl = getRes.headers.get('location')
console.log('Ссылка (10 мин):', downloadUrl)

const delRes = await fetch(`${BASE}/storage/objects/${encodeURIComponent(object.key)}`, {
  method: 'DELETE',
  headers: { 'X-Api-Key': KEY },
})
const { object: deleted } = await delRes.json()
console.log('Удалён:', deleted.key, '| deletedAt:', deleted.deletedAt)

Типовые сценарии

Большой файл составной загрузкой

Загрузка крупного файла по частям с отменой при ошибке (чтобы не оплачивать незавершённые части):

javascript
const KEY = 'YOUR_API_KEY'
const BASE = 'https://vibecode.bitrix24.tech/v1'
const PART_SIZE = 8 * 1024 * 1024 // 8 МБ

// file — File или Blob, например из <input type="file">
const init = await fetch(`${BASE}/storage/objects/multipart/create`, {
  method: 'POST',
  headers: { 'X-Api-Key': KEY, 'Content-Type': 'application/json' },
  body: JSON.stringify({
    key: 'videos/demo.mp4',
    contentType: 'video/mp4',
    totalSize: file.size,
    partSize: PART_SIZE,
    visibility: 'PRIVATE',
  }),
})
const { objectId, parts } = await init.json()

try {
  const uploaded = []
  for (const part of parts) {
    const chunk = file.slice((part.partNumber - 1) * PART_SIZE, part.partNumber * PART_SIZE)
    const res = await fetch(part.uploadUrl, { method: 'PUT', body: chunk })
    uploaded.push({ partNumber: part.partNumber, etag: res.headers.get('etag') })
  }
  const done = await fetch(`${BASE}/storage/objects/multipart/complete`, {
    method: 'POST',
    headers: { 'X-Api-Key': KEY, 'Content-Type': 'application/json' },
    body: JSON.stringify({ objectId, parts: uploaded }),
  })
  const { object } = await done.json()
  console.log('Загружен:', object.key, object.sizeBytes)
} catch (err) {
  await fetch(`${BASE}/storage/objects/multipart/abort`, {
    method: 'POST',
    headers: { 'X-Api-Key': KEY, 'Content-Type': 'application/json' },
    body: JSON.stringify({ objectId }),
  })
  throw err
}

Массовая чистка по префиксу

Удаление всех файлов пользователя при его отключении: постраничный обход по префиксу и удаление каждого объекта.

javascript
const KEY = 'YOUR_API_KEY'
const BASE = 'https://vibecode.bitrix24.tech/v1'

async function deleteByPrefix(prefix) {
  let cursor = null
  let removed = 0
  while (true) {
    const url = new URL(`${BASE}/storage/objects`)
    url.searchParams.set('prefix', prefix)
    url.searchParams.set('limit', '500')
    if (cursor !== null) url.searchParams.set('cursor', cursor)

    const page = await fetch(url, { headers: { 'X-Api-Key': KEY } })
    const { data, cursor: next } = await page.json()

    for (const obj of data) {
      await fetch(`${BASE}/storage/objects/${encodeURIComponent(obj.key)}`, {
        method: 'DELETE',
        headers: { 'X-Api-Key': KEY },
      })
      removed++
    }
    if (next === null) return removed
    cursor = next
  }
}

const count = await deleteByPrefix('users/42/')
console.log('Удалено объектов:', count)

Справочник эндпоинтов

Загрузка

Метод Путь Описание
POST /v1/storage/objects/upload Прямая загрузка файла до 10 МБ одним multipart/form-data-запросом
POST /v1/storage/objects Создать предподписанный URL для загрузки файла до 5 ГБ напрямую в хранилище. Временно отключён
POST /v1/storage/objects/complete Подтвердить завершение загрузки по предподписанному URL. Временно отключён
POST /v1/storage/objects/multipart/create Инициировать составную загрузку: получить идентификатор сессии и URL для частей
POST /v1/storage/objects/multipart/complete Собрать объект из загруженных частей по их partNumber и etag
POST /v1/storage/objects/multipart/abort Отменить составную загрузку и удалить незавершённые части

Объекты

Метод Путь Описание
GET /v1/storage/objects Список объектов с фильтрацией по префиксу, непрозрачным курсором и точным подсчётом только по withTotal=true
GET /v1/storage/objects/:key Скачать объект (перенаправление 302 → предподписанный URL)
HEAD /v1/storage/objects/:key Метаданные объекта без тела ответа
DELETE /v1/storage/objects/:key Мягкое удаление объекта

Публичный доступ

Метод Путь Описание
GET /v1/public-storage/:portalId/:objectId Скачать завершённый PUBLIC-объект без API-ключа
HEAD /v1/public-storage/:portalId/:objectId Метаданные завершённого PUBLIC-объекта без API-ключа

Коды ошибок

Код HTTP Когда возникает
STORAGE_SCOPE_REQUIRED 403 API-ключ не имеет скоупа vibe:storage
STORAGE_NO_AUTH_CONTEXT 401 Запрос без пригодных учётных данных приложения
STORAGE_KEY_REQUIRED 400 Поле key не передано или пустое
STORAGE_INVALID_KEY 400 Ключ содержит недопустимые символы, начинается с / или ., содержит ..
STORAGE_INVALID_PATH 400 Ключ формирует инъекцию пути
STORAGE_CONTENT_TYPE_REQUIRED 400 Поле contentType не передано (пути B и C)
STORAGE_INVALID_VISIBILITY 400 visibility не равен PUBLIC или PRIVATE
STORAGE_INVALID_SIZE 400 sizeBytes не является неотрицательным целым числом
STORAGE_INVALID_TTL 400 ttlSeconds вне диапазона 60–86400
STORAGE_FILE_REQUIRED 400 Файловая часть в форме не передана (путь A)
STORAGE_MULTIPART_PARSE_FAILED 400 Ошибка разбора multipart/form-data
STORAGE_OBJECT_ID_REQUIRED 400 Параметр objectId не передан
STORAGE_INVALID_TOTAL_SIZE 400 totalSize не является положительным целым числом (путь C)
STORAGE_INVALID_PART_SIZE 400 partSize не является положительным целым числом (путь C)
STORAGE_PARTS_REQUIRED 400 Массив parts не передан или пуст
STORAGE_INVALID_PART 400 Элемент массива parts не соответствует формату { partNumber, etag }
STORAGE_INVALID_PARTS 400 Набор частей отклонён хранилищем при сборке составной загрузки
STORAGE_TOO_MANY_PARTS 400 Слишком много частей для составной загрузки. Увеличьте partSize
STORAGE_INVALID_LIMIT 400 limit не является положительным целым числом
STORAGE_INVALID_CURSOR 400 Новый непрозрачный курсор повреждён, изменён, слишком велик либо относится к другому владельцу, префиксу или порядку; старый CUID-курсор имеет неверный формат, слишком велик либо используется вместе с prefix
STORAGE_UPLOAD_TOO_LARGE 413 Размер файла превышает 10 МБ (путь A)
STORAGE_OBJECT_TOO_LARGE 413 totalSize превышает 5 ТБ (путь C)
STORAGE_FORBIDDEN_CONTENT_TYPE 415 Тип содержимого недопустим для PUBLIC-объектов
STORAGE_OBJECT_NOT_FOUND 404 Объект не найден или не принадлежит вызывающему приложению; публичный GET/HEAD также возвращает этот код для PENDING и PRIVATE
STORAGE_UPLOAD_NOT_PENDING 409 Объект уже загружен или находится в недопустимом состоянии
STORAGE_UPLOAD_PENDING 409 Загрузка объекта не завершена: при чтении — объект ещё не готов, при заливке — по ключу висит незавершённая бронь предподписанной ссылки
STORAGE_UPLOAD_NOT_FOUND_IN_BUCKET 409 PUT-запрос к предподписанному URL не был выполнен
STORAGE_MULTIPART_IN_PROGRESS 409 У объекта есть незавершённая составная загрузка. Вызовите /multipart/abort; заменить или удалить объект до этого нельзя
STORAGE_KEY_DELETED 409 Этот путь не может переиспользовать удалённый ключ. Путь A оживляет только объект COMPLETED без multipart-сессии и сохраняет id и visibility. Путь B сейчас отвечает 503 без URL; путь C принимает только подходящий личный COMPLETED-тумбстоун после явного DELETE, остальные состояния остаются 409
STORAGE_KEY_OWNED_ELSEWHERE 409 Имя занято объектом другого типа владения. Сотрудники приложения занимают имена независимо друг от друга, а общий файл приложения и персональный файл сотрудника делят одно пространство имён
STORAGE_KEY_EXISTS 409 Объект уже существует. Заменить содержимое через предподписанную ссылку нельзя; путь B временно отключён и отвечает 503 STORAGE_PRESIGNED_UPLOAD_DISABLED. Используйте путь A до 10 МБ; для большего app-bound файла используйте путь C. Для живого личного объекта сначала выполните DELETE, затем начните путь C. Это неатомарно: объект недоступен до успешного complete
STORAGE_KEY_CONFLICT 409 Конфликт состояния объекта либо конкурентная загрузка путём A или C не получила право записи в пределах 60 секунд или лимита конкурентности. Для пути A объектное хранилище не затрагивается до подтверждения; повторите весь запрос. На пути C ответ 409 от complete или abort возможен уже после загрузки частей: незавершённые части занимают место до подтверждённого abort, а зажурналированный complete означает, что провайдер уже собрал объект. Сериализация распространяется и на личный ключ (appId = null); путь B не входит
STORAGE_VISIBILITY_MISMATCH 400 Замена содержимого не может изменить видимость объекта. Не передавайте поле visibility либо передайте текущее значение
STORAGE_OBJECT_DELETED 410 Объект помечен как удалённый. Без подходящей последующей загрузки чтение и повторный DELETE остаются 410 до физической уборки
BILLING_INSUFFICIENT 402 Нулевой баланс портала — запись приостановлена на 24 часа (чтение работает)
STORAGE_QUOTA_EXCEEDED 507 Запись превышает потолок объёма хранилища портала. Потолок необязательный, включается платформой и применяется только к порталам, у которых нет и никогда не было коммерческого тарифа Битрикс24. Считается весь портал, а не отдельное приложение. Путь A отказывает, когда размер файла не помещается в остаток потолка, путь C — по заявленному totalSize на /multipart/create, до загрузки частей. Действующее значение потолка называет текст сообщения в ответе
STORAGE_RATE_LIMIT_EXCEEDED 429 Превышен лимит 60 запросов/мин к публичному URL с одного IP. Точное значение — в заголовке x-ratelimit-limit (потолок делится на реплики)
STORAGE_PUBLIC_GET_DISABLED_FOR_PORTAL 503 Публичный доступ отключён на уровне портала
STORAGE_FEATURE_DISABLED 503 Хранилище временно отключено
STORAGE_PRESIGNED_UPLOAD_DISABLED 503 Путь B временно отключён. Используйте прямую загрузку (путь A) или составную загрузку (путь C)
STORAGE_STS_UNAVAILABLE 503 Сервис выдачи временных учётных данных недоступен
STORAGE_BUCKET_ERROR 502 Ошибка при обращении к объектному хранилищу
STORAGE_OBJECT_MISSING_IN_BUCKET 502 Объект зафиксирован в базе, но отсутствует в хранилище
STORAGE_OBJECT_STREAM_FAILED 502 Ошибка при передаче данных объекта
STORAGE_OBJECT_HEAD_FAILED 502 Ошибка при получении метаданных объекта из хранилища

Общие коды ошибок API — Коды ошибок.

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