Skip to main content

Storage Objects API

Complete API reference for managing storage objects (files). This guide covers uploading, downloading, listing, and deleting files.

Endpoints Overview​

RESTful API​

MethodEndpointDescription
GET/api/apps/{app_slug}/storage/buckets/{bucket}/objects/List objects
POST/api/apps/{app_slug}/storage/buckets/{bucket}/objects/Upload file with multipart form (legacy)
POST/api/apps/{app_slug}/storage/buckets/{bucket}/objects/batch-upload/Upload multiple files (batch)
GET/api/apps/{app_slug}/storage/buckets/{bucket}/objects/{key}/Download file (default)
GET/api/apps/{app_slug}/storage/buckets/{bucket}/objects/{key}/?metadata=trueGet object metadata (JSON)
PATCH/api/apps/{app_slug}/storage/buckets/{bucket}/objects/{key}/Update metadata
DELETE/api/apps/{app_slug}/storage/buckets/{bucket}/objects/{key}/Delete object
POST/api/apps/{app_slug}/storage/buckets/{bucket}/objects/batch-delete/Delete multiple objects (batch)
MethodEndpointDescription
PUT/api/apps/{app_slug}/storage/buckets/{bucket}/objects/{key}Upload/update object
POST/api/apps/{app_slug}/storage/buckets/{bucket}/objects/{key}Upload/update object (alternative)
GET/api/apps/{app_slug}/storage/buckets/{bucket}/objects/{key}Download file (default) or get metadata (?metadata=true)
DELETE/api/apps/{app_slug}/storage/buckets/{bucket}/objects/{key}Delete object

Both PUT and POST support:

  • Multipart form data: Traditional file upload with file field
  • Raw binary: Send file bytes directly with metadata in headers

Object Model​

Fields​

FieldTypeDescription
idintegerInternal database ID
uuidUUIDUnique identifier for API references
bucketintegerBucket ID
bucket_slugstringBucket slug (read-only)
bucket_namestringBucket name (read-only)
filenamestringOriginal filename
file_pathstringBucket-relative file path (e.g., users/123/avatar.jpg)
file_urlstringAPI endpoint URL for accessing/downloading the file
sizeintegerFile size in bytes
mimetypestringMIME type (e.g., image/jpeg)
metadataobjectCustom JSON metadata
visibilitystringVisibility level: "private", "public", or null (inherits from bucket)
is_office_editablebooleanWhether SharePoint browser edit mode is supported for this object's MIME type
created_atdatetimeUpload timestamp
updated_atdatetimeLast update timestamp
created_byintegerUser who uploaded the file
modified_byintegerUser who last modified

Example Response​

{
"id": 1,
"uuid": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"bucket": 1,
"bucket_slug": "user-avatars",
"bucket_name": "User Avatars",
"filename": "avatar.jpg",
"file_path": "users/john-doe/avatar.jpg",
"file_url": "https://your-api.com/api/apps/my-app/storage/buckets/user-avatars/objects/users/john-doe/avatar.jpg",
"size": 245678,
"mimetype": "image/jpeg",
"metadata": {
"width": 512,
"height": 512,
"uploaded_from": "web"
},
"visibility": null,
"is_office_editable": false,
"created_at": "2025-01-15T10:05:00Z",
"updated_at": "2025-01-15T10:05:00Z",
"created_by": 1,
"modified_by": null
}

SharePoint-backed buckets expose current-user access actions for opening files in the browser:

MethodEndpointDescription
GET/api/apps/{app_slug}/storage/buckets/{bucket}/objects/{key}/view/Grant or refresh current-user view access and return SharePoint URL
GET/api/apps/{app_slug}/storage/buckets/{bucket}/objects/{key}/edit/Grant or refresh current-user Restricted Edit access and return SharePoint URL

These endpoints have no request body. The access target is always the current authenticated user.

Response Behavior (Content Negotiation)​

The response format depends on the Accept header:

Accept HeaderResponseUse Case
application/json200 OK with JSON body containing the URLSPA/frontend fetch() calls
Other / not set302 redirect to intermediate loader pageDirect browser navigation (window.open)

For API clients (SPA frontend):

GET /api/apps/{app_slug}/storage/buckets/{bucket}/objects/{key}/view/
Authorization: Bearer YOUR_TOKEN
Accept: application/json

Success Response (200 OK):

{
"status": "success",
"message": "View access granted",
"data": {
"url": "https://tenant.sharepoint.com/sites/taruvi-my-site/_layouts/15/Doc.aspx?sourcedoc={GUID}&action=embedview&web=1",
"mode": "view"
}
}

The frontend then opens the URL with window.open(data.url, '_blank').

For browser navigation (window.open directly to the endpoint):

When no Accept: application/json header is present, the endpoint redirects to a server-rendered intermediate page that:

  1. Shows a loading indicator ("Opening document...")
  2. Grants SharePoint permissions via a background SSE connection
  3. Redirects the browser to the SharePoint document URL on completion

This allows apps to simply call window.open('/api/.../objects/file.docx/view/') without implementing loading UI.

Edit Restrictions​

The /edit/ endpoint is restricted to Office-editable MIME types. The is_office_editable field on object responses indicates whether a file supports browser editing. Non-Office files (e.g., PDFs, images) will receive a 422 error on /edit/.

Supported Office MIME types include:

  • Word: application/vnd.openxmlformats-officedocument.wordprocessingml.document, .docx, .doc
  • Excel: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, .xlsx, .xls
  • PowerPoint: application/vnd.openxmlformats-officedocument.presentationml.presentation, .pptx, .ppt

