Für KI-Agenten: Markdown dieser Seite — /docs-content-en/storage.md Dokumentationsindex — /llms.txt

Dokumentationsartikel sind derzeit auf Englisch verfügbar.

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
Upload Three upload paths: direct form (≤10 MB), presigned PUT (temporarily disabled), 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.

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
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 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
A — direct upload up to 10 MB 1 New objects, live-content replacement, and revival of an eligible tombstone with the same id and visibility
B — presigned PUT up to 5 GB 3 Temporarily disabled: both steps return 503 STORAGE_PRESIGNED_UPLOAD_DISABLED without a URL. Does not revive deleted objects
C — multipart upload up to 5 TB 3 + N parts Large 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
Maximum file size (path A) 10 MB
Maximum file size (path B, temporarily disabled) 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 after 24 hours the scheduler starts cleanup; multipart deletion follows only a confirmed abort
Public URL request limit 60 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
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.

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
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. Temporarily disabled
POST /v1/storage/objects/complete Confirm completion of a presigned-URL upload. Temporarily disabled
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, an opaque cursor, and exact counting only with withTotal=true
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 completed PUBLIC object without an API key
HEAD /v1/public-storage/:portalId/:objectId Metadata of a completed 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_INVALID_CURSOR 400 A 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
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; public GET/HEAD also returns this code for PENDING and PRIVATE objects
STORAGE_UPLOAD_NOT_PENDING 409 The object is already uploaded or is in an invalid state
STORAGE_UPLOAD_PENDING 409 The object upload is not complete: on a read the object is not ready yet; on an upload the key holds an unfinished presigned reservation
STORAGE_UPLOAD_NOT_FOUND_IN_BUCKET 409 The PUT request to the presigned URL was never made
STORAGE_MULTIPART_IN_PROGRESS 409 The object has an unfinished multipart upload; call /multipart/abort — it cannot be replaced or deleted until then
STORAGE_KEY_DELETED 409 This 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
STORAGE_KEY_OWNED_ELSEWHERE 409 The 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
STORAGE_KEY_EXISTS 409 The 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
STORAGE_KEY_CONFLICT 409 The 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
STORAGE_VISIBILITY_MISMATCH 400 Replacing content cannot change object visibility. Omit the visibility field or pass the current value
STORAGE_OBJECT_DELETED 410 The object is marked as deleted. Without an eligible subsequent upload, reads and a repeated DELETE stay 410 until physical purge
BILLING_INSUFFICIENT 402 Portal balance is zero — writing is paused for 24 hours (reading works)
STORAGE_QUOTA_EXCEEDED 507 The 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
STORAGE_RATE_LIMIT_EXCEEDED 429 Exceeded 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)
STORAGE_PUBLIC_GET_DISABLED_FOR_PORTAL 503 Public access is disabled at the portal level
STORAGE_FEATURE_DISABLED 503 Storage is temporarily disabled
STORAGE_PRESIGNED_UPLOAD_DISABLED 503 Path B is temporarily disabled. Use direct upload (Path A) or multipart upload (Path C)
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