لوكلاء AI: ملف markdown لهذه الصفحة — /docs-content-en/storage.md فهرس التوثيق — /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 your Bitrix24 account's 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
SectionUpload DescriptionThree upload paths: direct form (≤10 MB), presigned PUT (temporarily disabled), multipart (≤5 TB)
SectionObjects DescriptionList, 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.

The vibe:storage scope is pre-selected in the key creation form. If a key does not have it, add the scope in the API Keys section of your Vibecode account.

Without the scope, any /v1/storage/* call returns 403 STORAGE_SCOPE_REQUIRED.

Key types

Key type Access Storage area
Key typevibe_api_* — personal API key Accessyes Storage areanamespace of the key owner in the Bitrix24 account
Key typevibe_app_* — app key Accessyes Storage areanamespace 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 that belong to another — such a request returns 404 STORAGE_OBJECT_NOT_FOUND, not 403, so it does not reveal that another account's resources exist.

Object lifecycle

PENDING      — created, awaiting file upload (first upload through Paths B and C)
    │
    ▼ (path A → immediately COMPLETED)
COMPLETED    — file uploaded and readable; app-bound Path C may stage a replacement on the same row
    │
    ▼ (DELETE)
deletedAt    — soft delete: read → 410; data retained for another 30 days
    │
    ├──► (Path A or personal Path C after DELETE, new bytes) → COMPLETED with the same id and visibility
    │
    ▼ (+30 days, scheduler)
deleted      — data erased from storage

Direct upload (Path A) creates the object immediately in the COMPLETED state. A first upload through Paths B and C creates a PENDING row until /complete is called. During replacement of an app-bound object, Path C keeps the row COMPLETED: reads return the old content until /multipart/complete publishes the new bytes. A successful complete response confirms the new version; 502 STORAGE_BUCKET_ERROR can mean publication happened but its result was not confirmed. A confirmed abort before publication preserves the old version. Automatic cleanup releases parts that are confirmed incomplete or finalizes a recorded publication of the new version, while an unconfirmed outcome retains the session for reconciliation.

An eligible soft-deleted object can be reused for new bytes: the row must have COMPLETED status, have no multipart session, and belong to the same owner tuple. Path A supports app-bound and personal keys up to 10 MB. Path C supports only a personal row after an explicit DELETE and is suitable for a large file. Both paths preserve id, createdAt, key, physical address, and visibility, and clear deletedAt only after a successful upload. This does not restore the old content. Path C is non-atomic: the object is unavailable between DELETE and successful complete. Path B is disabled and returns 503 STORAGE_PRESIGNED_UPLOAD_DISABLED.

After 24 hours, the background scheduler starts cleanup of unfinished PENDING objects. For a multipart session, the object and its parts are deleted only after the provider confirms the abort; if the abort is not confirmed, the session is retained and releasing it requires another abort attempt or operator action.

Upload paths

Path File size Steps When to use
PathA — direct upload File sizeup to 10 MB Steps1 When to useNew objects, live-content replacement, and revival of an eligible tombstone with the same id and visibility
PathB — presigned PUT File sizeup to 5 GB Steps3 When to useTemporarily disabled: both steps return 503 STORAGE_PRESIGNED_UPLOAD_DISABLED without a URL. Does not revive deleted objects
PathC — multipart upload File sizeup to 5 TB Steps3 + N parts When to useLarge new files, replacement of a live app-bound object, and a new personal-key upload after explicit DELETE. The last operation is non-atomic: the object is unavailable until complete

For Path A direct uploads and all Path C uploads, concurrent requests to the same physical address run one at a time. A request waits for its turn for no more than 60 seconds; if write ownership cannot be safely obtained, the server returns 409 STORAGE_KEY_CONFLICT. For Path A, this conflict occurs before writing to object storage. Retry the entire request. On Path C, a 409 from complete or abort can occur after parts already occupy storage; unfinished parts are released by a confirmed abort, while a journalled complete response means the provider has already assembled the object. Path A also covers a personal key (appId = null); Path C replaces a live object only for an app-bound key and accepts a personal key after explicit DELETE. Path B is excluded entirely.

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 — after the upload is completed (COMPLETED), additionally available via a permanent URL of the form https://vibecode.bitrix24.com/v1/public-storage/:portalId/:objectId without authorization. While the object is PENDING, the public URL returns 404 STORAGE_OBJECT_NOT_FOUND.

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 and visibility=PUBLIC returns 415 STORAGE_FORBIDDEN_CONTENT_TYPE.

Anonymous access via public-storage must be enabled for your Bitrix24 account 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 must be requested with an X-Api-Key header, so it does not work as a permanent source for tools that do not send authorization headers — spreadsheet data imports, report schedulers, external dashboards. For that scenario, complete the upload of a PUBLIC object and use its permanent URL https://vibecode.bitrix24.com/v1/public-storage/:portalId/:objectId, which is served without authorization while anonymous access is enabled for the Bitrix24 account. For PUBLIC reports, content types such as text/csv and application/json are suitable. The types text/html, application/javascript, and image/svg+xml are blocked.

Object key

The object key is the logical name of a file. It is unique per OWNER: the personal namespace of the key owner, the app-shared namespace, or an employee's personal namespace in the Bitrix24 account. So two employees of one app claim the same name independently of each other and get two distinct objects, while an app-shared file and a per-employee file share one namespace and cannot both hold one name. It follows that a name does NOT address an object unambiguously: use the object identifier for exact access. Format 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'.

Source code snapshots. The platform saves an app's sources into this same storage — they appear in GET /v1/storage/objects under the source/ prefix. A personal key sees all of its owner's objects in the Bitrix24 account. An app key without a user token lists only the app's shared objects, while a key with a user token lists only the objects of the corresponding Bitrix24 employee. Snapshots are removed through the source operations: DELETE /v1/infra/servers/:id/sources/:versionId for a single version and POST /v1/infra/servers/:id/sources/cleanup for bulk cleanup, with the same operations under the /v1/apps/:id/sources prefix for apps. What lives under that prefix and why the same key appears in the listing several times — Source code storage.

Limits

Parameter Value
ParameterMaximum file size (path A) Value10 MB
ParameterMaximum file size (path B, temporarily disabled) Value5 GB
ParameterMaximum file size (path C) Value5 TB
ParameterObject key length Value1–1024 characters
ParameterPresigned download URL validity Value10 minutes
ParameterSoft-delete period Value30 days
ParameterObjects in the PENDING state Valueafter 24 hours the scheduler starts cleanup; multipart deletion follows only a confirmed abort
ParameterPublic URL request limit Value60 requests/min from a single IP, platform-wide. The exact value arrives in the x-ratelimit-limit header (the cap is divided across replicas)

Pricing

Storage is billed on a pay-as-you-go basis. Charges apply to three resources.

Resource Unit Field in GET /v1/me
ResourceStorage UnitꝞ per GB-month Field in GET /v1/mecostRateGbMonthVibes
ResourceOutbound traffic UnitꝞ per GB Field in GET /v1/mecostRateGbEgressVibes
ResourceWrite operations UnitꝞ per 1,000 operations Field in GET /v1/mecostRateOps1kVibes

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.

When the balance is zero, 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 admin section of the Vibecode account.

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 MCP server's vibe_storage_* tools (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&withTotal=true`, {
  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 that user is deactivated: 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
  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('Objects deleted:', count)

Endpoint reference

Upload

Method Path Description
MethodPOST Path/v1/storage/objects/upload DescriptionDirect upload of a file up to 10 MB in a single multipart/form-data request
MethodPOST Path/v1/storage/objects DescriptionCreate a presigned URL to upload a file up to 5 GB directly to storage. Temporarily disabled
MethodPOST Path/v1/storage/objects/complete DescriptionConfirm completion of a presigned-URL upload. Temporarily disabled
MethodPOST Path/v1/storage/objects/multipart/create DescriptionInitiate a multipart upload: obtain a session identifier and URLs for the parts
MethodPOST Path/v1/storage/objects/multipart/complete DescriptionAssemble the object from uploaded parts by their partNumber and etag
MethodPOST Path/v1/storage/objects/multipart/abort DescriptionCancel a multipart upload and remove unfinished parts

Objects

Method Path Description
MethodGET Path/v1/storage/objects DescriptionList objects with prefix filtering, an opaque cursor, and exact counting only with withTotal=true
MethodGET Path/v1/storage/objects/:key DescriptionDownload an object (302 redirect → presigned URL)
MethodHEAD Path/v1/storage/objects/:key DescriptionObject metadata without a response body
MethodDELETE Path/v1/storage/objects/:key DescriptionSoft-delete an object

Public access

Method Path Description
MethodGET Path/v1/public-storage/:portalId/:objectId DescriptionDownload a completed PUBLIC object without an API key
MethodHEAD Path/v1/public-storage/:portalId/:objectId DescriptionMetadata of a completed PUBLIC object without an API key

Error codes

Code HTTP When it occurs
CodeSTORAGE_SCOPE_REQUIRED HTTP403 When it occursAPI key does not have the vibe:storage scope
CodeSTORAGE_NO_AUTH_CONTEXT HTTP401 When it occursRequest without usable app credentials
CodeSTORAGE_KEY_REQUIRED HTTP400 When it occursThe key field is not passed or is empty
CodeSTORAGE_INVALID_KEY HTTP400 When it occursThe key contains invalid characters, starts with / or ., or contains ..
CodeSTORAGE_INVALID_PATH HTTP400 When it occursThe key forms a path injection
CodeSTORAGE_CONTENT_TYPE_REQUIRED HTTP400 When it occursThe contentType field is not passed (paths B and C)
CodeSTORAGE_INVALID_VISIBILITY HTTP400 When it occursvisibility is not equal to PUBLIC or PRIVATE
CodeSTORAGE_INVALID_SIZE HTTP400 When it occurssizeBytes is not a non-negative integer
CodeSTORAGE_INVALID_TTL HTTP400 When it occursttlSeconds is outside the 60–86400 range
CodeSTORAGE_FILE_REQUIRED HTTP400 When it occursThe file part of the form is not passed (path A)
CodeSTORAGE_MULTIPART_PARSE_FAILED HTTP400 When it occursFailed to parse multipart/form-data
CodeSTORAGE_OBJECT_ID_REQUIRED HTTP400 When it occursThe objectId parameter is not passed
CodeSTORAGE_INVALID_TOTAL_SIZE HTTP400 When it occurstotalSize is not a positive integer (path C)
CodeSTORAGE_INVALID_PART_SIZE HTTP400 When it occurspartSize is not a positive integer (path C)
CodeSTORAGE_PARTS_REQUIRED HTTP400 When it occursThe parts array is not passed or is empty
CodeSTORAGE_INVALID_PART HTTP400 When it occursAn element of the parts array does not match the { partNumber, etag } format
CodeSTORAGE_INVALID_PARTS HTTP400 When it occursThe set of parts was rejected by storage when assembling the multipart upload
CodeSTORAGE_TOO_MANY_PARTS HTTP400 When it occursToo many parts for a multipart upload; increase partSize
CodeSTORAGE_INVALID_LIMIT HTTP400 When it occurslimit is not a positive integer
CodeSTORAGE_INVALID_CURSOR HTTP400 When it occursA new opaque cursor is corrupted, modified, oversized, or belongs to another owner, prefix, or ordering mode; a legacy CUID cursor has an invalid shape, is oversized, or is used with prefix
CodeSTORAGE_UPLOAD_TOO_LARGE HTTP413 When it occursFile size exceeds 10 MB (path A)
CodeSTORAGE_OBJECT_TOO_LARGE HTTP413 When it occurstotalSize exceeds 5 TB (path C)
CodeSTORAGE_FORBIDDEN_CONTENT_TYPE HTTP415 When it occursContent type is not allowed for PUBLIC objects
CodeSTORAGE_OBJECT_NOT_FOUND HTTP404 When it occursObject not found or does not belong to the calling app; public GET/HEAD also returns this code for PENDING and PRIVATE objects
CodeSTORAGE_UPLOAD_NOT_PENDING HTTP409 When it occursThe object is already uploaded or is in an invalid state
CodeSTORAGE_UPLOAD_PENDING HTTP409 When it occursThe object upload is not complete: on a read the object is not ready yet; on an upload the key holds an unfinished presigned reservation
CodeSTORAGE_UPLOAD_NOT_FOUND_IN_BUCKET HTTP409 When it occursThe PUT request to the presigned URL was never made
CodeSTORAGE_MULTIPART_IN_PROGRESS HTTP409 When it occursThe object has an unfinished multipart upload; call /multipart/abort — it cannot be replaced or deleted until then
CodeSTORAGE_KEY_DELETED HTTP409 When it occursThis path cannot reuse the deleted key. Path A revives only a COMPLETED object without a multipart session, preserving its id and visibility; Path B currently returns 503 without a URL; Path C accepts only an eligible personal-key COMPLETED tombstone after explicit DELETE, while other states remain 409
CodeSTORAGE_KEY_OWNED_ELSEWHERE HTTP409 When it occursThe name is taken by an object of the other ownership kind. Employees of an app claim names independently of each other, while an app-shared file and a per-employee file share one namespace
CodeSTORAGE_KEY_EXISTS HTTP409 When it occursThe object already exists. Replacing content through a presigned URL is not supported; Path B is temporarily disabled with 503 STORAGE_PRESIGNED_UPLOAD_DISABLED. Use Path A up to 10 MB; for a larger app-bound file use Path C. For a live personal object, DELETE first and then start Path C. This is non-atomic: the object is unavailable until complete succeeds
CodeSTORAGE_KEY_CONFLICT HTTP409 When it occursThe object is in a conflicting state, or a concurrent Path A or Path C upload did not obtain write ownership within 60 seconds or the concurrency limit. For Path A, object storage is untouched until confirmation; retry the entire request. On Path C, a 409 from complete or abort can arrive after parts were uploaded: unfinished parts occupy storage until a confirmed abort, while a journalled complete means the provider has already assembled the object. Serialization also covers a personal key (appId = null); Path B is excluded
CodeSTORAGE_VISIBILITY_MISMATCH HTTP400 When it occursReplacing content cannot change object visibility. Omit the visibility field or pass the current value
CodeSTORAGE_OBJECT_DELETED HTTP410 When it occursThe object is marked as deleted. Without an eligible subsequent upload, reads and a repeated DELETE stay 410 until physical purge
CodeBILLING_INSUFFICIENT HTTP402 When it occursPortal balance is zero — writing is paused for 24 hours (reading works)
CodeSTORAGE_QUOTA_EXCEEDED HTTP507 When it occursThe write exceeds the portal storage volume cap. The cap is optional, is switched on by the platform, and applies only to portals that have no commercial Bitrix24 plan and never had one. Usage is counted across the whole portal, not per app. Path A rejects the write when the file size does not fit the remaining cap, Path C on the declared totalSize at /multipart/create, before any part is uploaded. The response message names the cap in effect
CodeSTORAGE_RATE_LIMIT_EXCEEDED HTTP429 When it occursExceeded the limit of 60 requests/min to the public URL from a single IP. The exact value arrives in the x-ratelimit-limit header (the cap is divided across replicas)
CodeSTORAGE_PUBLIC_GET_DISABLED_FOR_PORTAL HTTP503 When it occursPublic access is disabled at the portal level
CodeSTORAGE_FEATURE_DISABLED HTTP503 When it occursStorage is temporarily disabled
CodeSTORAGE_PRESIGNED_UPLOAD_DISABLED HTTP503 When it occursPath B is temporarily disabled. Use direct upload (Path A) or multipart upload (Path C)
CodeSTORAGE_STS_UNAVAILABLE HTTP503 When it occursThe temporary-credentials issuance service is unavailable
CodeSTORAGE_BUCKET_ERROR HTTP502 When it occursError accessing the object storage
CodeSTORAGE_OBJECT_MISSING_IN_BUCKET HTTP502 When it occursThe object is recorded in the database but absent from storage
CodeSTORAGE_OBJECT_STREAM_FAILED HTTP502 When it occursError while transferring object data
CodeSTORAGE_OBJECT_HEAD_FAILED HTTP502 When it occursError while retrieving object metadata from storage

General API error codes — Error codes.

See also