Storage
File storage powered by Cloudflare R2. Upload, download, organize with buckets, transform images on-the-fly, and use resumable uploads.
Upload a File
POST /storage/project/{project_id}/upload
curl -X POST https://api.zmesh.in/storage/project/{project_id}/upload \
-H "Authorization: Bearer <token>" \
-F "file=@photo.jpg" \
-F "path=avatars"
// Response
{
"name": "photo.jpg",
"key": "proj_abc/avatars/photo.jpg",
"size": 204800,
"content_type": "image/jpeg",
"url": "https://assets.zmesh.in/proj_abc/avatars/photo.jpg"
}List Files
GET /storage/project/{project_id}/files?prefix=avatars
// Response
{
"files": [
{ "name": "photo.jpg", "size": 204800, "last_modified": "..." }
],
"folders": [
{ "name": "thumbnails", "is_folder": true }
]
}Delete File
DELETE /storage/project/{project_id}/files/{path}
Create Folder
POST /storage/project/{project_id}/folder?folder_name=images&path=uploads
Rename File
POST /storage/project/{project_id}/rename?old_path=photo.jpg&new_name=avatar.jpg
Presigned Upload URL
Generate a presigned URL for direct client-side uploads to R2 (bypasses server).
POST /storage/project/{project_id}/presigned-upload?filename=video.mp4&content_type=video/mp4
// Response
{
"upload_url": "https://...r2.cloudflarestorage.com/...",
"key": "proj_abc/video.mp4",
"method": "PUT",
"expires_in": 3600,
"headers": { "Content-Type": "video/mp4" }
}Resumable (Multipart) Uploads
For large files, use chunked uploads:
| Step | Endpoint | Description |
|---|---|---|
| 1 | POST /upload/init | Initialize upload → returns upload_id |
| 2 | PUT /upload/{upload_id} | Upload each chunk (part_number, file) |
| 3 | POST /upload/{upload_id}/complete | Finalize with parts array |
| ❌ | DELETE /upload/{upload_id} | Abort upload |
Storage Buckets
Organize files into buckets with per-bucket policies (public/private, size limits, allowed types).
// Create a bucket
POST /storage/project/{project_id}/buckets
{
"name": "avatars",
"public": true,
"file_size_limit": 5242880,
"allowed_types": ["image/jpeg", "image/png", "image/webp"]
}
// Upload to bucket
POST /storage/project/{project_id}/buckets/avatars/upload
// List bucket files
GET /storage/project/{project_id}/buckets/avatars/files
// Delete bucket
DELETE /storage/project/{project_id}/buckets/avatarsImage Transforms
On-the-fly image resizing, cropping, and format conversion.
GET /storage/project/{project_id}/transform/{path}
// Resize to 200x200, convert to WebP
GET /storage/project/{project_id}/transform/photo.jpg
?width=200&height=200&format=webp&quality=80&fit=cover| Param | Description |
|---|---|
| width | Target width (1–4096) |
| height | Target height (1–4096) |
| quality | Output quality 1–100 (default 80) |
| format | webp, png, jpeg |
| fit | cover, contain, fill |
Access Policies
// Get file access
GET /storage/project/{project_id}/access/{path}
// Set file access
PUT /storage/project/{project_id}/access/{path}
{ "access": "public" } // public | private | authenticatedSigned Download URLs
POST /storage/project/{project_id}/signed-url?path=secret.pdf&expires=3600
// Response
{ "signed_url": "https://...?X-Amz-Signature=...", "expires_in": 3600 }File Info & Download
// Get file metadata
GET /storage/project/{project_id}/info/{path}
// Download file (with Content-Disposition)
GET /storage/project/{project_id}/download/{path}
// Copy file
POST /storage/project/{project_id}/copy?src_path=a.jpg&dest_path=b.jpg
// Move file
POST /storage/project/{project_id}/move?src_path=a.jpg&dest_path=folder/a.jpg