Public vs Private Visibility​

The access flow differs based on the object's effective visibility:

Visibility/view/ behavior/edit/ behavior
PublicReturns an anonymous sharing link (no SharePoint sign-in needed)Returns an anonymous edit link (no sign-in needed)
PrivateGrants per-user Read permission, returns Doc.aspx URL (SharePoint sign-in required)Grants per-user Restricted Edit permission, returns Doc.aspx URL

For private objects, if the user is not yet in the Microsoft tenant, a silent guest invitation is created and the endpoint waits for propagation (up to ~8 seconds) before granting permission.

Authorization​

  • /view/ requires the caller to have read permission on the object (Cerbos storage:read)
  • /edit/ requires the caller to have update permission on the object (Cerbos storage:update)

Error Responses​

CodeError CodeWhen
200—Success (JSON mode)
302—Success (browser mode, redirects to loader page)
404NOT_FOUNDObject or bucket does not exist
409CONFLICTSharePoint site not ready (provider_error_code: "sharepoint_site_not_ready")
422VALIDATION_ERRORNon-Office file type for /edit/
502BAD_REQUESTProvider communication failure
503CONFLICTGuest invitation propagation timeout (provider_error_code: "guest_invitation_propagation_timeout") — retry the request

Example error (non-Office edit attempt):

{
"status": "error",
"message": "This file type cannot be edited in SharePoint browser mode.",
"code": "VALIDATION_ERROR"
}

Example error (propagation timeout):

{
"status": "error",
"message": "Guest user invitation did not propagate within the timeout. Please retry.",
"code": "CONFLICT",
"detail": "guest_invitation_propagation_timeout"
}

S3-Backed Objects​

The /view/ and /edit/ endpoints are only available for SharePoint-backed buckets. Calling them on S3-backed objects returns 422 Unprocessable Entity.

Upload or update an object using PUT requests with raw binary data.

Request​

PUT /api/apps/{app_slug}/storage/buckets/{bucket_slug}/objects/{key}
Authorization: Bearer YOUR_TOKEN
Content-Type: image/jpeg
X-Metadata-User-Id: 123
X-Metadata-Description: Profile photo

[raw binary data]

Features​

  • Raw Binary: Send file bytes directly in request body
  • Metadata Headers: Use X-Metadata-* headers for custom metadata
  • Path in URL: Object key/path is part of the URL
  • Upsert Behavior: Creates new or updates existing objects
  • Also supports POST: Use POST instead of PUT if preferred

Examples​

Upload with Raw Binary (curl)

curl -X PUT https://your-api.com/api/apps/my-app/storage/buckets/user-avatars/objects/users/john-doe/avatar.jpg \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: image/jpeg" \
-H "X-Metadata-User-Id: john-doe" \
-H "X-Metadata-Uploaded-From: mobile" \
--data-binary "@avatar.jpg"

Upload with Multipart (also works with PUT)

curl -X PUT https://your-api.com/api/apps/my-app/storage/buckets/user-avatars/objects/users/john-doe/avatar.jpg \
-H "Authorization: Bearer YOUR_TOKEN" \
-F "file=@avatar.jpg"

JavaScript Example (Raw Binary)

const file = document.querySelector('input[type="file"]').files[0];
const key = `users/${userId}/avatar.jpg`;

const response = await fetch(
`https://your-api.com/api/apps/my-app/storage/buckets/user-avatars/objects/${key}`,
{
method: 'PUT',
headers: {
'Authorization': 'Bearer YOUR_TOKEN',
'Content-Type': file.type,
'X-Metadata-User-Id': userId,
'X-Metadata-Uploaded-At': new Date().toISOString(),
},
body: file,
}
);

const result = await response.json();
console.log(result);

Python Example (Raw Binary)

import requests

with open('avatar.jpg', 'rb') as f:
file_content = f.read()

response = requests.put(
'https://your-api.com/api/apps/my-app/storage/buckets/user-avatars/objects/users/john-doe/avatar.jpg',
headers={
'Authorization': 'Bearer YOUR_TOKEN',
'Content-Type': 'image/jpeg',
'X-Metadata-User-Id': 'john-doe',
'X-Metadata-Source': 'python-script',
},
data=file_content
)

print(response.json())

Response (201 Created)​

When creating a new object:

{
"success": true,
"message": "Object created successfully",
"status_code": 201,
"data": {
"id": 1,
"uuid": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"bucket": 1,
"bucket_slug": "user-avatars",
"bucket_name": "User Avatars",
"filename": "avatar.jpg",
"file_path": "users/john-doe/avatar.jpg",
"size": 245678,
"mimetype": "image/jpeg",
"metadata": {
"user-id": "john-doe",
"uploaded-from": "mobile"
},
"created_at": "2025-01-15T10:05:00Z",
"updated_at": "2025-01-15T10:05:00Z",
"created_by": 1,
"modified_by": null
}
}

Response (200 OK)​

When updating an existing object:

{
"success": true,
"message": "Object updated successfully",
"status_code": 200,
"data": {
"id": 1,
"uuid": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"bucket": 1,
"bucket_slug": "user-avatars",
"bucket_name": "User Avatars",
"filename": "avatar.jpg",
"file_path": "users/john-doe/avatar.jpg",
"size": 312456,
"mimetype": "image/jpeg",
"metadata": {
"user-id": "john-doe",
"uploaded-from": "mobile"
},
"created_at": "2025-01-15T10:05:00Z",
"updated_at": "2025-01-15T11:30:00Z",
"created_by": 1,
"modified_by": 1
}
}

