#Хранилище
Хранилище файлов для Битрикс24-приложений: загружайте, управляйте видимостью и скачивайте по временным ссылкам.
Скоуп: vibe:storage | Базовый URL: https://vibecode.bitrix24.tech/v1 | Авторизация: API-ключ (X-Api-Key)
Авторизация · Жизненный цикл · Пути загрузки · Видимость · Ключ объекта · Лимиты · Тарификация · Быстрый старт · Полный пример · Эндпоинты · Ошибки
#Когда использовать
Подходит для:
- Аватаров пользователей и логотипов приложения
- Вложений в формах: фотографии, сканы, чеки
- Экспортов и сгенерированных отчётов (CSV, PDF)
- Видеовложений в CRM-сделках
- Резервных копий конфигурации приложения
- Промежуточных файлов в цепочке обработки данных
Не подходит для:
- Публикации файлов в Диск Битрикс24 портала — для этого предназначен раздел Диск; хранилище изолировано по приложению и не входит в дисковое дерево портала
- Хранения секретов и ключей — используйте переменные окружения сервера
- Журналирования и аудита приложения
- Сценариев без привязки к приложению — хранилище всегда изолировано по ключу
#Разделы документации
| Раздел | Описание |
|---|---|
| Загрузка | Три пути загрузки: прямая форма (≤10 МБ), предподписанный PUT (≤5 ГБ), составные части (≤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 | Загрузка из браузера без промежуточного сервера |
| C — составная загрузка | до 5 ТБ | 3 + N частей | Крупные файлы, параллельная загрузка |
Подробное описание каждого пути — Загрузка.
#Видимость и доступ
Каждый объект имеет атрибут visibility:
PRIVATE(по умолчанию) — скачивание через авторизованный запросGET /v1/storage/objects/:key, который возвращает перенаправление 302 на предподписанный URL, действующий 10 минут.PUBLIC— дополнительно доступен через постоянный URL видаhttps://vibecode.bitrix24.tech/v1/public-storage/:portalId/:objectIdбез авторизации.
Для 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.
#Ключ объекта
Ключ объекта — уникальный идентификатор в пространстве имён, привязанном к вашему ключу (личное пространство владельца или пространство приложения). Правила:
- Длина: 1–1024 символа.
- Допустимые символы:
a-z,A-Z,0-9,.,_,/,-. - Не может начинаться с
/или.. - Не может содержать последовательность
... - Символ
/— разделитель пути, используется для группировки по префиксу.
В параметрах пути URL слеши в ключе необходимо кодировать: encodeURIComponent('users/42/report.csv') возвращает 'users%2F42%2Freport.csv'.
#Лимиты
| Параметр | Значение |
|---|---|
| Максимальный размер файла (путь A) | 10 МБ |
| Максимальный размер файла (путь B) | 5 ГБ |
| Максимальный размер файла (путь C) | 5 ТБ |
| Длина ключа объекта | 1–1024 символа |
| Срок действия предподписанного URL | 10 минут |
| Период мягкого удаления | 30 суток |
| Объекты в состоянии PENDING | удаляются через 24 часа |
| Лимит запросов к публичному URL | 60 запросов/мин с одного IP |
#Тарификация
Хранилище работает по схеме оплаты по факту использования.
| Ресурс | Ставка |
|---|---|
| Хранение | 1,96 Ꝟ/ГБ-месяц |
| Исходящий трафик | 1,20 Ꝟ/ГБ |
| Операции | 0,50 Ꝟ / 1 000 операций записи |
При нулевом балансе запросы на запись блокируются на 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 | Объект не найден или не принадлежит вызывающему приложению |
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_OBJECT_DELETED |
410 | Объект помечен как удалённый |
STORAGE_QUOTA_EXCEEDED |
507 | Превышена квота хранилища приложения |
BILLING_INSUFFICIENT |
402 | Нулевой баланс портала — запись приостановлена на 24 часа (чтение работает) |
STORAGE_RATE_LIMIT_EXCEEDED |
429 | Превышен лимит 60 запросов/мин к публичному URL с одного IP |
STORAGE_PUBLIC_GET_DISABLED_FOR_PORTAL |
503 | Публичный доступ отключён на уровне портала |
STORAGE_FEATURE_DISABLED |
503 | Хранилище временно отключено |
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 — Коды ошибок.