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 (DRF-based)

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)
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,
"created_at": "2025-01-15T10:05:00Z",
"updated_at": "2025-01-15T10:05:00Z",
"created_by": 1,
"modified_by": null
}

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-Iduser-id
  • X-Amz-Meta-Custom-Fieldcustom-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)

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., .jpgimage/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"'