Metadata Headers​

Metadata can be passed via headers when uploading with raw binary data.

Recommended Format:

X-Metadata-User-Id: 123
X-Metadata-Custom-Field: value
X-Metadata-Author: John Doe
X-Metadata-Version: 2

S3-Compatible Format (also supported): For compatibility with S3 clients and tools, X-Amz-Meta-* headers are also supported:

X-Amz-Meta-User-Id: 123
X-Amz-Meta-Custom-Field: value

Headers are converted to lowercase keys with hyphens:

  • X-Metadata-User-Id → user-id
  • X-Amz-Meta-Custom-Field → custom-field (when using S3 format)

Validation​

Files are validated against bucket rules:

  • Size: Must not exceed file_size_limit
  • MIME Type: Must match allowed_mime_types (if set)

Permissions​

OperationPublic BucketPrivate Bucket
Read (GET)AnyoneOwner or staff
Write (PUT/POST/PATCH/DELETE)Authenticated usersOwner or staff only

Unauthenticated users can only read objects in public buckets.

Error Responses​

File Too Large (400)

{
"success": false,
"message": "File size (10485760 bytes) exceeds bucket limit (5MB)",
"status_code": 400
}

Invalid MIME Type (400)

{
"success": false,
"message": "MIME type 'application/pdf' not allowed. Allowed types: ['image/*']",
"status_code": 400
}

Upload File (Legacy)​

Upload a file to a bucket with multipart/form-data.

Request​

POST /api/apps/{app_slug}/storage/buckets/{bucket_slug}/objects/
Authorization: Bearer YOUR_TOKEN
Content-Type: multipart/form-data

file: [binary data]
path: users/john-doe/avatar.jpg (optional)
metadata: {"key": "value"} (optional)

Form Fields​

FieldTypeRequiredDescription
filefileYesThe file to upload
pathstringNoCustom path (defaults to filename)
metadataJSONNoCustom metadata object

Validation​

Before upload, the file is validated against bucket rules:

  • Size: Must not exceed file_size_limit
  • MIME Type: Must match allowed_mime_types (if set)

Upsert Behavior​

If an object with the same path already exists:

  • The existing object is updated (not duplicated)
  • File content is replaced
  • Metadata is updated
  • Returns 200 OK instead of 201 Created

Examples​

Basic Upload (curl)

curl -X POST https://your-api.com/api/apps/my-app/storage/buckets/user-avatars/objects/ \
-H "Authorization: Bearer YOUR_TOKEN" \
-F "file=@avatar.jpg"

Upload with Custom Path

curl -X POST https://your-api.com/api/apps/my-app/storage/buckets/user-avatars/objects/ \
-H "Authorization: Bearer YOUR_TOKEN" \
-F "file=@avatar.jpg" \
-F "path=users/john-doe/profile.jpg"

Upload with Metadata

curl -X POST https://your-api.com/api/apps/my-app/storage/buckets/documents/objects/ \
-H "Authorization: Bearer YOUR_TOKEN" \
-F "file=@report.pdf" \
-F "path=reports/2025/q1-report.pdf" \
-F 'metadata={"quarter":"Q1","year":2025,"author":"John Doe"}'

JavaScript Example

const formData = new FormData();
formData.append('file', fileInput.files[0]);
formData.append('path', 'users/john-doe/avatar.jpg');
formData.append('metadata', JSON.stringify({ uploaded_from: 'web' }));

const response = await fetch(
'https://your-api.com/api/apps/my-app/storage/buckets/user-avatars/objects/',
{
method: 'POST',
headers: { 'Authorization': 'Bearer YOUR_TOKEN' },
body: formData,
}
);

const result = await response.json();
console.log(result);

Response (201 Created)​

{
"success": true,
"message": "Object created successfully",
"status_code": 201,
"data": {
"id": 1,
"uuid": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"bucket": 1,
"bucket_slug": "user-avatars",
"bucket_name": "User Avatars",
"filename": "avatar.jpg",
"file_path": "users/john-doe/avatar.jpg",
"size": 245678,
"mimetype": "image/jpeg",
"metadata": {},
"created_at": "2025-01-15T10:05:00Z",
"updated_at": "2025-01-15T10:05:00Z",
"created_by": 1,
"modified_by": null
}
}

Response (200 OK - Update)​

When replacing an existing file:

{
"success": true,
"message": "Object updated successfully",
"status_code": 200,
"data": {
"id": 1,
"uuid": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"bucket": 1,
"bucket_slug": "user-avatars",
"bucket_name": "User Avatars",
"filename": "avatar.jpg",
"file_path": "users/john-doe/avatar.jpg",
"size": 312456,
"mimetype": "image/jpeg",
"metadata": {},
"created_at": "2025-01-15T10:05:00Z",
"updated_at": "2025-01-15T11:30:00Z",
"created_by": 1,
"modified_by": 1
}
}

Error Responses​

File Too Large (400)

{
"success": false,
"message": "File size (10485760 bytes) exceeds bucket limit (5MB)",
"status_code": 400
}

Invalid MIME Type (400)

{
"success": false,
"message": "MIME type 'application/pdf' not allowed. Allowed types: ['image/*']",
"status_code": 400
}

Empty File (400)

{
"error": "Cannot upload empty file"
}

Batch Upload Multiple Files​

