Для 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 — файл загружен, доступен для чтения
│
▼ (DELETE)
deletedAt — мягкое удаление: чтение → 410; данные хранятся ещё 30 суток
│
▼ (+30 суток, планировщик)
удалён — данные стёрты из хранилища
Прямая загрузка (путь A) создаёт объект сразу в состоянии COMPLETED. Пути B и C создают объект в состоянии PENDING — до вызова /complete.
Объекты в состоянии PENDING без завершения удаляются фоновым планировщиком через 24 часа.
Пути загрузки
| Путь | Размер файла | Шагов | Когда применять |
|---|---|---|---|
| A — прямая загрузка | до 10 МБ | 1 | Серверный код, небольшие файлы |
| B — предподписанный PUT | до 5 ГБ | 3 | Временно отключён: оба шага отвечают 503 STORAGE_PRESIGNED_UPLOAD_DISABLED. Для загрузки из браузера используйте путь C |
| C — составная загрузка | до 5 ТБ | 3 + N частей | Крупные файлы, параллельная загрузка |
Для прямой загрузки по пути A с ключом, привязанным к приложению, конкурентные запросы к одному физическому адресу выполняются по очереди. Запрос ждёт свою очередь не более 60 секунд; если безопасно получить право записи не удалось, сервер возвращает 409 STORAGE_KEY_CONFLICT до записи в объектное хранилище. Повторите весь запрос. Эта гарантия не распространяется на личные ключи без привязки к приложению (appId = null) и пути B/C.
Подробное описание каждого пути — Загрузка.
Видимость и доступ
Каждый объект имеет атрибут 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/. Личный ключ видит снимки серверов своего владельца, ключ приложения — только снимки этого приложения. Убирают их операциями с исходниками: 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 часа |
| Лимит запросов к публичному URL | 60 запросов/мин с одного IP |
Тарификация
Хранилище работает по схеме оплаты по факту использования. Плата берётся за три ресурса.
| Ресурс | Единица | Поле в 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):
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"
Получить ссылку для скачивания:
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 минут>
Полный пример
Полный цикл: загрузка → список → получение ссылки → удаление.
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`, {
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)
Типовые сценарии
Большой файл составной загрузкой
Загрузка крупного файла по частям с отменой при ошибке (чтобы не оплачивать незавершённые части):
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
}
Массовая чистка по префиксу
Удаление всех файлов пользователя при его отключении: постраничный обход по префиксу и удаление каждого объекта.
const KEY = 'YOUR_API_KEY'
const BASE = 'https://vibecode.bitrix24.tech/v1'
async function deleteByPrefix(prefix) {
let cursor = null
let removed = 0
do {
const url = new URL(`${BASE}/storage/objects`)
url.searchParams.set('prefix', prefix)
url.searchParams.set('limit', '500')
if (cursor) 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++
}
cursor = next
} while (cursor)
return removed
}
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 |
Список объектов с фильтрацией по префиксу и курсорной пагинацией |
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_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 | Объект под этим ключом удалён и держит имя до физической уборки (30 дней). Восстановление пока не поддерживается — используйте другой ключ |
STORAGE_KEY_OWNED_ELSEWHERE |
409 | Логическое имя занято другим объектом приложения: общие и персональные файлы хранятся по разным адресам |
STORAGE_KEY_EXISTS |
409 | Объект уже существует. Заменить содержимое через предподписанную ссылку нельзя — используйте прямую заливку (путь A) |
STORAGE_KEY_CONFLICT |
409 | Конфликт состояния объекта либо конкурентная прямая загрузка (путь A) с ключом приложения не получила право записи в пределах 60 секунд или лимита конкурентности. До подтверждения объектное хранилище не затрагивается; повторите весь запрос. Для личного ключа (appId = null) и путей B/C сериализация не обещана |
STORAGE_VISIBILITY_MISMATCH |
400 | Замена содержимого не может изменить видимость объекта. Не передавайте поле visibility либо передайте текущее значение |
STORAGE_OBJECT_DELETED |
410 | Объект помечен как удалённый |
BILLING_INSUFFICIENT |
402 | Нулевой баланс портала — запись приостановлена на 24 часа (чтение работает) |
STORAGE_RATE_LIMIT_EXCEEDED |
429 | Превышен лимит 60 запросов/мин к публичному URL с одного IP |
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 — Коды ошибок.