Para agentes de IA: markdown desta página — /docs-content-en/storage.md índice da documentação — /llms.txt

Os artigos da documentação estão disponíveis atualmente em inglês.

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 safe write ownership cannot be confirmed, 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 a portal employee's own namespace. 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 the snapshots of its owner's servers; an app key sees only that app's snapshots. They 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

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 your 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`, {
  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
  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. 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 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 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_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 state changed, 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_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_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