For uploading multiple files efficiently (up to 10 at once), use the Batch Upload API:

POST /api/apps/{app_slug}/storage/buckets/{bucket_slug}/objects/batch-upload/

Benefits:

  • ~10x faster than sequential uploads
  • Single request for up to 10 files
  • 100MB total size limit across all files
  • Detailed success/failure tracking per file
  • Partial success handling

Example:

curl -X POST https://your-api.com/api/apps/my-app/storage/buckets/my-bucket/objects/batch-upload/ \
-H "Authorization: Bearer YOUR_TOKEN" \
-F "files=@photo1.jpg" \
-F "files=@photo2.jpg" \
-F "files=@document.pdf" \
-F 'paths=["users/123/photo1.jpg","users/123/photo2.jpg","users/123/doc.pdf"]'

See Batch Upload Objects for complete documentation with JavaScript, Python, and React examples.

List Objects​

Get all objects in a bucket with filtering and pagination.

Request​

GET /api/apps/{app_slug}/storage/buckets/{bucket_slug}/objects/
Authorization: Bearer YOUR_TOKEN

Query Parameters​

Basic Parameters​

ParameterTypeDescription
pageintegerPage number (default: 1)
page_sizeintegerResults per page (default: 10, max: 100)
orderingstringOrder by field (prefix with - for descending). Options: created_at, updated_at, size, filename, path

Search & Filter Parameters​

ParameterTypeDescription
searchstringSearch in both filename AND file path (case-insensitive)
filestringExact file path match
file__startswithstringFilter by file path prefix (e.g., invoices/)
file__icontainsstringSearch anywhere in file path (case-insensitive)
file__istartswithstringFile path starts with (case-insensitive)
filenamestringExact filename match
filename__icontainsstringSearch in filename only (case-insensitive)
filename__istartswithstringFilename starts with (case-insensitive)
filename__iendswithstringFilename ends with (case-insensitive)

Range Filters​

ParameterTypeDescription
size__gteintegerNew: Minimum file size in bytes (greater than or equal)
size__lteintegerNew: Maximum file size in bytes (less than or equal)
size__gtintegerNew: File size greater than
size__ltintegerNew: File size less than
min_sizeintegerNew: Friendly alternative to size__gte
max_sizeintegerNew: Friendly alternative to size__lte
created_at__gtedatetimeCreated on or after date (ISO 8601 format)
created_at__ltedatetimeCreated on or before date
created_at__datedateCreated on specific date (YYYY-MM-DD)
created_at__yearintegerFilter by year (e.g., 2024)
created_at__monthintegerFilter by month (1-12)
created_at__dayintegerFilter by day (1-31)
created_afterdatetimeFriendly alternative to created_at__gte
created_beforedatetimeFriendly alternative to created_at__lte
updated_at__gtedatetimeModified on or after date
updated_at__ltedatetimeModified on or before date
updated_at__yearintegerFilter by year
updated_at__monthintegerFilter by month (1-12)
updated_at__dayintegerFilter by day (1-31)
modified_afterdatetimeFriendly alternative to updated_at__gte
modified_beforedatetimeFriendly alternative to updated_at__lte

MIME Type Filters​

ParameterTypeDescription
mimetypestringExact MIME type match (e.g., image/jpeg)
mimetype__icontainsstringNew: MIME type contains (e.g., jpeg)
mimetype__istartswithstringNew: MIME type starts with (e.g., image/)
mimetype__instringNew: Multiple MIME types (comma-separated)
mimetype_categorystringNew: Filter by category: image, video, audio, application, text

Visibility & User Filters​

ParameterTypeDescription
visibilitystringNew: Filter by visibility: public or private
created_by_mebooleanNew: Filter files created by authenticated user (true/false)
modified_by_mebooleanNew: Filter files modified by authenticated user
created_byintegerNew: Filter by creator user ID
created_by__usernamestringNew: Filter by creator username (exact match)
created_by__username__icontainsstringNew: Creator username contains
modified_byintegerNew: Filter by modifier user ID
modified_by__username__icontainsstringNew: Modifier username contains
ParameterTypeDescription
metadata_searchstringNew: Search anywhere in metadata JSON (case-insensitive)

Examples​

Basic Listing​

List All Objects

curl https://your-api.com/api/apps/my-app/storage/buckets/user-avatars/objects/ \
-H "Authorization: Bearer YOUR_TOKEN"

With Pagination

curl "https://your-api.com/api/apps/my-app/storage/buckets/user-avatars/objects/?page=2&page_size=50" \
-H "Authorization: Bearer YOUR_TOKEN"

Search Examples​

Search by Filename or Path

# Finds "avatar.jpg" OR files in path containing "avatar"
curl "https://your-api.com/api/apps/my-app/storage/buckets/user-avatars/objects/?search=avatar" \
-H "Authorization: Bearer YOUR_TOKEN"

Search in File Path

# Find all files in "financials" folder
curl "https://your-api.com/api/apps/my-app/storage/buckets/documents/objects/?file__icontains=financials" \
-H "Authorization: Bearer YOUR_TOKEN"

Search by Filename Only

# Only search filename, not full path
curl "https://your-api.com/api/apps/my-app/storage/buckets/documents/objects/?filename__icontains=report" \
-H "Authorization: Bearer YOUR_TOKEN"

Range Filtering​

Files Between 1MB and 10MB

curl "https://your-api.com/api/apps/my-app/storage/buckets/documents/objects/?size__gte=1048576&size__lte=10485760" \
-H "Authorization: Bearer YOUR_TOKEN"

