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

Create multipart upload

POST /v1/storage/objects/multipart/create

Opens a multipart upload session for files up to 5 TB. Returns the object identifier, the session identifier, and presigned URLs for uploading parts. After all parts are uploaded, call POST /v1/storage/objects/multipart/complete to assemble the object.

Request fields (body)

The request body is JSON.

Parameter Type Required Default Description
key string yes Logical object key: from 1 to 1024 characters, allowed a-z, A-Z, 0-9, ., _, /, -; must not start with / or ., must not contain ..
contentType string yes File MIME type, e.g. video/mp4 or application/zip
totalSize number yes Total file size in bytes; positive integer; no more than 5 TB (5,497,558,138,880 bytes)
partSize number no 8388608 Size of one part in bytes; from 5 MB (5,242,880) to 5 GB (5,368,709,120); the last part may be smaller than the minimum
visibility string no PRIVATE Object visibility: PRIVATE or PUBLIC

Examples

curl — personal key

Terminal
curl -X POST https://vibecode.bitrix24.com/v1/storage/objects/multipart/create \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "key": "videos/lecture-01.mp4",
    "contentType": "video/mp4",
    "totalSize": 209715200
  }'

curl — OAuth application

Terminal
curl -X POST https://vibecode.bitrix24.com/v1/storage/objects/multipart/create \
  -H "X-Api-Key: YOUR_APP_KEY" \
  -H "Authorization: Bearer USER_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "key": "videos/lecture-01.mp4",
    "contentType": "video/mp4",
    "totalSize": 209715200
  }'

JavaScript — personal key

javascript
const file = fileInput.files[0]

// Step 1 — open a multipart upload session
const createRes = await fetch(
  'https://vibecode.bitrix24.com/v1/storage/objects/multipart/create',
  {
    method: 'POST',
    headers: {
      'X-Api-Key': 'YOUR_API_KEY',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      key: 'videos/lecture-01.mp4',
      contentType: file.type,
      totalSize: file.size,
    }),
  }
)
const { objectId, parts } = await createRes.json()

// Step 2 — upload the parts in parallel, save the ETag from the response headers
const PART_SIZE = 8 * 1024 * 1024
const uploadedParts = await Promise.all(
  parts.map(async ({ partNumber, uploadUrl }) => {
    const start = (partNumber - 1) * PART_SIZE
    const end = Math.min(start + PART_SIZE, file.size)

    const putRes = await fetch(uploadUrl, {
      method: 'PUT',
      body: file.slice(start, end),
    })
    const etag = putRes.headers.get('ETag')
    return { partNumber, etag }
  })
)

// Step 3 — finalize object assembly
const completeRes = await fetch(
  'https://vibecode.bitrix24.com/v1/storage/objects/multipart/complete',
  {
    method: 'POST',
    headers: {
      'X-Api-Key': 'YOUR_API_KEY',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ objectId, parts: uploadedParts }),
  }
)
const { object } = await completeRes.json()
console.log('Object ID:', object.id)

JavaScript — OAuth application

javascript
const file = fileInput.files[0]

const createRes = await fetch(
  'https://vibecode.bitrix24.com/v1/storage/objects/multipart/create',
  {
    method: 'POST',
    headers: {
      'X-Api-Key': 'YOUR_APP_KEY',
      'Authorization': 'Bearer USER_SESSION_TOKEN',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      key: 'videos/lecture-01.mp4',
      contentType: file.type,
      totalSize: file.size,
    }),
  }
)
const { objectId, parts } = await createRes.json()

const PART_SIZE = 8 * 1024 * 1024
const uploadedParts = await Promise.all(
  parts.map(async ({ partNumber, uploadUrl }) => {
    const start = (partNumber - 1) * PART_SIZE
    const end = Math.min(start + PART_SIZE, file.size)

    const putRes = await fetch(uploadUrl, { method: 'PUT', body: file.slice(start, end) })
    const etag = putRes.headers.get('ETag')
    return { partNumber, etag }
  })
)

const completeRes = await fetch(
  'https://vibecode.bitrix24.com/v1/storage/objects/multipart/complete',
  {
    method: 'POST',
    headers: {
      'X-Api-Key': 'YOUR_APP_KEY',
      'Authorization': 'Bearer USER_SESSION_TOKEN',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ objectId, parts: uploadedParts }),
  }
)
const { object } = await completeRes.json()

Response fields

Field Type Description
objectId string Object identifier in storage
uploadId string Multipart upload session identifier
partCount number Number of parts, computed from totalSize and partSize
parts array Array of part descriptors
parts[].partNumber number Part number (starting from 1)
parts[].uploadUrl string Presigned URL for uploading the part via the PUT method
parts[].expiresAt string URL expiry (ISO 8601); matches the session lifetime — 24 hours

Response example

JSON
{
  "objectId": "cmpfg012a01aco510mrr6u632",
  "uploadId": "00065252B4EA33D1",
  "partCount": 2,
  "parts": [
    {
      "partNumber": 1,
      "uploadUrl": "https://storage.example.com/upload?partNumber=1&uploadId=00065252B4EA33D1&...",
      "expiresAt": "2026-05-22T12:03:56.398Z"
    },
    {
      "partNumber": 2,
      "uploadUrl": "https://storage.example.com/upload?partNumber=2&uploadId=00065252B4EA33D1&...",
      "expiresAt": "2026-05-22T12:03:56.398Z"
    }
  ]
}

Error response example

413 — the file exceeds the 5 TB limit:

JSON
{
  "success": false,
  "error": {
    "code": "STORAGE_OBJECT_TOO_LARGE",
    "message": "Object exceeds 5 TB cap"
  }
}

Errors

HTTP Code Description
403 STORAGE_SCOPE_REQUIRED The API key lacks the vibe:storage scope
401 STORAGE_NO_AUTH_CONTEXT The request was made without authorization
400 STORAGE_INVALID_PATH The caller identifier contains invalid path characters
400 STORAGE_KEY_REQUIRED The key field was not provided
400 STORAGE_INVALID_KEY The key value violates the format rules
400 STORAGE_CONTENT_TYPE_REQUIRED The contentType field was not provided
400 STORAGE_INVALID_VISIBILITY Invalid visibility value
400 STORAGE_INVALID_TOTAL_SIZE totalSize is not a positive integer
400 STORAGE_INVALID_PART_SIZE partSize is outside the allowed range (5 MB – 5 GB) or is not a positive integer
413 STORAGE_OBJECT_TOO_LARGE totalSize exceeds 5 TB
415 STORAGE_FORBIDDEN_CONTENT_TYPE For PUBLIC objects the types text/html, application/javascript, application/x-javascript, image/svg+xml are forbidden — Error codes
400 STORAGE_TOO_MANY_PARTS The computed number of parts exceeds 10,000 — increase partSize
402 BILLING_INSUFFICIENT Insufficient balance
503 STORAGE_FEATURE_DISABLED Storage is disabled for the portal
503 STORAGE_STS_UNAVAILABLE The temporary credentials issuance service is unavailable
502 STORAGE_BUCKET_ERROR Error while interacting with storage

Full list of common API errors — Errors.

Known specifics

Presigned URLs for parts do not require authorization headers. PUT requests to these URLs are sent directly to storage without X-Api-Key or Authorization — all necessary credentials are already signed into the URL itself.

Session lifetime — 24 hours. After it expires, the presigned URLs become invalid and the unuploaded parts are deleted automatically. To free resources early, call POST /v1/storage/objects/multipart/abort.

Smaller files. For files up to 10 MB use direct upload (Path A). For files up to 5 GB — a presigned URL (Path B).

See also