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:

StepEndpointDescription
1POST /upload/initInitialize upload → returns upload_id
2PUT /upload/{upload_id}Upload each chunk (part_number, file)
3POST /upload/{upload_id}/completeFinalize 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/avatars

Image 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
ParamDescription
widthTarget width (1–4096)
heightTarget height (1–4096)
qualityOutput quality 1–100 (default 80)
formatwebp, png, jpeg
fitcover, 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 | authenticated

Signed 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