# Or use friendly names
curl "https://your-api.com/api/apps/my-app/storage/buckets/documents/objects/?min_size=1048576&max_size=10485760" \
-H "Authorization: Bearer YOUR_TOKEN"

Files Uploaded in January 2024

curl "https://your-api.com/api/apps/my-app/storage/buckets/documents/objects/?created_at__gte=2024-01-01&created_at__lte=2024-01-31" \
-H "Authorization: Bearer YOUR_TOKEN"

# Or use friendly names
curl "https://your-api.com/api/apps/my-app/storage/buckets/documents/objects/?created_after=2024-01-01&created_before=2024-01-31" \
-H "Authorization: Bearer YOUR_TOKEN"

Large Files Uploaded This Month

curl "https://your-api.com/api/apps/my-app/storage/buckets/documents/objects/?size__gte=10485760&created_at__gte=2024-01-01" \
-H "Authorization: Bearer YOUR_TOKEN"

MIME Type Filtering​

Single MIME Type

curl "https://your-api.com/api/apps/my-app/storage/buckets/documents/objects/?mimetype=application/pdf" \
-H "Authorization: Bearer YOUR_TOKEN"

Multiple MIME Types (All Images)

curl "https://your-api.com/api/apps/my-app/storage/buckets/assets/objects/?mimetype__in=image/png,image/jpeg,image/gif,image/webp" \
-H "Authorization: Bearer YOUR_TOKEN"

By MIME Category (All Images)

curl "https://your-api.com/api/apps/my-app/storage/buckets/assets/objects/?mimetype_category=image" \
-H "Authorization: Bearer YOUR_TOKEN"

All Videos

curl "https://your-api.com/api/apps/my-app/storage/buckets/media/objects/?mimetype_category=video" \
-H "Authorization: Bearer YOUR_TOKEN"

Visibility Filtering​

Only Public Files

curl "https://your-api.com/api/apps/my-app/storage/buckets/media/objects/?visibility=public" \
-H "Authorization: Bearer YOUR_TOKEN"

Only Private Files

curl "https://your-api.com/api/apps/my-app/storage/buckets/documents/objects/?visibility=private" \
-H "Authorization: Bearer YOUR_TOKEN"

Public Images Only

curl "https://your-api.com/api/apps/my-app/storage/buckets/assets/objects/?visibility=public&mimetype_category=image" \
-H "Authorization: Bearer YOUR_TOKEN"

User/Ownership Filtering​

Files I Uploaded

curl "https://your-api.com/api/apps/my-app/storage/buckets/documents/objects/?created_by_me=true" \
-H "Authorization: Bearer YOUR_TOKEN"

Files Uploaded by Specific User

curl "https://your-api.com/api/apps/my-app/storage/buckets/documents/objects/?created_by__username=john-doe" \
-H "Authorization: Bearer YOUR_TOKEN"

Files I Modified

curl "https://your-api.com/api/apps/my-app/storage/buckets/documents/objects/?modified_by_me=true" \
-H "Authorization: Bearer YOUR_TOKEN"

Search for Files by Username

curl "https://your-api.com/api/apps/my-app/storage/buckets/documents/objects/?created_by__username__icontains=john" \
-H "Authorization: Bearer YOUR_TOKEN"

Metadata Search​

Search in Metadata

# Find files with "invoice" anywhere in metadata
curl "https://your-api.com/api/apps/my-app/storage/buckets/documents/objects/?metadata_search=invoice" \
-H "Authorization: Bearer YOUR_TOKEN"

Search for Tagged Files

# Find files tagged with "important"
curl "https://your-api.com/api/apps/my-app/storage/buckets/documents/objects/?metadata_search=important" \
-H "Authorization: Bearer YOUR_TOKEN"

Sorting​

Order by Size (Largest First)

curl "https://your-api.com/api/apps/my-app/storage/buckets/user-avatars/objects/?ordering=-size" \
-H "Authorization: Bearer YOUR_TOKEN"

Order by Upload Date (Newest First)

curl "https://your-api.com/api/apps/my-app/storage/buckets/documents/objects/?ordering=-created_at" \
-H "Authorization: Bearer YOUR_TOKEN"

Order by Filename (A-Z)

curl "https://your-api.com/api/apps/my-app/storage/buckets/documents/objects/?ordering=filename" \
-H "Authorization: Bearer YOUR_TOKEN"

Complex Combined Filters​

Large PDFs Uploaded by John in January

curl "https://your-api.com/api/apps/my-app/storage/buckets/documents/objects/ \
?mimetype=application/pdf \
&size__gte=5242880 \
&created_by__username=john-doe \
&created_at__gte=2024-01-01 \
&created_at__lte=2024-01-31 \
&ordering=-size" \
-H "Authorization: Bearer YOUR_TOKEN"

My Public Images from Last Week

curl "https://your-api.com/api/apps/my-app/storage/buckets/photos/objects/ \
?created_by_me=true \
&visibility=public \
&mimetype_category=image \
&created_at__gte=2024-01-08 \
&ordering=-created_at" \
-H "Authorization: Bearer YOUR_TOKEN"

Find Large Files in Project Folder

curl "https://your-api.com/api/apps/my-app/storage/buckets/documents/objects/ \
?file__startswith=projects/project-123/ \
&size__gte=10485760 \
&ordering=-size" \
-H "Authorization: Bearer YOUR_TOKEN"

Search for Reports with Metadata

