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)
| Method | Endpoint | Description |
|---|---|---|
| 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=true | Get 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) |
Path-Based API ⭐ Recommended
| Method | Endpoint | Description |
|---|---|---|
| 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
filefield - Raw binary: Send file bytes directly with metadata in headers
Object Model
Fields
| Field | Type | Description |
|---|---|---|
id | integer | Internal database ID |
uuid | UUID | Unique identifier for API references |
bucket | integer | Bucket ID |
bucket_slug | string | Bucket slug (read-only) |
bucket_name | string | Bucket name (read-only) |
filename | string | Original filename |
file_path | string | Bucket-relative file path (e.g., users/123/avatar.jpg) |
file_url | string | API endpoint URL for accessing/downloading the file |
size | integer | File size in bytes |
mimetype | string | MIME type (e.g., image/jpeg) |
metadata | object | Custom JSON metadata |
visibility | string | Visibility level: "private", "public", or null (inherits from bucket) |
created_at | datetime | Upload timestamp |
updated_at | datetime | Last update timestamp |
created_by | integer | User who uploaded the file |
modified_by | integer | User 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
}
PutObject ⭐ Recommended
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-idX-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)
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
| Field | Type | Required | Description |
|---|---|---|---|
file | file | Yes | The file to upload |
path | string | No | Custom path (defaults to filename) |
metadata | JSON | No | Custom 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 OKinstead of201 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
| Parameter | Type | Description |
|---|---|---|
page | integer | Page number (default: 1) |
page_size | integer | Results per page (default: 10, max: 100) |
ordering | string | Order by field (prefix with - for descending). Options: created_at, updated_at, size, filename, path |
Search & Filter Parameters
| Parameter | Type | Description |
|---|---|---|
search | string | Search in both filename AND file path (case-insensitive) |
file | string | Exact file path match |
file__startswith | string | Filter by file path prefix (e.g., invoices/) |
file__icontains | string | Search anywhere in file path (case-insensitive) |
file__istartswith | string | File path starts with (case-insensitive) |
filename | string | Exact filename match |
filename__icontains | string | Search in filename only (case-insensitive) |
filename__istartswith | string | Filename starts with (case-insensitive) |
filename__iendswith | string | Filename ends with (case-insensitive) |
Range Filters
| Parameter | Type | Description |
|---|---|---|
size__gte | integer | New: Minimum file size in bytes (greater than or equal) |
size__lte | integer | New: Maximum file size in bytes (less than or equal) |
size__gt | integer | New: File size greater than |
size__lt | integer | New: File size less than |
min_size | integer | New: Friendly alternative to size__gte |
max_size | integer | New: Friendly alternative to size__lte |
created_at__gte | datetime | Created on or after date (ISO 8601 format) |
created_at__lte | datetime | Created on or before date |
created_at__date | date | Created on specific date (YYYY-MM-DD) |
created_at__year | integer | Filter by year (e.g., 2024) |
created_at__month | integer | Filter by month (1-12) |
created_at__day | integer | Filter by day (1-31) |
created_after | datetime | Friendly alternative to created_at__gte |
created_before | datetime | Friendly alternative to created_at__lte |
updated_at__gte | datetime | Modified on or after date |
updated_at__lte | datetime | Modified on or before date |
updated_at__year | integer | Filter by year |
updated_at__month | integer | Filter by month (1-12) |
updated_at__day | integer | Filter by day (1-31) |
modified_after | datetime | Friendly alternative to updated_at__gte |
modified_before | datetime | Friendly alternative to updated_at__lte |
MIME Type Filters
| Parameter | Type | Description |
|---|---|---|
mimetype | string | Exact MIME type match (e.g., image/jpeg) |
mimetype__icontains | string | New: MIME type contains (e.g., jpeg) |
mimetype__istartswith | string | New: MIME type starts with (e.g., image/) |
mimetype__in | string | New: Multiple MIME types (comma-separated) |
mimetype_category | string | New: Filter by category: image, video, audio, application, text |
Visibility & User Filters
| Parameter | Type | Description |
|---|---|---|
visibility | string | New: Filter by visibility: public or private |
created_by_me | boolean | New: Filter files created by authenticated user (true/false) |
modified_by_me | boolean | New: Filter files modified by authenticated user |
created_by | integer | New: Filter by creator user ID |
created_by__username | string | New: Filter by creator username (exact match) |
created_by__username__icontains | string | New: Creator username contains |
modified_by | integer | New: Filter by modifier user ID |
modified_by__username__icontains | string | New: Modifier username contains |
Metadata Search
| Parameter | Type | Description |
|---|---|---|
metadata_search | string | New: 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 pagetotal: Total number of objects matching the filterspage: Current page numberpage_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
| Field | Type | Description |
|---|---|---|
filename | string | Change filename |
path | string | Change path (rename/move) |
metadata | object | Update 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:
- File extension (e.g.,
.jpg→image/jpeg) - Upload
Content-Typeheader (if provided) - Defaults to
application/octet-streamif unknown
Common MIME Types
| Extension | MIME Type |
|---|---|
.jpg, .jpeg | image/jpeg |
.png | image/png |
.gif | image/gif |
.svg | image/svg+xml |
.pdf | application/pdf |
.doc | application/msword |
.docx | application/vnd.openxmlformats-officedocument.wordprocessingml.document |
.xls | application/vnd.ms-excel |
.xlsx | application/vnd.openxmlformats-officedocument.spreadsheetml.sheet |
.txt | text/plain |
.csv | text/csv |
.mp4 | video/mp4 |
.mp3 | audio/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
-
Use File Path Prefixes for Filtering
?file__startswith=invoices/Efficient filtering by file path prefix.
-
Limit Result Sets
?page_size=20 -
Use Lightweight List Endpoint The list endpoint returns minimal fields for better performance.
Security
-
Validate on Client and Server
- Check file size before upload
- Validate MIME types
- Sanitize filenames
-
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}/`; -
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
1. Public Gallery with Private Drafts
# 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
2. Private Documents with Shared Links
# 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
visibilityisnull→ check the bucket's visibility - If
visibilityis"public"or"private"→ use that value
Best Practices
- Default to Inheritance: Leave
visibility: nullunless you need to override - Use Sparingly: Too many overrides can be confusing. Consider using separate buckets instead
- Document Overrides: Add metadata explaining why an object has a different visibility:
{
"visibility": "private",
"metadata": {
"reason": "Contains unreleased product information",
"expires": "2025-12-31"
}
} - 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:
- Write Operations Always Require Auth: Even for public objects, upload/delete/update operations require authentication and ownership
- Visibility != Permissions: Visibility controls read access. Write access is always restricted to the owner
- 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"'
Related Documentation
- Storage Quickstart - Getting started
- Buckets API Reference - Bucket operations and visibility
- Advanced Features - Search, copy, move, batch operations
- Storage Overview - Architecture and concepts