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

Create presigned URL

POST /v1/storage/objects

The first step of uploading a file from 10 MB to 5 GB — returns a temporary URL the client uses to send the file directly via PUT; after sending, you call upload completion.

Request fields (body)

Field 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; must match the Content-Type header on the PUT request to uploadUrl
visibility string no PRIVATE Object visibility: PRIVATE or PUBLIC
sizeBytes number no Expected file size in bytes; an optional hint, the actual size is captured at the completion step
ttlSeconds number no 3600 Presigned URL lifetime in seconds: from 60 to 86400

Examples

curl — personal key

Terminal
# Step 1 — get a presigned URL
curl -X POST https://vibecode.bitrix24.com/v1/storage/objects \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "key": "users/42/report.pdf",
    "contentType": "application/pdf",
    "visibility": "PRIVATE"
  }'

# Step 2 — send the file to the received uploadUrl
curl -X PUT "https://storage.example.com/upload/..." \
  -H "Content-Type: application/pdf" \
  --data-binary @report.pdf

curl — OAuth application

Terminal
# Step 1 — get a presigned URL
curl -X POST https://vibecode.bitrix24.com/v1/storage/objects \
  -H "X-Api-Key: YOUR_APP_KEY" \
  -H "Authorization: Bearer USER_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "key": "users/42/report.pdf",
    "contentType": "application/pdf",
    "visibility": "PRIVATE"
  }'

# Step 2 — send the file to the received uploadUrl
curl -X PUT "https://storage.example.com/upload/..." \
  -H "Content-Type: application/pdf" \
  --data-binary @report.pdf

JavaScript — personal key

javascript
// Step 1 — get a presigned URL
const res = await fetch('https://vibecode.bitrix24.com/v1/storage/objects', {
  method: 'POST',
  headers: {
    'X-Api-Key': 'YOUR_API_KEY',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    key: 'users/42/report.pdf',
    contentType: 'application/pdf',
    visibility: 'PRIVATE',
  }),
})
if (!res.ok) {
  const { error } = await res.json()
  console.error(error.code, error.message)
  return
}
const result = await res.json()

// Step 2 — send the file to the presigned URL
const put = await fetch(result.uploadUrl, {
  method: 'PUT',
  headers: { 'Content-Type': 'application/pdf' },
  body: file, // File or Blob
})
if (!put.ok) throw new Error(`PUT failed: ${put.status}`)

JavaScript — OAuth application

javascript
// Step 1 — get a presigned URL
const res = await fetch('https://vibecode.bitrix24.com/v1/storage/objects', {
  method: 'POST',
  headers: {
    'X-Api-Key': 'YOUR_APP_KEY',
    'Authorization': 'Bearer USER_SESSION_TOKEN',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    key: 'users/42/report.pdf',
    contentType: 'application/pdf',
    visibility: 'PRIVATE',
  }),
})
const result = await res.json()

// Step 2 — send the file to the presigned URL
await fetch(result.uploadUrl, {
  method: 'PUT',
  headers: { 'Content-Type': 'application/pdf' },
  body: file, // File or Blob
})

Response fields

Field Type Description
objectId string Object identifier; pass it to POST /v1/storage/objects/complete
uploadUrl string Presigned URL for sending the file via the PUT method
bucket string Internal container name
objectKey string Internal object key in storage
expiresAt string Presigned URL expiry (ISO 8601)

Response example

JSON
{
  "objectId": "cmpf9b1gp003omr0zkc0qjojn",
  "uploadUrl": "https://storage.example.com/upload/portals/example-portal/users/42/report.pdf?X-Amz-Signature=abc123",
  "bucket": "example-storage-bucket",
  "objectKey": "portals/example-portal/users/42/report.pdf",
  "expiresAt": "2026-05-21T09:57:43.675Z"
}

Error response example

400 — ttlSeconds is outside the allowed range:

JSON
{
  "success": false,
  "error": {
    "code": "STORAGE_INVALID_TTL",
    "message": "ttlSeconds must be between 60 and 86400"
  }
}

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_KEY_REQUIRED The key field was not provided
400 STORAGE_CONTENT_TYPE_REQUIRED The contentType field was not provided
400 STORAGE_INVALID_KEY The key value violates the format rules
400 STORAGE_INVALID_VISIBILITY Invalid visibility value
400 STORAGE_INVALID_SIZE Invalid sizeBytes value
400 STORAGE_INVALID_TTL ttlSeconds is outside the range 60–86400
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

Mandatory second step. After sending the file to uploadUrl you must call upload completion with the objectId from this response. Until completion, the object stays in PENDING status and cannot be downloaded.

URL lifetime. ttlSeconds limits the lifetime of the presigned upload URL, not the retention period of the finished object.

See also