curl "https://your-api.com/api/apps/my-app/storage/buckets/documents/objects/ \
?search=report \
&metadata_search=Q1 \
&created_at__gte=2024-01-01 \
&ordering=-created_at" \
-H "Authorization: Bearer YOUR_TOKEN"

Response (200 OK)​

{
"status": "success",
"message": "Data retrieved successfully",
"status_code": 200,
"data": [
{
"id": 1,
"uuid": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"filename": "avatar.jpg",
"file_path": "users/john-doe/avatar.jpg",
"size": 245678,
"mimetype": "image/jpeg",
"created_at": "2025-01-15T10:05:00Z",
"updated_at": "2025-01-15T10:05:00Z"
},
{
"id": 2,
"uuid": "a3c5e2b7-9d4f-4b2a-8e6c-1f5d3a8b9c0d",
"filename": "profile.png",
"file_path": "users/jane-doe/profile.png",
"size": 189234,
"mimetype": "image/png",
"created_at": "2025-01-15T10:10:00Z",
"updated_at": "2025-01-15T10:10:00Z"
}
],
"total": 42,
"page": 1,
"page_size": 10
}

Response Fields:

  • data: Array of objects in the current page
  • total: Total number of objects matching the filters
  • page: Current page number
  • page_size: Number of items per page

Note: List responses use a simplified format for better performance when fetching many objects.

Get Object Metadata​

Retrieve metadata for a specific object by UUID.

Request​

GET /api/apps/{app_slug}/storage/buckets/{bucket_slug}/objects/{uuid}/
Authorization: Bearer YOUR_TOKEN

Example​

curl https://your-api.com/api/apps/my-app/storage/buckets/user-avatars/objects/7c9e6679-7425-40de-944b-e07fc1f90ae7/ \
-H "Authorization: Bearer YOUR_TOKEN"

Response (200 OK)​

{
"id": 1,
"uuid": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"bucket": 1,
"bucket_slug": "user-avatars",
"bucket_name": "User Avatars",
"filename": "avatar.jpg",
"file_path": "users/john-doe/avatar.jpg",
"file_url": "https://your-api.com/api/apps/my-app/storage/buckets/user-avatars/objects/users/john-doe/avatar.jpg",
"size": 245678,
"mimetype": "image/jpeg",
"metadata": {
"width": 512,
"height": 512
},
"created_at": "2025-01-15T10:05:00Z",
"updated_at": "2025-01-15T10:05:00Z",
"created_by": 1,
"modified_by": null
}

Download File​

Download the actual file content (binary data). Files are downloaded by default when accessing object endpoints.

Method 1: By UUID (Default Download)​

GET /api/apps/{app_slug}/storage/buckets/{bucket_slug}/objects/{uuid}/
Authorization: Bearer YOUR_TOKEN

Example:

curl https://your-api.com/api/apps/my-app/storage/buckets/user-avatars/objects/7c9e6679-7425-40de-944b-e07fc1f90ae7/ \
-H "Authorization: Bearer YOUR_TOKEN" \
-o downloaded_avatar.jpg

Method 2: By Path (Default Download)​

GET /api/apps/{app_slug}/storage/buckets/{bucket}/objects/{path}
Authorization: Bearer YOUR_TOKEN

Example:

curl https://your-api.com/api/apps/my-app/storage/buckets/user-avatars/objects/users/john-doe/avatar.jpg \
-H "Authorization: Bearer YOUR_TOKEN" \
-o avatar.jpg

Note: To get object metadata as JSON instead of downloading, add ?metadata=true to the URL.

Response Headers​

Content-Type: image/jpeg
Content-Disposition: inline; filename="avatar.jpg"
Content-Length: 245678

Response Body​

Binary file content (streamed directly from cloud storage).

Error Response (404)​

If file is not found:

{
"error": "Object file not found in storage",
"detail": "The requested file could not be found"
}

Using in HTML​

<!-- Direct image display -->
<img src="https://your-api.com/api/apps/my-app/storage/buckets/user-avatars/object/users/john-doe/avatar.jpg"
alt="Avatar" />

<!-- Download link -->
<a href="https://your-api.com/api/apps/my-app/storage/buckets/user-avatars/object/users/john-doe/avatar.jpg"
download="avatar.jpg">
Download Avatar
</a>

Note: Add Authorization header via JavaScript for private buckets.

Update Object Metadata​

Update an object's metadata without re-uploading the file.

Request​

PATCH /api/apps/{app_slug}/storage/buckets/{bucket_slug}/objects/{uuid}/
Authorization: Bearer YOUR_TOKEN
Content-Type: application/json

{
"metadata": {
"updated": true,
"processed_at": "2025-01-15T12:00:00Z"
}
}

Updatable Fields​

FieldTypeDescription
filenamestringChange filename
pathstringChange path (rename/move)
metadataobjectUpdate custom metadata

Note: size, mimetype, bucket, and uuid cannot be changed.

Examples​

Update Metadata

curl -X PATCH https://your-api.com/api/apps/my-app/storage/buckets/user-avatars/objects/7c9e6679-7425-40de-944b-e07fc1f90ae7/ \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"metadata": {
"width": 1024,
"height": 1024,
"processed": true
}
}'

Rename File (Change Path)

curl -X PATCH https://your-api.com/api/apps/my-app/storage/buckets/user-avatars/objects/7c9e6679-7425-40de-944b-e07fc1f90ae7/ \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"file": "users/john-doe/profile-picture.jpg",
"filename": "profile-picture.jpg"
}'

