For AI agents: markdown of this page — /docs-content-en/storage.md documentation index — /llms.txt

Storage

File storage for Bitrix24 apps: upload, manage visibility, and download via temporary links.

Scope: vibe:storage | Base URL: https://vibecode.bitrix24.com/v1 | Authorization: API key (X-Api-Key)

Authorization · Lifecycle · Upload paths · Visibility · Object key · Limits · Pricing · Quick start · Full example · Endpoints · Errors

When to use

Suitable for:

  • User avatars and app logos
  • Form attachments: photos, scans, receipts
  • Exports and generated reports (CSV, PDF)
  • Video attachments in CRM deals
  • App configuration backups
  • Intermediate files in a data-processing pipeline

Not suitable for:

  • Publishing files to the Bitrix24 account Drive — use the Drive section for that; storage is isolated per app and is not part of the Bitrix24 account's drive tree
  • Storing secrets and keys — use server environment variables
  • App logging and audit
  • Scenarios not tied to an app — storage is always isolated per key

Documentation sections

Section Description
Upload Three upload paths: direct form (≤10 MB), presigned PUT (≤5 GB), multipart (≤5 TB)
Objects List, retrieve, metadata, delete objects, and public access

Authorization

All requests to /v1/storage/* require an API key in the X-Api-Key header with the vibe:storage scope.

New keys receive the vibe:storage scope automatically. If a key was created before it existed — add the scope in the API Keys section of your account.

A missing scope returns 403 STORAGE_SCOPE_REQUIRED on any /v1/storage/* call.

Key types

Key type Access Storage area
vibe_api_* — personal API key yes namespace of the key owner in the Bitrix24 account
vibe_app_* — app key yes namespace of the app the key is bound to

Management keys (vibe_live_*) are not suitable for /v1/storage/* — they have no Bitrix24 account binding. Any call returns 403 STORAGE_REQUIRES_PORTAL_BINDING.

Isolation. Objects are isolated per key owner: a key from one Bitrix24 account cannot access objects of another Bitrix24 account — such a request returns 404 STORAGE_OBJECT_NOT_FOUND, not 403, so as not to reveal the existence of another Bitrix24 account's resources.

Object lifecycle

PENDING      — created, awaiting file upload (paths B and C)
    │
    ▼ (path A → immediately COMPLETED)
COMPLETED    — file uploaded, available for reading
    │
    ▼ (DELETE)
deletedAt    — soft delete: read → 410; data retained for another 30 days
    │
    ▼ (+30 days, scheduler)
deleted      — data erased from storage

Direct upload (path A) creates the object immediately in the COMPLETED state. Paths B and C create the object in the PENDING state — until /complete is called.

Objects in the PENDING state without completion are removed by a background scheduler after 24 hours.

Upload paths

Path File size Steps When to use
A — direct upload up to 10 MB 1 Server-side code, small files
B — presigned PUT up to 5 GB 3 Browser upload without an intermediate server
C — multipart upload up to 5 TB 3 + N parts Large files, parallel upload

A detailed description of each path — Upload.

Visibility and access

Each object has a visibility attribute:

  • PRIVATE (default) — download via an authorized GET /v1/storage/objects/:key request, which returns a 302 redirect to a presigned URL valid for 10 minutes.
  • PUBLIC — additionally available via a permanent URL of the form https://vibecode.bitrix24.com/v1/public-storage/:portalId/:objectId without authorization.

For PUBLIC objects, serving files with content types text/html, application/javascript, application/x-javascript, image/svg+xml is blocked. An attempt to upload a file with such a type at visibility=PUBLIC returns 415 STORAGE_FORBIDDEN_CONTENT_TYPE.

Anonymous access via public-storage is enabled on the Bitrix24 account side and is off by default. While it is off, requests to the public URL return 503 STORAGE_PUBLIC_GET_DISABLED_FOR_PORTAL.

A permanent source for external tools. The presigned URL from GET /v1/storage/objects/:key is valid for 10 minutes and requires an X-Api-Key header, so it does not work as a permanent source for tools that do not send authorization headers — importing data into spreadsheets, report schedulers, external dashboards. For that scenario, set the object to PUBLIC and use its permanent URL https://vibecode.bitrix24.com/v1/public-storage/:portalId/:objectId, which is served without authorization while anonymous access is enabled on the Bitrix24 account side. For PUBLIC reports, content types such as text/csv, application/json, and similar are suitable. The types text/html, application/javascript, and image/svg+xml are blocked.

Object key

The object key is a unique identifier within the namespace bound to your key (the owner's personal namespace or the app's namespace). Rules:

  • Length: 1–1024 characters.
  • Allowed characters: a-z, A-Z, 0-9, ., _, /, -.
  • Cannot start with / or ..
  • Cannot contain the sequence ...
  • The / character is a path separator, used for grouping by prefix.

In URL path parameters, slashes in the key must be encoded: encodeURIComponent('users/42/report.csv') returns 'users%2F42%2Freport.csv'.

Limits

Parameter Value
Maximum file size (path A) 10 MB
Maximum file size (path B) 5 GB
Maximum file size (path C) 5 TB
Object key length 1–1024 characters
Presigned download URL validity 10 minutes
Soft-delete period 30 days
Objects in the PENDING state removed after 24 hours
Public URL request limit 60 requests/min from a single IP

Pricing

Storage runs on a pay-as-you-go scheme. The platform charges for three resources.

Resource Unit Field in GET /v1/me
Storage Ꝟ per GB-month costRateGbMonthVibes
Outbound traffic Ꝟ per GB costRateGbEgressVibes
Write operations Ꝟ per 1,000 operations costRateOps1kVibes

Rates are set by the platform and differ from instance to instance. The current values are returned in the storage block of the GET /v1/me response.

At a zero balance, write requests are blocked for 24 hours. Reading objects continues to work. Topping up the balance unblocks writing immediately.

Current usage and a cost forecast are in the storage block of the GET /v1/me response. The Bitrix24 account owner also sees aggregate spending across apps and can export a report on the Storage page in the Bitrix24 account admin panel.

Use in AI agents

Storage is available to AI models and agents without reading this page:

  • The storage block in the GET /v1/me response — storage state, rates, upload paths, and the list of endpoints.
  • The vibe_storage_* tools of the MCP server (upload, presigned links, list, delete, spending control) — see MCP for AI.

Quick start

Upload a file (path A):

Terminal
curl -X POST https://vibecode.bitrix24.com/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"

Get a download link:

Terminal
curl -I \
  -H "X-Api-Key: YOUR_API_KEY" \
  "https://vibecode.bitrix24.com/v1/storage/objects/reports%2Fmarch.csv"
# HTTP/1.1 302 Found
# Location: <temporary link to the object, valid for 10 minutes>

Full example

Full cycle: upload → list → get link → delete.

javascript
const KEY = 'YOUR_API_KEY'
const BASE = 'https://vibecode.bitrix24.com/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('Uploaded:', 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('Objects:', total, '| first:', 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('Link (10 min):', 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:', deleted.key, '| deletedAt:', deleted.deletedAt)

Common scenarios

Large file via multipart upload

Uploading a large file in parts with cancellation on error (to avoid paying for unfinished parts):

javascript
const KEY = 'YOUR_API_KEY'
const BASE = 'https://vibecode.bitrix24.com/v1'
const PART_SIZE = 8 * 1024 * 1024 // 8 MB

// file — File or Blob, e.g. from <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('Uploaded:', 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
}

Bulk cleanup by prefix

Deleting all of a user's files when they are disabled: paginated iteration by prefix and deletion of each object.

javascript
const KEY = 'YOUR_API_KEY'
const BASE = 'https://vibecode.bitrix24.com/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('Objects deleted:', count)

Endpoint reference

Upload

Method Path Description
POST /v1/storage/objects/upload Direct upload of a file up to 10 MB in a single multipart/form-data request
POST /v1/storage/objects Create a presigned URL to upload a file up to 5 GB directly to storage
POST /v1/storage/objects/complete Confirm completion of a presigned-URL upload
POST /v1/storage/objects/multipart/create Initiate a multipart upload: obtain a session identifier and URLs for the parts
POST /v1/storage/objects/multipart/complete Assemble the object from uploaded parts by their partNumber and etag
POST /v1/storage/objects/multipart/abort Cancel a multipart upload and remove unfinished parts

Objects

Method Path Description
GET /v1/storage/objects List objects with prefix filtering and cursor pagination
GET /v1/storage/objects/:key Download an object (302 redirect → presigned URL)
HEAD /v1/storage/objects/:key Object metadata without a response body
DELETE /v1/storage/objects/:key Soft-delete an object

Public access

Method Path Description
GET /v1/public-storage/:portalId/:objectId Download a PUBLIC object without an API key
HEAD /v1/public-storage/:portalId/:objectId Metadata of a PUBLIC object without an API key

Error codes

Code HTTP When it occurs
STORAGE_SCOPE_REQUIRED 403 API key does not have the vibe:storage scope
STORAGE_NO_AUTH_CONTEXT 401 Request without usable app credentials
STORAGE_KEY_REQUIRED 400 The key field is not passed or is empty
STORAGE_INVALID_KEY 400 The key contains invalid characters, starts with / or ., or contains ..
STORAGE_INVALID_PATH 400 The key forms a path injection
STORAGE_CONTENT_TYPE_REQUIRED 400 The contentType field is not passed (paths B and C)
STORAGE_INVALID_VISIBILITY 400 visibility is not equal to PUBLIC or PRIVATE
STORAGE_INVALID_SIZE 400 sizeBytes is not a non-negative integer
STORAGE_INVALID_TTL 400 ttlSeconds is outside the 60–86400 range
STORAGE_FILE_REQUIRED 400 The file part of the form is not passed (path A)
STORAGE_MULTIPART_PARSE_FAILED 400 Failed to parse multipart/form-data
STORAGE_OBJECT_ID_REQUIRED 400 The objectId parameter is not passed
STORAGE_INVALID_TOTAL_SIZE 400 totalSize is not a positive integer (path C)
STORAGE_INVALID_PART_SIZE 400 partSize is not a positive integer (path C)
STORAGE_PARTS_REQUIRED 400 The parts array is not passed or is empty
STORAGE_INVALID_PART 400 An element of the parts array does not match the { partNumber, etag } format
STORAGE_INVALID_PARTS 400 The set of parts was rejected by storage when assembling the multipart upload
STORAGE_TOO_MANY_PARTS 400 Too many parts for a multipart upload; increase partSize
STORAGE_INVALID_LIMIT 400 limit is not a positive integer
STORAGE_UPLOAD_TOO_LARGE 413 File size exceeds 10 MB (path A)
STORAGE_OBJECT_TOO_LARGE 413 totalSize exceeds 5 TB (path C)
STORAGE_FORBIDDEN_CONTENT_TYPE 415 Content type is not allowed for PUBLIC objects
STORAGE_OBJECT_NOT_FOUND 404 Object not found or does not belong to the calling app
STORAGE_UPLOAD_NOT_PENDING 409 The object is already uploaded or is in an invalid state
STORAGE_UPLOAD_PENDING 409 Attempt to read an object whose upload is not complete
STORAGE_UPLOAD_NOT_FOUND_IN_BUCKET 409 The PUT request to the presigned URL was not performed
STORAGE_MULTIPART_IN_PROGRESS 409 The object has an unfinished multipart upload; call /multipart/abort
STORAGE_OBJECT_DELETED 410 The object is marked as deleted
BILLING_INSUFFICIENT 402 Portal balance is zero — writing is paused for 24 hours (reading works)
STORAGE_RATE_LIMIT_EXCEEDED 429 Exceeded the limit of 60 requests/min to the public URL from a single IP
STORAGE_PUBLIC_GET_DISABLED_FOR_PORTAL 503 Public access is disabled at the portal level
STORAGE_FEATURE_DISABLED 503 Storage is temporarily disabled
STORAGE_STS_UNAVAILABLE 503 The temporary-credentials issuance service is unavailable
STORAGE_BUCKET_ERROR 502 Error accessing the object storage
STORAGE_OBJECT_MISSING_IN_BUCKET 502 The object is recorded in the database but absent from storage
STORAGE_OBJECT_STREAM_FAILED 502 Error while transferring object data
STORAGE_OBJECT_HEAD_FAILED 502 Error while retrieving object metadata from storage

General API error codes — Error codes.

See also