{"openapi":"3.1.0","info":{"title":"Aivaux File Storage API","version":"1.0.0","description":"Secure cloud file storage with AI features. Authenticate with session cookies or API keys (Bearer token).","contact":{"url":"https://aivaux.com"}},"servers":[{"url":"https://aivaux.com"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"API key obtained from POST /account/api-keys. Format: aiv_xxxxxxxxxxxx"},"sessionAuth":{"type":"apiKey","in":"cookie","name":"connect.sid","description":"Session cookie obtained by calling POST /auth/login"}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"string"}}},"File":{"type":"object","properties":{"id":{"type":"integer"},"user_id":{"type":"integer"},"original_name":{"type":"string"},"stored_name":{"type":"string"},"mime_type":{"type":"string"},"size_bytes":{"type":"integer"},"uploaded_at":{"type":"string","format":"date-time"},"folder_id":{"type":"integer","nullable":true},"tags":{"type":"string","nullable":true}}},"Folder":{"type":"object","properties":{"id":{"type":"integer"},"user_id":{"type":"integer"},"parent_id":{"type":"integer","nullable":true},"name":{"type":"string"},"created_at":{"type":"string","format":"date-time"}}}}},"paths":{"/auth/register":{"post":{"tags":["Auth"],"summary":"Create a new account","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["username","password","email"],"properties":{"username":{"type":"string","minLength":3,"maxLength":32,"pattern":"^[a-zA-Z0-9_]+$"},"password":{"type":"string","minLength":8},"email":{"type":"string","format":"email"}}}}}},"responses":{"201":{"description":"Account created and session started"},"400":{"description":"Validation error"},"409":{"description":"Username or email already taken"}}}},"/auth/login":{"post":{"tags":["Auth"],"summary":"Log in with username and password","description":"Returns a session cookie. For programmatic access, consider generating an API key via /account/api-keys instead.","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["username","password"],"properties":{"username":{"type":"string"},"password":{"type":"string"}}}}}},"responses":{"200":{"description":"Login successful, session cookie set"},"401":{"description":"Invalid credentials"}}}},"/auth/logout":{"post":{"tags":["Auth"],"summary":"End the current session","responses":{"200":{"description":"Logged out"}}}},"/auth/me":{"get":{"tags":["Auth"],"summary":"Get current user info","responses":{"200":{"description":"Current user details","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"integer"},"username":{"type":"string"},"is_premium":{"type":"boolean"},"premium_since":{"type":"string","nullable":true}}}}}},"401":{"description":"Not authenticated"}}}},"/account/profile":{"get":{"tags":["Account"],"summary":"Get account profile","responses":{"200":{"description":"Profile details","content":{"application/json":{"schema":{"type":"object","properties":{"username":{"type":"string"},"email":{"type":"string"},"created_at":{"type":"string"},"is_premium":{"type":"boolean"},"premium_since":{"type":"string","nullable":true}}}}}}}}},"/account/quota":{"get":{"tags":["Account"],"summary":"Get storage usage and quota","responses":{"200":{"description":"Storage quota info","content":{"application/json":{"schema":{"type":"object","properties":{"used_bytes":{"type":"integer"},"quota_bytes":{"type":"integer"},"file_count":{"type":"integer"}}}}}}}}},"/account/password":{"patch":{"tags":["Account"],"summary":"Change password","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["current_password","new_password"],"properties":{"current_password":{"type":"string"},"new_password":{"type":"string","minLength":8}}}}}},"responses":{"200":{"description":"Password updated"},"401":{"description":"Current password incorrect"}}}},"/account/api-keys":{"get":{"tags":["API Keys"],"summary":"List active API keys","responses":{"200":{"description":"Array of active API keys (prefix only, not full keys)","content":{"application/json":{"schema":{"type":"object","properties":{"keys":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"key_prefix":{"type":"string"},"label":{"type":"string"},"created_at":{"type":"string"},"last_used":{"type":"string","nullable":true}}}}}}}}}}},"post":{"tags":["API Keys"],"summary":"Generate a new API key","description":"Creates a new API key for programmatic access. The full key is returned ONLY in this response — save it immediately.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"label":{"type":"string","maxLength":64,"description":"A label to identify this key"}}}}}},"responses":{"201":{"description":"API key created. Save the key immediately — it cannot be retrieved again.","content":{"application/json":{"schema":{"type":"object","properties":{"key":{"type":"string","description":"Full API key (shown only once)"},"prefix":{"type":"string"},"label":{"type":"string"},"message":{"type":"string"}}}}}},"400":{"description":"Maximum key limit reached"}}}},"/account/api-keys/{id}":{"delete":{"tags":["API Keys"],"summary":"Revoke an API key","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Key revoked"},"404":{"description":"Key not found"}}}},"/account":{"delete":{"tags":["Account"],"summary":"Delete your account and all data","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["password"],"properties":{"password":{"type":"string"}}}}}},"responses":{"200":{"description":"Account deleted"},"401":{"description":"Incorrect password"}}}},"/files":{"get":{"tags":["Files"],"summary":"List files with optional search and filters","parameters":[{"name":"q","in":"query","schema":{"type":"string"},"description":"Search by filename"},{"name":"mime","in":"query","schema":{"type":"string"},"description":"Filter by MIME type prefix (e.g. image/)"},{"name":"folder_id","in":"query","schema":{"type":"string"},"description":"Filter by folder (use \"root\" for top-level)"},{"name":"size_min","in":"query","schema":{"type":"integer"},"description":"Min size in bytes"},{"name":"size_max","in":"query","schema":{"type":"integer"},"description":"Max size in bytes"},{"name":"date_from","in":"query","schema":{"type":"string"},"description":"Upload date from (YYYY-MM-DD)"},{"name":"date_to","in":"query","schema":{"type":"string"},"description":"Upload date to (YYYY-MM-DD)"}],"responses":{"200":{"description":"Array of files","content":{"application/json":{"schema":{"type":"object","properties":{"files":{"type":"array","items":{"$ref":"#/components/schemas/File"}}}}}}}}}},"/files/upload":{"post":{"tags":["Files"],"summary":"Upload files (multipart, up to 20 files, 100 MB each)","requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"files":{"type":"array","items":{"type":"string","format":"binary"}},"folder_id":{"type":"integer","description":"Destination folder ID (optional)"}}}}}},"responses":{"201":{"description":"Files uploaded"},"400":{"description":"No files provided"},"413":{"description":"Storage quota exceeded or file too large"}}}},"/files/download/{id}":{"get":{"tags":["Files"],"summary":"Download a file","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"File content (supports Range requests)"},"404":{"description":"File not found"}}}},"/files/{id}":{"put":{"tags":["Files"],"summary":"Replace file content (keep same file ID)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary"}}}}}},"responses":{"200":{"description":"File replaced"},"404":{"description":"File not found"},"413":{"description":"Storage quota exceeded"}}},"delete":{"tags":["Files"],"summary":"Soft-delete a file (move to trash)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"File moved to trash"},"404":{"description":"File not found"}}}},"/files/{id}/rename":{"patch":{"tags":["Files"],"summary":"Rename a file","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string"}}}}}},"responses":{"200":{"description":"File renamed"},"409":{"description":"Duplicate name in folder"}}}},"/files/{id}/move":{"patch":{"tags":["Files"],"summary":"Move a file to a different folder","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"folder_id":{"type":"integer","nullable":true,"description":"null = root"}}}}}},"responses":{"200":{"description":"File moved"},"404":{"description":"File or destination folder not found"}}}},"/folders":{"post":{"tags":["Folders"],"summary":"Create a folder","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"parent_id":{"type":"integer","nullable":true}}}}}},"responses":{"201":{"description":"Folder created"},"409":{"description":"Folder name already exists"}}}},"/folders/tree":{"get":{"tags":["Folders"],"summary":"Get full folder tree","responses":{"200":{"description":"Recursive folder structure","content":{"application/json":{"schema":{"type":"object","properties":{"folders":{"type":"array","items":{"$ref":"#/components/schemas/Folder"}}}}}}}}}},"/folders/{id}/contents":{"get":{"tags":["Folders"],"summary":"List subfolders and files in a folder","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Folder ID or \"root\""}],"responses":{"200":{"description":"Folder contents with subfolders and files"}}}},"/folders/{id}":{"delete":{"tags":["Folders"],"summary":"Soft-delete a folder and its contents","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Folder deleted"},"404":{"description":"Folder not found"}}}},"/folders/{id}/rename":{"patch":{"tags":["Folders"],"summary":"Rename a folder","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string"}}}}}},"responses":{"200":{"description":"Folder renamed"},"409":{"description":"Duplicate name"}}}},"/trash":{"get":{"tags":["Trash"],"summary":"List all trashed files and folders","responses":{"200":{"description":"Trashed items"}}},"delete":{"tags":["Trash"],"summary":"Empty trash (permanent delete all)","responses":{"200":{"description":"Trash emptied"}}}},"/trash/files/{id}/restore":{"post":{"tags":["Trash"],"summary":"Restore a trashed file","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"File restored"}}}},"/trash/files/{id}":{"delete":{"tags":["Trash"],"summary":"Permanently delete a trashed file","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"File permanently deleted"}}}},"/share":{"post":{"tags":["Share"],"summary":"Create a share link for a file","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["file_id"],"properties":{"file_id":{"type":"integer"},"expires_at":{"type":"string","format":"date-time","description":"Optional expiry"},"password":{"type":"string","description":"Optional password protection"}}}}}},"responses":{"201":{"description":"Share link created"},"404":{"description":"File not found"}}},"get":{"tags":["Share"],"summary":"List all your share links","responses":{"200":{"description":"Array of share links"}}}},"/share/{id}":{"delete":{"tags":["Share"],"summary":"Revoke a share link","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Link revoked"}}}}}}