Response (200 OK)​

{
"id": 1,
"uuid": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"bucket": 1,
"bucket_slug": "user-avatars",
"bucket_name": "User Avatars",
"filename": "avatar.jpg",
"file_path": "users/john-doe/avatar.jpg",
"file_url": "https://your-api.com/api/apps/my-app/storage/buckets/user-avatars/objects/users/john-doe/avatar.jpg",
"size": 245678,
"mimetype": "image/jpeg",
"metadata": {
"width": 1024,
"height": 1024,
"processed": true
},
"created_at": "2025-01-15T10:05:00Z",
"updated_at": "2025-01-15T12:30:00Z",
"created_by": 1,
"modified_by": 1
}

Delete Object​

Delete an object and its file from storage.

Method 1: By UUID​

DELETE /api/apps/{app_slug}/storage/buckets/{bucket_slug}/objects/{uuid}/
Authorization: Bearer YOUR_TOKEN

Example:

curl -X DELETE https://your-api.com/api/apps/my-app/storage/buckets/user-avatars/objects/7c9e6679-7425-40de-944b-e07fc1f90ae7/ \
-H "Authorization: Bearer YOUR_TOKEN"

Method 2: By Path​

DELETE /api/apps/{app_slug}/storage/buckets/{bucket}/object/{path}
Authorization: Bearer YOUR_TOKEN

Example:

curl -X DELETE https://your-api.com/api/apps/my-app/storage/buckets/user-avatars/object/users/john-doe/avatar.jpg \
-H "Authorization: Bearer YOUR_TOKEN"

Response (204 No Content)​

{
"success": true,
"message": "Object \"users/john-doe/avatar.jpg\" deleted successfully",
"status_code": 204
}

Error Response (500)​

{
"success": false,
"message": "Failed to delete object \"users/john-doe/avatar.jpg\"",
"status_code": 500
}

Batch Delete Multiple Objects​

For deleting multiple objects efficiently (up to 100 at once), use the Batch Delete API:

POST /api/apps/{app_slug}/storage/buckets/{bucket_slug}/objects/batch-delete/

Benefits:

  • ~100x faster than individual deletes
  • Single request for up to 100 objects
  • Detailed success/failure tracking
  • Automatic permission handling

See Batch Delete Objects for complete documentation.

Path Organization​

Use forward slashes to create folder-like structures:

User Files​

users/{user_id}/avatar.jpg
users/{user_id}/documents/resume.pdf
users/{user_id}/photos/vacation-2025.jpg

Project Files​

projects/{project_id}/images/banner.png
projects/{project_id}/assets/logo.svg
projects/{project_id}/reports/monthly-report.pdf

Date-Based Organization​

uploads/2025/01/file1.jpg
uploads/2025/02/file2.jpg

Type-Based Organization​

images/photos/photo1.jpg
images/avatars/avatar1.jpg
documents/contracts/contract1.pdf
documents/invoices/invoice1.pdf

MIME Type Detection​

MIME types are automatically detected from:

  1. File extension (e.g., .jpg → image/jpeg)
  2. Upload Content-Type header (if provided)
  3. Defaults to application/octet-stream if unknown

Common MIME Types​

ExtensionMIME Type
.jpg, .jpegimage/jpeg
.pngimage/png
.gifimage/gif
.svgimage/svg+xml
.pdfapplication/pdf
.docapplication/msword
.docxapplication/vnd.openxmlformats-officedocument.wordprocessingml.document
.xlsapplication/vnd.ms-excel
.xlsxapplication/vnd.openxmlformats-officedocument.spreadsheetml.sheet
.txttext/plain
.csvtext/csv
.mp4video/mp4
.mp3audio/mpeg

Best Practices​

Path Design​

Do:

  • Use descriptive paths: users/john-doe/avatar.jpg
  • Group related files: projects/project-123/...
  • Use consistent naming: lowercase, hyphens

Don't:

  • Use absolute paths: /users/avatar.jpg (leading slash)
  • Use special characters: user@123/file!.jpg
  • Create very deep hierarchies: a/b/c/d/e/f/g/file.jpg

Metadata Usage​

Store custom information in the metadata field:

{
"metadata": {
"original_filename": "my photo.jpg",
"uploaded_by_device": "iPhone 12",
"image_dimensions": {"width": 1920, "height": 1080},
"tags": ["vacation", "2025", "beach"],
"processed": true,
"thumbnail_generated": true
}
}

Performance​

  1. Use File Path Prefixes for Filtering

    ?file__startswith=invoices/

    Efficient filtering by file path prefix.

  2. Limit Result Sets

    ?page_size=20
  3. Use Lightweight List Endpoint The list endpoint returns minimal fields for better performance.

Security​

  1. Validate on Client and Server

    • Check file size before upload
    • Validate MIME types
    • Sanitize filenames
  2. Use UUIDs for References

    // Store UUID in your database
    user.avatar_uuid = result.object.uuid;

    // Construct download URL (downloads by default)
    const url = `/api/apps/${appSlug}/storage/buckets/avatars/objects/${user.avatar_uuid}/`;
  3. Sanitize Paths

    # Remove leading/trailing slashes
    path = path.strip('/')

    # Replace multiple slashes
    path = re.sub(r'/+', '/', path)

File Access​

Path-Based Organization​

Objects are accessed using intuitive paths:

users/john-doe/avatar.jpg

Benefits:

  • Secure: Files are stored securely in the cloud
  • Flexible: Rename files without re-uploading
  • Organized: Use folder-like structures for organization

Efficient Downloads​

Files are delivered efficiently for the best performance:

  • No file size limits for downloads
  • Optimized for large files
  • Fast streaming delivery

Visibility Overrides​

Overview​

Objects can override their bucket's default visibility for fine-grained access control. This allows you to have a public bucket with some private files, or vice versa.

How It Works​

Every object has a visibility field:

  • null (default): Inherits visibility from the bucket
  • "public": Object is publicly accessible (no auth required for downloads)
  • "private": Object requires authentication and ownership

Setting Visibility on Upload​

Multipart Form Data Upload​

POST /api/apps/{app_slug}/storage/buckets/public-images/objects/
Content-Type: multipart/form-data

file=<binary>
path="users/123/private-doc.pdf"
visibility="private" # Override bucket's public visibility
metadata={"sensitive": true}

Raw Binary Upload (S3-style)​

PUT /api/apps/{app_slug}/storage/buckets/public-images/objects/users/123/secret.jpg
Content-Type: image/jpeg
X-Metadata-Visibility: private # Set visibility via header

<binary data>

Or in the request body (multipart):

PUT /api/apps/{app_slug}/storage/buckets/public-images/objects/users/123/secret.jpg
Content-Type: multipart/form-data

file=<binary>
visibility="private"

Updating Visibility​

You can change an object's visibility after upload:

PATCH /api/apps/{app_slug}/storage/buckets/public-images/objects/users/123/photo.jpg/
Content-Type: application/json

{
"visibility": "private" # Change from public to private
}

Or make it inherit from the bucket again:

PATCH /api/apps/{app_slug}/storage/buckets/public-images/objects/users/123/photo.jpg/
Content-Type: application/json

{
"visibility": null # Remove override, inherit from bucket
}

Access Control Examples​

Example 1: Private File in Public Bucket​

Bucket: "marketing-assets" (visibility: "public")
Object: "draft/new-logo.png" (visibility: "private")

Result: ✅ Most files are public
❌ Draft files require authentication
# This works (public image)
curl https://api.example.com/api/apps/my-app/storage/buckets/marketing-assets/objects/released/logo.png
# → 200 OK (no auth required)

# This requires auth (private image)
curl https://api.example.com/api/apps/my-app/storage/buckets/marketing-assets/objects/draft/new-logo.png
# → 403 Forbidden (needs authentication + ownership)

Example 2: Public File in Private Bucket​

Bucket: "user-documents" (visibility: "private")
Object: "shared/public-report.pdf" (visibility: "public")

Result: ✅ Most files are private
✅ Specific files can be shared publicly
# This requires auth (private document)
curl https://api.example.com/api/apps/my-app/storage/buckets/user-documents/objects/personal/tax-return.pdf
# → 403 Forbidden

# This works (public document)
curl https://api.example.com/api/apps/my-app/storage/buckets/user-documents/objects/shared/public-report.pdf
# → 200 OK (no auth required)

Use Cases​

# Create public bucket for images
POST /api/apps/{app_slug}/storage/buckets/
{
"name": "Product Images",
"visibility": "public"
}

# Upload public image (inherits)
PUT /api/apps/{app_slug}/storage/buckets/product-images/objects/products/shoe-1.jpg
file=<binary>
# visibility: null → effective: public

# Upload draft image (override)
PUT /api/apps/{app_slug}/storage/buckets/product-images/objects/drafts/shoe-2.jpg
file=<binary>
visibility="private"
# visibility: private → effective: private
# Create private bucket for documents
POST /api/apps/{app_slug}/storage/buckets/
{
"name": "Company Documents",
"visibility": "private"
}

# Upload private document (inherits)
PUT /api/apps/{app_slug}/storage/buckets/company-documents/objects/internal/strategy.pdf
file=<binary>
# visibility: null → effective: private

# Upload public shareable document (override)
PUT /api/apps/{app_slug}/storage/buckets/company-documents/objects/public/annual-report.pdf
file=<binary>
visibility="public"
# visibility: public → effective: public

Checking Effective Visibility​

The API response always includes the object's visibility field:

{
"file_path": "users/123/avatar.jpg",
"visibility": null, // null = inherits from bucket
"bucket_slug": "user-avatars",
// ... other fields
}

To determine effective visibility:

  • If visibility is null → check the bucket's visibility
  • If visibility is "public" or "private" → use that value

Best Practices​

  1. Default to Inheritance: Leave visibility: null unless you need to override
  2. Use Sparingly: Too many overrides can be confusing. Consider using separate buckets instead
  3. Document Overrides: Add metadata explaining why an object has a different visibility:
    {
    "visibility": "private",
    "metadata": {
    "reason": "Contains unreleased product information",
    "expires": "2025-12-31"
    }
    }
  4. Audit Regularly: List objects with specific visibility to find exceptions:
    # Find all private objects in a public bucket
    GET /api/apps/{app_slug}/storage/buckets/public-bucket/objects/?visibility=private

Security Considerations​

⚠️ Important Notes:

  1. Write Operations Always Require Auth: Even for public objects, upload/delete/update operations require authentication and ownership
  2. Visibility != Permissions: Visibility controls read access. Write access is always restricted to the owner
  3. Monitor Public Objects: Regularly audit public objects to ensure no sensitive data is exposed:
    GET /api/apps/{app_slug}/storage/buckets/my-bucket/objects/ | grep '"visibility": "public"'