{"openapi":"3.0.0","info":{"title":"ProjectFlow API","version":"1.0.0","description":"Backend for the Smart Project & Task Collaboration System. All endpoints are testable via Swagger UI at /docs."},"servers":[{"url":"http://localhost:8000","description":"Production"}],"components":{"schemas":{"CsvFile":{"type":"string"},"LogoutResult":{"type":"object","properties":{"loggedOut":{"type":"boolean"}},"required":["loggedOut"]},"LogoutBody":{"type":"object","properties":{"refreshToken":{"type":"string"}}},"DeleteResult":{"type":"object","properties":{"deleted":{"type":"boolean"}},"required":["deleted"]},"RemoveResult":{"type":"object","properties":{"removed":{"type":"boolean"}},"required":["removed"]},"CreateTaskWithAttachments":{"type":"object","properties":{"title":{"type":"string"},"description":{"type":"string"},"status":{"type":"string"},"priority":{"type":"string"},"assigneeId":{"type":"string"},"dueDate":{"type":"string"},"labels":{"type":"string"},"file":{"type":"string","format":"binary"}},"required":["title"]},"UnreadCount":{"type":"object","properties":{"count":{"type":"integer"}},"required":["count"]},"NotificationResult":{"type":"object","nullable":true,"properties":{"id":{"type":"string"},"userId":{"type":"string"},"type":{"type":"string"},"title":{"type":"string"},"message":{"type":"string"},"data":{"nullable":true},"read":{"type":"boolean"},"createdAt":{"type":"string"}},"required":["id","userId","type","title","message","read","createdAt"]},"AllReadResult":{"type":"object","properties":{"updated":{"type":"integer"}},"required":["updated"]}},"parameters":{}},"paths":{"/health":{"get":{"tags":["System"],"summary":"Health & system status","description":"Public endpoint. Returns API status, database status, runtime, timestamp, and version.","responses":{"200":{"description":"Service is healthy","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"object","properties":{"status":{"type":"string","example":"ok"},"api":{"type":"string","example":"running"},"database":{"type":"string","example":"connected"},"runtime":{"type":"string","example":"Deno 2.8.1"},"timestamp":{"type":"string","example":"2025-01-01T00:00:00.000Z"},"version":{"type":"string","example":"1.0.0"},"environment":{"type":"string","example":"development"}},"required":["status","api","database","runtime","timestamp","version","environment"]}},"required":["success","message","data"]}}}},"503":{"description":"Service is degraded","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"object","properties":{"status":{"type":"string","example":"ok"},"api":{"type":"string","example":"running"},"database":{"type":"string","example":"connected"},"runtime":{"type":"string","example":"Deno 2.8.1"},"timestamp":{"type":"string","example":"2025-01-01T00:00:00.000Z"},"version":{"type":"string","example":"1.0.0"},"environment":{"type":"string","example":"development"}},"required":["status","api","database","runtime","timestamp","version","environment"]}},"required":["success","message","data"]}}}}}}},"/readyz":{"get":{"tags":["System"],"summary":"Readiness probe (DB-checked)","description":"Deno Deploy uses this to know when the isolate is ready to receive traffic. Returns 200 only after the database is reachable.","responses":{"200":{"description":"Service is ready","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"object","properties":{"status":{"type":"string","example":"ok"},"api":{"type":"string","example":"running"},"database":{"type":"string","example":"connected"},"runtime":{"type":"string","example":"Deno 2.8.1"},"timestamp":{"type":"string","example":"2025-01-01T00:00:00.000Z"},"version":{"type":"string","example":"1.0.0"},"environment":{"type":"string","example":"development"}},"required":["status","api","database","runtime","timestamp","version","environment"]}},"required":["success","message","data"]}}}},"503":{"description":"Service is not ready (db unreachable)","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"object","properties":{"status":{"type":"string","example":"ok"},"api":{"type":"string","example":"running"},"database":{"type":"string","example":"connected"},"runtime":{"type":"string","example":"Deno 2.8.1"},"timestamp":{"type":"string","example":"2025-01-01T00:00:00.000Z"},"version":{"type":"string","example":"1.0.0"},"environment":{"type":"string","example":"development"}},"required":["status","api","database","runtime","timestamp","version","environment"]}},"required":["success","message","data"]}}}}}}},"/api/dashboard":{"get":{"tags":["System"],"summary":"Personal dashboard analytics","description":"Returns project counts, task counts by status/priority, overdue, recent activity, etc.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Dashboard","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"type":"object","properties":{"projects":{"type":"object","properties":{"total":{"type":"integer"},"active":{"type":"integer"},"completed":{"type":"integer"},"onHold":{"type":"integer"},"archived":{"type":"integer"}},"required":["total","active","completed","onHold","archived"]},"tasks":{"type":"object","properties":{"total":{"type":"integer"},"byStatus":{"type":"object","properties":{"TODO":{"type":"integer"},"IN_PROGRESS":{"type":"integer"},"COMPLETED":{"type":"integer"}},"required":["TODO","IN_PROGRESS","COMPLETED"]},"byPriority":{"type":"object","properties":{"LOW":{"type":"integer"},"MEDIUM":{"type":"integer"},"HIGH":{"type":"integer"}},"required":["LOW","MEDIUM","HIGH"]},"overdue":{"type":"integer"},"completed":{"type":"integer"}},"required":["total","byStatus","byPriority","overdue","completed"]},"mine":{"type":"object","properties":{"assignedOpen":{"type":"integer"},"byStatus":{"type":"object","properties":{"TODO":{"type":"integer"},"IN_PROGRESS":{"type":"integer"},"COMPLETED":{"type":"integer"}},"required":["TODO","IN_PROGRESS","COMPLETED"]},"overdue":{"type":"integer"}},"required":["assignedOpen","byStatus","overdue"]},"notifications":{"type":"object","properties":{"unread":{"type":"integer"}},"required":["unread"]},"recentActivity":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"actorId":{"type":"string"},"action":{"type":"string"},"entityType":{"type":"string"},"entityId":{"type":"string"},"projectId":{"type":"string","nullable":true},"taskId":{"type":"string","nullable":true},"metadata":{"nullable":true},"createdAt":{"type":"string"},"actor":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"username":{"type":"string"},"avatar":{"type":"string","nullable":true}},"required":["id","name","username"]}},"required":["id","actorId","action","entityType","entityId","createdAt","actor"]}}},"required":["projects","tasks","mine","notifications","recentActivity"]}},"required":["success","message","data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}}},"/api/projects/:projectId/analytics":{"get":{"tags":["System"],"summary":"Per-project analytics","description":"Detailed analytics for a single project: status/priority breakdown, member workload, team productivity.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","pattern":"^[a-fA-F0-9]{24}$"},"required":true,"name":"projectId","in":"path"}],"responses":{"200":{"description":"Project analytics","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"type":"object","properties":{"project":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"color":{"type":"string","nullable":true},"status":{"type":"string","enum":["ACTIVE","COMPLETED","ON_HOLD","ARCHIVED"]},"progress":{"type":"integer"}},"required":["id","name","status","progress"]},"tasks":{"type":"object","properties":{"total":{"type":"integer"},"done":{"type":"integer"},"overdue":{"type":"integer"},"byStatus":{"type":"object","properties":{"TODO":{"type":"integer"},"IN_PROGRESS":{"type":"integer"},"COMPLETED":{"type":"integer"}},"required":["TODO","IN_PROGRESS","COMPLETED"]},"byPriority":{"type":"object","properties":{"LOW":{"type":"integer"},"MEDIUM":{"type":"integer"},"HIGH":{"type":"integer"}},"required":["LOW","MEDIUM","HIGH"]}},"required":["total","done","overdue","byStatus","byPriority"]},"members":{"type":"array","items":{"type":"object","properties":{"user":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"username":{"type":"string"},"avatar":{"type":"string","nullable":true}},"required":["id","name","username"]},"role":{"type":"string","enum":["ADMIN","PROJECT_MANAGER","TEAM_MEMBER","VIEWER"]},"totalTasks":{"type":"integer"},"openTasks":{"type":"integer"},"doneTasks":{"type":"integer"}},"required":["user","role","totalTasks","openTasks","doneTasks"]}},"teamProductivity":{"type":"object","properties":{"completionRate":{"type":"integer"},"averageTasksPerMember":{"type":"integer"},"activeMembers":{"type":"integer"}},"required":["completionRate","averageTasksPerMember","activeMembers"]}},"required":["project","tasks","members","teamProductivity"]}},"required":["success","message","data"]}}}},"403":{"description":"Not a member","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"404":{"description":"Project not found","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}}},"/api/projects/:projectId/export/tasks.csv":{"get":{"tags":["System"],"summary":"Export project tasks as CSV","description":"Returns a CSV file with all tasks in the project. OWNER or MANAGER only.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","pattern":"^[a-fA-F0-9]{24}$"},"required":true,"name":"projectId","in":"path"}],"responses":{"200":{"description":"CSV file download","content":{"text/csv":{"schema":{"$ref":"#/components/schemas/CsvFile"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"404":{"description":"Project not found","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}}},"/api/analytics/dashboard":{"get":{"tags":["Analytics"],"summary":"Workspace-wide chart payload (KPI, status, priority, trend, productivity)","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Dashboard charts","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"type":"object","properties":{"kpi":{"type":"object","properties":{"totalProjects":{"type":"integer"},"totalTasks":{"type":"integer"},"completedTasks":{"type":"integer"},"pendingTasks":{"type":"integer"},"overdueTasks":{"type":"integer"},"completionRate":{"type":"integer"}},"required":["totalProjects","totalTasks","completedTasks","pendingTasks","overdueTasks","completionRate"]},"byPriority":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"label":{"type":"string"},"value":{"type":"integer"},"color":{"type":"string"}},"required":["name","label","value","color"]}},"byStatus":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"label":{"type":"string"},"value":{"type":"integer"},"color":{"type":"string"}},"required":["name","label","value","color"]}},"trend":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string"},"label":{"type":"string"},"created":{"type":"integer"},"completed":{"type":"integer"}},"required":["date","label","created","completed"]}},"productivity":{"type":"array","items":{"type":"object","properties":{"userId":{"type":"string"},"name":{"type":"string"},"avatar":{"type":"string","nullable":true},"completed":{"type":"integer"},"inProgress":{"type":"integer"},"total":{"type":"integer"}},"required":["userId","name","avatar","completed","inProgress","total"]}},"comparison":{"type":"array","items":{"type":"object","properties":{"label":{"type":"string"},"completed":{"type":"integer"},"overdue":{"type":"integer"},"pending":{"type":"integer"}},"required":["label","completed","overdue","pending"]}},"generatedAt":{"type":"string","format":"date-time"}},"required":["kpi","byPriority","byStatus","trend","productivity","comparison","generatedAt"]}},"required":["success","message","data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}}},"/api/analytics/project/:projectId":{"get":{"tags":["Analytics"],"summary":"Single-project chart payload","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","pattern":"^[a-fA-F0-9]{24}$"},"required":true,"name":"projectId","in":"path"}],"responses":{"200":{"description":"Project charts","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"type":"object","properties":{"kpi":{"type":"object","properties":{"totalProjects":{"type":"integer"},"totalTasks":{"type":"integer"},"completedTasks":{"type":"integer"},"pendingTasks":{"type":"integer"},"overdueTasks":{"type":"integer"},"completionRate":{"type":"integer"}},"required":["totalProjects","totalTasks","completedTasks","pendingTasks","overdueTasks","completionRate"]},"byPriority":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"label":{"type":"string"},"value":{"type":"integer"},"color":{"type":"string"}},"required":["name","label","value","color"]}},"byStatus":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"label":{"type":"string"},"value":{"type":"integer"},"color":{"type":"string"}},"required":["name","label","value","color"]}},"trend":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string"},"label":{"type":"string"},"created":{"type":"integer"},"completed":{"type":"integer"}},"required":["date","label","created","completed"]}},"productivity":{"type":"array","items":{"type":"object","properties":{"userId":{"type":"string"},"name":{"type":"string"},"avatar":{"type":"string","nullable":true},"completed":{"type":"integer"},"inProgress":{"type":"integer"},"total":{"type":"integer"}},"required":["userId","name","avatar","completed","inProgress","total"]}},"comparison":{"type":"array","items":{"type":"object","properties":{"label":{"type":"string"},"completed":{"type":"integer"},"overdue":{"type":"integer"},"pending":{"type":"integer"}},"required":["label","completed","overdue","pending"]}},"generatedAt":{"type":"string","format":"date-time"}},"required":["kpi","byPriority","byStatus","trend","productivity","comparison","generatedAt"]}},"required":["success","message","data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"403":{"description":"Not a project member","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"404":{"description":"Project not found","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}}},"/api/auth/signup":{"post":{"tags":["Auth"],"summary":"Sign up a new user","description":"Create a new user account, hash the password, and return JWT tokens.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email","example":"owner@example.com"},"password":{"type":"string","minLength":8,"example":"Password123!"},"name":{"type":"string","example":"Olivia Owner"},"username":{"type":"string","minLength":3,"example":"olivia"}},"required":["email","password","name"]}}}},"responses":{"201":{"description":"User created","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"type":"object","properties":{"user":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string","format":"email"},"username":{"type":"string"},"name":{"type":"string"},"avatar":{"type":"string","nullable":true},"bio":{"type":"string","nullable":true},"status":{"type":"string"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","email","username","name","status","createdAt","updatedAt"]},"accessToken":{"type":"string"},"refreshToken":{"type":"string"},"refreshExpiresIn":{"type":"integer"}},"required":["user","accessToken","refreshToken","refreshExpiresIn"]}},"required":["success","message","data"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"409":{"description":"Email already in use","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}}},"/api/auth/login":{"post":{"tags":["Auth"],"summary":"Log in","description":"Authenticate with email and password and return JWT access + refresh tokens.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email","example":"owner@example.com"},"password":{"type":"string","minLength":1,"example":"Password123!"}},"required":["email","password"]}}}},"responses":{"200":{"description":"Login successful","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"type":"object","properties":{"user":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string","format":"email"},"username":{"type":"string"},"name":{"type":"string"},"avatar":{"type":"string","nullable":true},"bio":{"type":"string","nullable":true},"status":{"type":"string"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","email","username","name","status","createdAt","updatedAt"]},"accessToken":{"type":"string"},"refreshToken":{"type":"string"},"refreshExpiresIn":{"type":"integer"}},"required":["user","accessToken","refreshToken","refreshExpiresIn"]}},"required":["success","message","data"]}}}},"401":{"description":"Invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}}},"/api/auth/refresh":{"post":{"tags":["Auth"],"summary":"Refresh access token","description":"Exchange a valid refresh token for a new access token (and rotate the refresh token).","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"refreshToken":{"type":"string","example":"eyJhbGciOi..."}},"required":["refreshToken"]}}}},"responses":{"200":{"description":"Token refreshed","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"type":"object","properties":{"accessToken":{"type":"string"},"refreshToken":{"type":"string"},"refreshExpiresIn":{"type":"integer"}},"required":["accessToken","refreshToken","refreshExpiresIn"]}},"required":["success","message","data"]}}}},"401":{"description":"Invalid or expired refresh token","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}}},"/api/auth/logout":{"post":{"tags":["Auth"],"summary":"Log out","description":"Revoke the provided refresh token (or all tokens if none provided).","security":[{"bearerAuth":[]}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LogoutBody"}}}},"responses":{"200":{"description":"Logged out","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/LogoutResult"}},"required":["success","message","data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}}},"/api/auth/me":{"get":{"tags":["Auth"],"summary":"Get current user","description":"Return the profile of the currently authenticated user.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Current user","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string","format":"email"},"username":{"type":"string"},"name":{"type":"string"},"avatar":{"type":"string","nullable":true},"bio":{"type":"string","nullable":true},"status":{"type":"string"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","email","username","name","status","createdAt","updatedAt"]}},"required":["success","message","data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}}},"/api/auth/google":{"get":{"tags":["Auth"],"summary":"Google OAuth login","description":"Redirect to Google OAuth consent screen.","responses":{"302":{"description":"Redirect to Google"}}}},"/api/auth/google/callback":{"get":{"tags":["Auth"],"summary":"Google OAuth callback","description":"Handle Google OAuth callback, exchange code for tokens.","responses":{"302":{"description":"Redirect to frontend with tokens"}}}},"/api/auth/github":{"get":{"tags":["Auth"],"summary":"GitHub OAuth login","description":"Redirect to GitHub OAuth consent screen.","responses":{"302":{"description":"Redirect to GitHub"}}}},"/api/auth/github/callback":{"get":{"tags":["Auth"],"summary":"GitHub OAuth callback","description":"Handle GitHub OAuth callback, exchange code for tokens.","responses":{"302":{"description":"Redirect to frontend with tokens"}}}},"/api/users/me":{"patch":{"tags":["Users"],"summary":"Update my profile","description":"Update name, username, avatar, or bio for the current user.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1},"username":{"type":"string","minLength":3},"avatar":{"type":"string","nullable":true,"pattern":"^(https?:\\/\\/.+|\\/.*)"},"bio":{"type":"string","nullable":true}}}}}},"responses":{"200":{"description":"Profile updated","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string","format":"email"},"username":{"type":"string"},"name":{"type":"string"},"avatar":{"type":"string","nullable":true},"bio":{"type":"string","nullable":true},"status":{"type":"string"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","email","username","name","status","createdAt","updatedAt"]}},"required":["success","message","data"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"409":{"description":"Username already in use","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}}},"/api/users/search":{"get":{"tags":["Users"],"summary":"Search users by email, name, or username","description":"Used to look up users to invite to projects. Excludes the current user.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":80,"example":"olivia"},"required":false,"name":"q","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":50,"default":20},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Matching users","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string","format":"email"},"username":{"type":"string"},"name":{"type":"string"},"avatar":{"type":"string","nullable":true},"bio":{"type":"string","nullable":true},"status":{"type":"string"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","email","username","name","status","createdAt","updatedAt"]}},"total":{"type":"integer"}},"required":["items","total"]}},"required":["success","message","data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}}},"/api/users/:userId":{"get":{"tags":["Users"],"summary":"Get a user by id","description":"Returns the public profile of a single user.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","pattern":"^[a-fA-F0-9]{24}$"},"required":true,"name":"userId","in":"path"}],"responses":{"200":{"description":"User profile","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string","format":"email"},"username":{"type":"string"},"name":{"type":"string"},"avatar":{"type":"string","nullable":true},"bio":{"type":"string","nullable":true},"status":{"type":"string"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","email","username","name","status","createdAt","updatedAt"]}},"required":["success","message","data"]}}}},"404":{"description":"User not found","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}}},"/api/users/:userId/role":{"patch":{"tags":["Users"],"summary":"Change a user's account role (admin only)","description":"Promote or demote a user to PROJECT_MANAGER, TEAM_MEMBER, or VIEWER. Only admins can call this. Admins cannot demote themselves or other admins through this endpoint.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","pattern":"^[a-fA-F0-9]{24}$"},"required":true,"name":"userId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"role":{"type":"string","enum":["PROJECT_MANAGER","TEAM_MEMBER","VIEWER"]}},"required":["role"]}}}},"responses":{"200":{"description":"Role updated","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string","format":"email"},"username":{"type":"string"},"name":{"type":"string"},"avatar":{"type":"string","nullable":true},"bio":{"type":"string","nullable":true},"status":{"type":"string"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","email","username","name","status","createdAt","updatedAt"]}},"required":["success","message","data"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"403":{"description":"Forbidden (non-admin or self/other-admin target)","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"404":{"description":"User not found","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}}},"/api/users/:userId/avatar":{"get":{"tags":["Users"],"summary":"Stream a user's avatar image (DB storage only)","description":"Returns the avatar bytes stored in User.avatarData. Returns 404 if the user has no DB-stored avatar (e.g. they use an external URL).","parameters":[{"schema":{"type":"string","pattern":"^[a-fA-F0-9]{24}$"},"required":true,"name":"userId","in":"path"}],"responses":{"200":{"description":"Avatar image bytes","content":{"image/*":{"schema":{"type":"string","format":"binary"}}}},"404":{"description":"No DB-stored avatar for this user","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}}},"/api/projects":{"get":{"tags":["Projects"],"summary":"List my projects","description":"Returns projects the caller owns or is a member of. Supports filtering and pagination.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","enum":["ACTIVE","COMPLETED","ON_HOLD","ARCHIVED"]},"required":false,"name":"status","in":"query"},{"schema":{"type":"string","maxLength":100},"required":false,"name":"search","in":"query"},{"schema":{"type":"integer","minimum":1,"default":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":20},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string"},"required":false,"name":"sort","in":"query"}],"responses":{"200":{"description":"List of projects","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string","nullable":true},"color":{"type":"string","nullable":true},"status":{"type":"string","enum":["ACTIVE","COMPLETED","ON_HOLD","ARCHIVED"]},"ownerId":{"type":"string"},"progress":{"type":"integer"},"taskCount":{"type":"integer"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"_count":{"type":"object","properties":{"tasks":{"type":"integer"},"members":{"type":"integer"}},"required":["tasks","members"]},"owner":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"username":{"type":"string"},"avatar":{"type":"string","nullable":true}},"required":["id","name","username"]}},"required":["id","name","status","ownerId","progress","taskCount","createdAt","updatedAt"]}},"pagination":{"type":"object","properties":{"page":{"type":"integer"},"limit":{"type":"integer"},"total":{"type":"integer"},"totalPages":{"type":"integer"},"hasNext":{"type":"boolean"},"hasPrev":{"type":"boolean"}},"required":["page","limit","total","totalPages","hasNext","hasPrev"]}},"required":["items","pagination"]}},"required":["success","message","data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}},"post":{"tags":["Projects"],"summary":"Create a new project","description":"The caller becomes the OWNER and is added as the first member.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"example":"Q4 Marketing Launch"},"description":{"type":"string","example":"Cross-team launch plan"},"color":{"type":"string","example":"#10b981"},"startDate":{"type":"string","nullable":true,"format":"date-time"},"deadline":{"type":"string","nullable":true,"format":"date-time"}},"required":["name"]}}}},"responses":{"201":{"description":"Project created","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"type":"object","properties":{}}},"required":["success","message","data"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}}},"/api/projects/:projectId":{"get":{"tags":["Projects"],"summary":"Get project details","description":"Returns the project with owner, members, progress, and task statistics.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","pattern":"^[a-fA-F0-9]{24}$"},"required":true,"name":"projectId","in":"path"}],"responses":{"200":{"description":"Project detail","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string","nullable":true},"color":{"type":"string","nullable":true},"status":{"type":"string","enum":["ACTIVE","COMPLETED","ON_HOLD","ARCHIVED"]},"ownerId":{"type":"string"},"owner":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"username":{"type":"string"},"avatar":{"type":"string","nullable":true},"email":{"type":"string","format":"email"}},"required":["id","name","username","email"]},"members":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"role":{"type":"string","enum":["ADMIN","PROJECT_MANAGER","TEAM_MEMBER","VIEWER"]},"joinedAt":{"type":"string"},"user":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"username":{"type":"string"},"email":{"type":"string","format":"email"},"avatar":{"type":"string","nullable":true}},"required":["id","name","username","email"]}},"required":["id","role","joinedAt","user"]}},"progress":{"type":"integer"},"taskStats":{"type":"object","properties":{"total":{"type":"integer"},"todo":{"type":"integer"},"inProgress":{"type":"integer"},"review":{"type":"integer"},"done":{"type":"integer"}},"required":["total","todo","inProgress","review","done"]},"startDate":{"type":"string","nullable":true},"deadline":{"type":"string","nullable":true},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","status","ownerId","owner","members","progress","taskStats","createdAt","updatedAt"]}},"required":["success","message","data"]}}}},"403":{"description":"Not a member","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"404":{"description":"Project not found","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}},"patch":{"tags":["Projects"],"summary":"Update a project","description":"Update name/description/color/status. Requires OWNER or MANAGER.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","pattern":"^[a-fA-F0-9]{24}$"},"required":true,"name":"projectId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1},"description":{"type":"string","nullable":true},"color":{"type":"string"},"status":{"type":"string","enum":["ACTIVE","COMPLETED","ON_HOLD","ARCHIVED"]},"startDate":{"type":"string","nullable":true,"format":"date-time"},"deadline":{"type":"string","nullable":true,"format":"date-time"}}}}}},"responses":{"200":{"description":"Project updated","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"type":"object","properties":{}}},"required":["success","message","data"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"404":{"description":"Project not found","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}},"delete":{"tags":["Projects"],"summary":"Delete a project","description":"Permanently delete a project and all its data. OWNER only.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","pattern":"^[a-fA-F0-9]{24}$"},"required":true,"name":"projectId","in":"path"}],"responses":{"200":{"description":"Project deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/DeleteResult"}},"required":["success","message","data"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"404":{"description":"Project not found","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}}},"/api/projects/:projectId/archive":{"post":{"tags":["Projects"],"summary":"Archive a project","description":"Mark the project as ARCHIVED. OWNER only.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","pattern":"^[a-fA-F0-9]{24}$"},"required":true,"name":"projectId","in":"path"}],"responses":{"200":{"description":"Project archived","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"type":"object","properties":{}}},"required":["success","message","data"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"404":{"description":"Project not found","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}}},"/api/projects/:projectId/members":{"get":{"tags":["Projects"],"summary":"List project members","description":"Returns all members including the OWNER.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","pattern":"^[a-fA-F0-9]{24}$"},"required":true,"name":"projectId","in":"path"}],"responses":{"200":{"description":"Members","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"projectId":{"type":"string"},"userId":{"type":"string"},"role":{"type":"string","enum":["ADMIN","PROJECT_MANAGER","TEAM_MEMBER","VIEWER"]},"joinedAt":{"type":"string"},"user":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"username":{"type":"string"},"email":{"type":"string","format":"email"},"avatar":{"type":"string","nullable":true}},"required":["id","name","username","email"]}},"required":["id","projectId","userId","role","joinedAt","user"]}},"total":{"type":"integer"}},"required":["items","total"]}},"required":["success","message","data"]}}}},"403":{"description":"Not a member","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"404":{"description":"Project not found","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}}},"/api/projects/:projectId/invitations":{"post":{"tags":["Projects"],"summary":"Invite a member to a project","description":"Send an invitation by email. Requires OWNER or MANAGER. If the user already has an account they are notified immediately.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","pattern":"^[a-fA-F0-9]{24}$"},"required":true,"name":"projectId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email","example":"teammate@example.com"},"role":{"type":"string","enum":["ADMIN","PROJECT_MANAGER","TEAM_MEMBER","VIEWER"],"default":"TEAM_MEMBER"},"message":{"type":"string"}},"required":["email"]}}}},"responses":{"201":{"description":"Invitation sent","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string"},"projectId":{"type":"string"},"email":{"type":"string","format":"email"},"role":{"type":"string","enum":["ADMIN","PROJECT_MANAGER","TEAM_MEMBER","VIEWER"]},"status":{"type":"string"},"invitedById":{"type":"string"},"invitedUserId":{"type":"string","nullable":true},"expiresAt":{"type":"string"},"message":{"type":"string","nullable":true},"createdAt":{"type":"string"},"project":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"color":{"type":"string","nullable":true}},"required":["id","name"]},"invitedBy":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"username":{"type":"string"}},"required":["id","name","username"]}},"required":["id","projectId","email","role","status","invitedById","expiresAt","createdAt"]}},"required":["success","message","data"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"404":{"description":"Project not found","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"409":{"description":"Already a member","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}}},"/api/invitations/:invitationId/accept":{"post":{"tags":["Projects"],"summary":"Accept an invitation","description":"The caller accepts the invitation and becomes a member of the project.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","pattern":"^[a-fA-F0-9]{24}$"},"required":true,"name":"invitationId","in":"path"}],"responses":{"200":{"description":"Invitation accepted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string"},"projectId":{"type":"string"},"email":{"type":"string","format":"email"},"role":{"type":"string","enum":["ADMIN","PROJECT_MANAGER","TEAM_MEMBER","VIEWER"]},"status":{"type":"string"},"invitedById":{"type":"string"},"invitedUserId":{"type":"string","nullable":true},"expiresAt":{"type":"string"},"message":{"type":"string","nullable":true},"createdAt":{"type":"string"},"project":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"color":{"type":"string","nullable":true}},"required":["id","name"]},"invitedBy":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"username":{"type":"string"}},"required":["id","name","username"]}},"required":["id","projectId","email","role","status","invitedById","expiresAt","createdAt"]}},"required":["success","message","data"]}}}},"400":{"description":"Invalid or expired invitation","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"403":{"description":"Not your invitation","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"404":{"description":"Invitation not found","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}}},"/api/invitations/:invitationId/reject":{"post":{"tags":["Projects"],"summary":"Reject an invitation","description":"The caller declines the invitation.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","pattern":"^[a-fA-F0-9]{24}$"},"required":true,"name":"invitationId","in":"path"}],"responses":{"200":{"description":"Invitation rejected","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string"},"projectId":{"type":"string"},"email":{"type":"string","format":"email"},"role":{"type":"string","enum":["ADMIN","PROJECT_MANAGER","TEAM_MEMBER","VIEWER"]},"status":{"type":"string"},"invitedById":{"type":"string"},"invitedUserId":{"type":"string","nullable":true},"expiresAt":{"type":"string"},"message":{"type":"string","nullable":true},"createdAt":{"type":"string"},"project":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"color":{"type":"string","nullable":true}},"required":["id","name"]},"invitedBy":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"username":{"type":"string"}},"required":["id","name","username"]}},"required":["id","projectId","email","role","status","invitedById","expiresAt","createdAt"]}},"required":["success","message","data"]}}}},"400":{"description":"Invalid invitation","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"403":{"description":"Not your invitation","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"404":{"description":"Invitation not found","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}}},"/api/projects/:projectId/members/:memberId/role":{"patch":{"tags":["Projects"],"summary":"Change a member's role","description":"OWNER or MANAGER can change roles. Only OWNER can promote to MANAGER.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","pattern":"^[a-fA-F0-9]{24}$"},"required":true,"name":"projectId","in":"path"},{"schema":{"type":"string","pattern":"^[a-fA-F0-9]{24}$"},"required":true,"name":"memberId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"role":{"type":"string","enum":["ADMIN","PROJECT_MANAGER","TEAM_MEMBER","VIEWER"]}},"required":["role"]}}}},"responses":{"200":{"description":"Role updated","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string"},"projectId":{"type":"string"},"userId":{"type":"string"},"role":{"type":"string","enum":["ADMIN","PROJECT_MANAGER","TEAM_MEMBER","VIEWER"]},"joinedAt":{"type":"string"},"user":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"username":{"type":"string"},"email":{"type":"string","format":"email"},"avatar":{"type":"string","nullable":true}},"required":["id","name","username","email"]}},"required":["id","projectId","userId","role","joinedAt","user"]}},"required":["success","message","data"]}}}},"400":{"description":"Cannot change OWNER","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"404":{"description":"Member not found","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}}},"/api/projects/:projectId/members/:memberId":{"delete":{"tags":["Projects"],"summary":"Remove a project member","description":"OWNER or MANAGER can remove a member (with rank restrictions).","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","pattern":"^[a-fA-F0-9]{24}$"},"required":true,"name":"projectId","in":"path"},{"schema":{"type":"string","pattern":"^[a-fA-F0-9]{24}$"},"required":true,"name":"memberId","in":"path"}],"responses":{"200":{"description":"Member removed","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/RemoveResult"}},"required":["success","message","data"]}}}},"400":{"description":"Cannot remove OWNER","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"404":{"description":"Member not found","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}}},"/api/projects/:projectId/activity":{"get":{"tags":["Projects"],"summary":"Project activity log","description":"Returns recent activity entries for a project in reverse chronological order.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","pattern":"^[a-fA-F0-9]{24}$"},"required":true,"name":"projectId","in":"path"},{"schema":{"type":"integer","minimum":1,"default":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Activity","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"actorId":{"type":"string"},"action":{"type":"string"},"entityType":{"type":"string"},"entityId":{"type":"string"},"projectId":{"type":"string","nullable":true},"taskId":{"type":"string","nullable":true},"metadata":{"nullable":true},"createdAt":{"type":"string"},"actor":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"username":{"type":"string"},"avatar":{"type":"string","nullable":true}},"required":["id","name","username"]}},"required":["id","actorId","action","entityType","entityId","createdAt","actor"]}},"pagination":{"type":"object","properties":{"page":{"type":"integer"},"limit":{"type":"integer"},"total":{"type":"integer"},"totalPages":{"type":"integer"},"hasNext":{"type":"boolean"},"hasPrev":{"type":"boolean"}},"required":["page","limit","total","totalPages","hasNext","hasPrev"]}},"required":["items","pagination"]}},"required":["success","message","data"]}}}},"403":{"description":"Not a member","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"404":{"description":"Project not found","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}}},"/api/tasks":{"get":{"tags":["Tasks"],"summary":"List tasks across accessible projects","description":"Returns tasks from every active or completed project the signed-in user can access.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","enum":["TODO","IN_PROGRESS","COMPLETED","DONE","REVIEW"]},"required":false,"name":"status","in":"query"},{"schema":{"type":"string","enum":["LOW","MEDIUM","HIGH","URGENT"]},"required":false,"name":"priority","in":"query"},{"schema":{"type":"string","pattern":"^[a-fA-F0-9]{24}$"},"required":false,"name":"assigneeId","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":40},"required":false,"name":"label","in":"query"},{"schema":{"type":"string","maxLength":200},"required":false,"name":"search","in":"query"},{"schema":{"type":"string","format":"date-time"},"required":false,"name":"dueBefore","in":"query"},{"schema":{"type":"string","format":"date-time"},"required":false,"name":"dueAfter","in":"query"},{"schema":{"type":"boolean","nullable":true},"required":false,"name":"overdue","in":"query"},{"schema":{"type":"integer","minimum":1,"default":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string"},"required":false,"name":"sort","in":"query"}],"responses":{"200":{"description":"Tasks","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"projectId":{"type":"string"},"title":{"type":"string"},"description":{"type":"string","nullable":true},"status":{"type":"string","enum":["TODO","IN_PROGRESS","COMPLETED","DONE","REVIEW"]},"priority":{"type":"string","enum":["LOW","MEDIUM","HIGH","URGENT"]},"assigneeId":{"type":"string","nullable":true},"creatorId":{"type":"string"},"dueDate":{"type":"string","nullable":true},"labels":{"type":"array","items":{"type":"string"}},"order":{"type":"integer"},"completedAt":{"type":"string","nullable":true},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"assignee":{"type":"object","nullable":true,"properties":{"id":{"type":"string"},"name":{"type":"string"},"username":{"type":"string"},"avatar":{"type":"string","nullable":true}},"required":["id","name","username"]},"creator":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"username":{"type":"string"},"avatar":{"type":"string","nullable":true}},"required":["id","name","username"]},"_count":{"type":"object","properties":{"comments":{"type":"integer"}},"required":["comments"]}},"required":["id","projectId","title","status","priority","creatorId","labels","order","createdAt","updatedAt"]}},"pagination":{"type":"object","properties":{"page":{"type":"integer"},"limit":{"type":"integer"},"total":{"type":"integer"},"totalPages":{"type":"integer"},"hasNext":{"type":"boolean"},"hasPrev":{"type":"boolean"}},"required":["page","limit","total","totalPages","hasNext","hasPrev"]}},"required":["items","pagination"]}},"required":["success","message","data"]}}}}}}},"/api/projects/:projectId/tasks":{"get":{"tags":["Tasks"],"summary":"List tasks in a project","description":"Filter by status, priority, assignee, label, due date, or full-text search. Paginated.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","pattern":"^[a-fA-F0-9]{24}$"},"required":true,"name":"projectId","in":"path"},{"schema":{"type":"string","enum":["TODO","IN_PROGRESS","COMPLETED","DONE","REVIEW"]},"required":false,"name":"status","in":"query"},{"schema":{"type":"string","enum":["LOW","MEDIUM","HIGH","URGENT"]},"required":false,"name":"priority","in":"query"},{"schema":{"type":"string","pattern":"^[a-fA-F0-9]{24}$"},"required":false,"name":"assigneeId","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":40},"required":false,"name":"label","in":"query"},{"schema":{"type":"string","maxLength":200},"required":false,"name":"search","in":"query"},{"schema":{"type":"string","format":"date-time"},"required":false,"name":"dueBefore","in":"query"},{"schema":{"type":"string","format":"date-time"},"required":false,"name":"dueAfter","in":"query"},{"schema":{"type":"boolean","nullable":true},"required":false,"name":"overdue","in":"query"},{"schema":{"type":"integer","minimum":1,"default":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string"},"required":false,"name":"sort","in":"query"}],"responses":{"200":{"description":"Tasks","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"projectId":{"type":"string"},"title":{"type":"string"},"description":{"type":"string","nullable":true},"status":{"type":"string","enum":["TODO","IN_PROGRESS","COMPLETED","DONE","REVIEW"]},"priority":{"type":"string","enum":["LOW","MEDIUM","HIGH","URGENT"]},"assigneeId":{"type":"string","nullable":true},"creatorId":{"type":"string"},"dueDate":{"type":"string","nullable":true},"labels":{"type":"array","items":{"type":"string"}},"order":{"type":"integer"},"completedAt":{"type":"string","nullable":true},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"assignee":{"type":"object","nullable":true,"properties":{"id":{"type":"string"},"name":{"type":"string"},"username":{"type":"string"},"avatar":{"type":"string","nullable":true}},"required":["id","name","username"]},"creator":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"username":{"type":"string"},"avatar":{"type":"string","nullable":true}},"required":["id","name","username"]},"_count":{"type":"object","properties":{"comments":{"type":"integer"}},"required":["comments"]}},"required":["id","projectId","title","status","priority","creatorId","labels","order","createdAt","updatedAt"]}},"pagination":{"type":"object","properties":{"page":{"type":"integer"},"limit":{"type":"integer"},"total":{"type":"integer"},"totalPages":{"type":"integer"},"hasNext":{"type":"boolean"},"hasPrev":{"type":"boolean"}},"required":["page","limit","total","totalPages","hasNext","hasPrev"]}},"required":["items","pagination"]}},"required":["success","message","data"]}}}},"403":{"description":"Not a member","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"404":{"description":"Project not found","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}},"post":{"tags":["Tasks"],"summary":"Create a task in a project","description":"Requires MEMBER+. The creator is automatically the task creator; an assignee may be set.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","pattern":"^[a-fA-F0-9]{24}$"},"required":true,"name":"projectId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","minLength":1,"example":"Design landing page"},"description":{"type":"string","example":"Hero, features, pricing"},"status":{"type":"string","enum":["TODO","IN_PROGRESS","COMPLETED","DONE","REVIEW"],"default":"TODO"},"priority":{"type":"string","enum":["LOW","MEDIUM","HIGH","URGENT"],"default":"MEDIUM"},"assigneeId":{"type":"string","nullable":true},"dueDate":{"type":"string","nullable":true,"example":"2025-02-01T00:00:00.000Z"},"labels":{"type":"array","items":{"type":"string"},"default":[],"example":["design","frontend"]},"order":{"type":"integer"}},"required":["title"]}}}},"responses":{"201":{"description":"Task created","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string"},"projectId":{"type":"string"},"title":{"type":"string"},"description":{"type":"string","nullable":true},"status":{"type":"string","enum":["TODO","IN_PROGRESS","COMPLETED","DONE","REVIEW"]},"priority":{"type":"string","enum":["LOW","MEDIUM","HIGH","URGENT"]},"assigneeId":{"type":"string","nullable":true},"creatorId":{"type":"string"},"dueDate":{"type":"string","nullable":true},"labels":{"type":"array","items":{"type":"string"}},"order":{"type":"integer"},"completedAt":{"type":"string","nullable":true},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"assignee":{"type":"object","nullable":true,"properties":{"id":{"type":"string"},"name":{"type":"string"},"username":{"type":"string"},"avatar":{"type":"string","nullable":true}},"required":["id","name","username"]},"creator":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"username":{"type":"string"},"avatar":{"type":"string","nullable":true}},"required":["id","name","username"]},"_count":{"type":"object","properties":{"comments":{"type":"integer"}},"required":["comments"]}},"required":["id","projectId","title","status","priority","creatorId","labels","order","createdAt","updatedAt"]}},"required":["success","message","data"]}}}},"400":{"description":"Validation error or project archived","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"404":{"description":"Project not found","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}}},"/api/projects/:projectId/tasks/with-attachments":{"post":{"tags":["Tasks"],"summary":"Create a task and upload attachments in one request","description":"Multipart form data. Fields: title, description, status, priority, assigneeId, dueDate, labels (JSON or comma list). File fields: file (one or many). Same authorization as task creation.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","pattern":"^[a-fA-F0-9]{24}$"},"required":true,"name":"projectId","in":"path"}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/CreateTaskWithAttachments"}}}},"responses":{"201":{"description":"Task created with attachments","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string"},"projectId":{"type":"string"},"title":{"type":"string"},"description":{"type":"string","nullable":true},"status":{"type":"string","enum":["TODO","IN_PROGRESS","COMPLETED","DONE","REVIEW"]},"priority":{"type":"string","enum":["LOW","MEDIUM","HIGH","URGENT"]},"assigneeId":{"type":"string","nullable":true},"creatorId":{"type":"string"},"dueDate":{"type":"string","nullable":true},"labels":{"type":"array","items":{"type":"string"}},"order":{"type":"integer"},"completedAt":{"type":"string","nullable":true},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"assignee":{"type":"object","nullable":true,"properties":{"id":{"type":"string"},"name":{"type":"string"},"username":{"type":"string"},"avatar":{"type":"string","nullable":true}},"required":["id","name","username"]},"creator":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"username":{"type":"string"},"avatar":{"type":"string","nullable":true}},"required":["id","name","username"]},"_count":{"type":"object","properties":{"comments":{"type":"integer"}},"required":["comments"]},"attachments":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"taskId":{"type":"string"},"fileName":{"type":"string"},"mimeType":{"type":"string"},"fileSize":{"type":"integer"},"uploadedBy":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"username":{"type":"string"},"avatar":{"type":"string","nullable":true}},"required":["id","name","username"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"downloadUrl":{"type":"string"}},"required":["id","taskId","fileName","mimeType","fileSize","uploadedBy","createdAt","updatedAt","downloadUrl"]}}},"required":["id","projectId","title","status","priority","creatorId","labels","order","createdAt","updatedAt","attachments"]}},"required":["success","message","data"]}}}},"400":{"description":"Validation error, unsupported file type, or maximum file size exceeded","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"404":{"description":"Project not found","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}}},"/api/tasks/:taskId":{"get":{"tags":["Tasks"],"summary":"Get a task by id","description":"Returns the task with creator, assignee, and project info.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","pattern":"^[a-fA-F0-9]{24}$"},"required":true,"name":"taskId","in":"path"}],"responses":{"200":{"description":"Task","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string"},"projectId":{"type":"string"},"title":{"type":"string"},"description":{"type":"string","nullable":true},"status":{"type":"string","enum":["TODO","IN_PROGRESS","COMPLETED","DONE","REVIEW"]},"priority":{"type":"string","enum":["LOW","MEDIUM","HIGH","URGENT"]},"assigneeId":{"type":"string","nullable":true},"creatorId":{"type":"string"},"dueDate":{"type":"string","nullable":true},"labels":{"type":"array","items":{"type":"string"}},"order":{"type":"integer"},"completedAt":{"type":"string","nullable":true},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"assignee":{"type":"object","nullable":true,"properties":{"id":{"type":"string"},"name":{"type":"string"},"username":{"type":"string"},"avatar":{"type":"string","nullable":true}},"required":["id","name","username"]},"creator":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"username":{"type":"string"},"avatar":{"type":"string","nullable":true}},"required":["id","name","username"]},"_count":{"type":"object","properties":{"comments":{"type":"integer"}},"required":["comments"]}},"required":["id","projectId","title","status","priority","creatorId","labels","order","createdAt","updatedAt"]}},"required":["success","message","data"]}}}},"403":{"description":"Not a member","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"404":{"description":"Task not found","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}},"patch":{"tags":["Tasks"],"summary":"Update a task","description":"Update any field. Setting status to DONE records completedAt. Requires MEMBER+.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","pattern":"^[a-fA-F0-9]{24}$"},"required":true,"name":"taskId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","minLength":1},"description":{"type":"string","nullable":true},"status":{"type":"string","enum":["TODO","IN_PROGRESS","COMPLETED","DONE","REVIEW"]},"priority":{"type":"string","enum":["LOW","MEDIUM","HIGH","URGENT"]},"assigneeId":{"type":"string","nullable":true},"dueDate":{"type":"string","nullable":true},"labels":{"type":"array","items":{"type":"string"}},"order":{"type":"integer"}}}}}},"responses":{"200":{"description":"Task updated","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string"},"projectId":{"type":"string"},"title":{"type":"string"},"description":{"type":"string","nullable":true},"status":{"type":"string","enum":["TODO","IN_PROGRESS","COMPLETED","DONE","REVIEW"]},"priority":{"type":"string","enum":["LOW","MEDIUM","HIGH","URGENT"]},"assigneeId":{"type":"string","nullable":true},"creatorId":{"type":"string"},"dueDate":{"type":"string","nullable":true},"labels":{"type":"array","items":{"type":"string"}},"order":{"type":"integer"},"completedAt":{"type":"string","nullable":true},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"assignee":{"type":"object","nullable":true,"properties":{"id":{"type":"string"},"name":{"type":"string"},"username":{"type":"string"},"avatar":{"type":"string","nullable":true}},"required":["id","name","username"]},"creator":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"username":{"type":"string"},"avatar":{"type":"string","nullable":true}},"required":["id","name","username"]},"_count":{"type":"object","properties":{"comments":{"type":"integer"}},"required":["comments"]}},"required":["id","projectId","title","status","priority","creatorId","labels","order","createdAt","updatedAt"]}},"required":["success","message","data"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"404":{"description":"Task not found","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}},"delete":{"tags":["Tasks"],"summary":"Delete a task","description":"Only the creator or a MANAGER+ can delete a task.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","pattern":"^[a-fA-F0-9]{24}$"},"required":true,"name":"taskId","in":"path"}],"responses":{"200":{"description":"Task deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/DeleteResult"}},"required":["success","message","data"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"404":{"description":"Task not found","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}}},"/api/tasks/:taskId/move":{"post":{"tags":["Tasks"],"summary":"Move task to a different Kanban column","description":"Changes the task's status. Order is optional; if omitted, the task is appended to the new column.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","pattern":"^[a-fA-F0-9]{24}$"},"required":true,"name":"taskId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["TODO","IN_PROGRESS","COMPLETED","DONE","REVIEW"]},"order":{"type":"integer"}},"required":["status"]}}}},"responses":{"200":{"description":"Task moved","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string"},"projectId":{"type":"string"},"title":{"type":"string"},"description":{"type":"string","nullable":true},"status":{"type":"string","enum":["TODO","IN_PROGRESS","COMPLETED","DONE","REVIEW"]},"priority":{"type":"string","enum":["LOW","MEDIUM","HIGH","URGENT"]},"assigneeId":{"type":"string","nullable":true},"creatorId":{"type":"string"},"dueDate":{"type":"string","nullable":true},"labels":{"type":"array","items":{"type":"string"}},"order":{"type":"integer"},"completedAt":{"type":"string","nullable":true},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"assignee":{"type":"object","nullable":true,"properties":{"id":{"type":"string"},"name":{"type":"string"},"username":{"type":"string"},"avatar":{"type":"string","nullable":true}},"required":["id","name","username"]},"creator":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"username":{"type":"string"},"avatar":{"type":"string","nullable":true}},"required":["id","name","username"]},"_count":{"type":"object","properties":{"comments":{"type":"integer"}},"required":["comments"]}},"required":["id","projectId","title","status","priority","creatorId","labels","order","createdAt","updatedAt"]}},"required":["success","message","data"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"404":{"description":"Task not found","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}}},"/api/tasks/:taskId/reorder":{"post":{"tags":["Tasks"],"summary":"Reorder a task within a column","description":"Set a new order value within a specific status column for drag-and-drop Kanban reordering.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","pattern":"^[a-fA-F0-9]{24}$"},"required":true,"name":"taskId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["TODO","IN_PROGRESS","COMPLETED","DONE","REVIEW"]},"order":{"type":"integer","minimum":0}},"required":["status","order"]}}}},"responses":{"200":{"description":"Task reordered","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string"},"projectId":{"type":"string"},"title":{"type":"string"},"description":{"type":"string","nullable":true},"status":{"type":"string","enum":["TODO","IN_PROGRESS","COMPLETED","DONE","REVIEW"]},"priority":{"type":"string","enum":["LOW","MEDIUM","HIGH","URGENT"]},"assigneeId":{"type":"string","nullable":true},"creatorId":{"type":"string"},"dueDate":{"type":"string","nullable":true},"labels":{"type":"array","items":{"type":"string"}},"order":{"type":"integer"},"completedAt":{"type":"string","nullable":true},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"assignee":{"type":"object","nullable":true,"properties":{"id":{"type":"string"},"name":{"type":"string"},"username":{"type":"string"},"avatar":{"type":"string","nullable":true}},"required":["id","name","username"]},"creator":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"username":{"type":"string"},"avatar":{"type":"string","nullable":true}},"required":["id","name","username"]},"_count":{"type":"object","properties":{"comments":{"type":"integer"}},"required":["comments"]}},"required":["id","projectId","title","status","priority","creatorId","labels","order","createdAt","updatedAt"]}},"required":["success","message","data"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"404":{"description":"Task not found","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}}},"/api/tasks/:taskId/comments":{"get":{"tags":["Comments"],"summary":"List comments on a task","description":"Returns comments in chronological order, including any @mentions.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","pattern":"^[a-fA-F0-9]{24}$"},"required":true,"name":"taskId","in":"path"},{"schema":{"type":"integer","minimum":1,"default":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Comments","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"content":{"type":"string"},"authorId":{"type":"string"},"taskId":{"type":"string"},"edited":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"author":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"username":{"type":"string"},"avatar":{"type":"string","nullable":true}},"required":["id","name","username"]},"mentions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"userId":{"type":"string"},"username":{"type":"string"},"user":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"username":{"type":"string"}},"required":["id","name","username"]}},"required":["id","userId","username"]}}},"required":["id","content","authorId","taskId","edited","createdAt","updatedAt","author","mentions"]}},"pagination":{"type":"object","properties":{"page":{"type":"integer"},"limit":{"type":"integer"},"total":{"type":"integer"},"totalPages":{"type":"integer"},"hasNext":{"type":"boolean"},"hasPrev":{"type":"boolean"}},"required":["page","limit","total","totalPages","hasNext","hasPrev"]}},"required":["items","pagination"]}},"required":["success","message","data"]}}}},"403":{"description":"Not a member","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"404":{"description":"Task not found","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}},"post":{"tags":["Comments"],"summary":"Add a comment to a task","description":"Use @username to mention other users. The mentioned user is notified immediately.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","pattern":"^[a-fA-F0-9]{24}$"},"required":true,"name":"taskId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"content":{"type":"string","minLength":1,"example":"Looks good @manager!"}},"required":["content"]}}}},"responses":{"201":{"description":"Comment created","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string"},"content":{"type":"string"},"authorId":{"type":"string"},"taskId":{"type":"string"},"edited":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"author":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"username":{"type":"string"},"avatar":{"type":"string","nullable":true}},"required":["id","name","username"]},"mentions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"userId":{"type":"string"},"username":{"type":"string"},"user":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"username":{"type":"string"}},"required":["id","name","username"]}},"required":["id","userId","username"]}}},"required":["id","content","authorId","taskId","edited","createdAt","updatedAt","author","mentions"]}},"required":["success","message","data"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"403":{"description":"Not a member","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"404":{"description":"Task not found","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}}},"/api/comments/:commentId":{"patch":{"tags":["Comments"],"summary":"Edit a comment","description":"Only the author can edit their own comment. Re-extracts mentions.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","pattern":"^[a-fA-F0-9]{24}$"},"required":true,"name":"commentId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"content":{"type":"string","minLength":1}},"required":["content"]}}}},"responses":{"200":{"description":"Comment updated","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string"},"content":{"type":"string"},"authorId":{"type":"string"},"taskId":{"type":"string"},"edited":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"author":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"username":{"type":"string"},"avatar":{"type":"string","nullable":true}},"required":["id","name","username"]},"mentions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"userId":{"type":"string"},"username":{"type":"string"},"user":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"username":{"type":"string"}},"required":["id","name","username"]}},"required":["id","userId","username"]}}},"required":["id","content","authorId","taskId","edited","createdAt","updatedAt","author","mentions"]}},"required":["success","message","data"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"403":{"description":"Not your comment","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"404":{"description":"Comment not found","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}},"delete":{"tags":["Comments"],"summary":"Delete a comment","description":"Author or project manager+ can delete a comment.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","pattern":"^[a-fA-F0-9]{24}$"},"required":true,"name":"commentId","in":"path"}],"responses":{"200":{"description":"Comment deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/DeleteResult"}},"required":["success","message","data"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"404":{"description":"Comment not found","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}}},"/api/notifications":{"get":{"tags":["Notifications"],"summary":"List my notifications","description":"Optional `read=true|false` filter, paginated newest first.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"boolean","nullable":true},"required":false,"name":"read","in":"query"},{"schema":{"type":"integer","minimum":1,"default":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Notifications","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"userId":{"type":"string"},"type":{"type":"string"},"title":{"type":"string"},"message":{"type":"string"},"data":{"nullable":true},"read":{"type":"boolean"},"createdAt":{"type":"string"}},"required":["id","userId","type","title","message","read","createdAt"]}},"pagination":{"type":"object","properties":{"page":{"type":"integer"},"limit":{"type":"integer"},"total":{"type":"integer"},"totalPages":{"type":"integer"},"hasNext":{"type":"boolean"},"hasPrev":{"type":"boolean"}},"required":["page","limit","total","totalPages","hasNext","hasPrev"]}},"required":["items","pagination"]}},"required":["success","message","data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}}},"/api/notifications/unread-count":{"get":{"tags":["Notifications"],"summary":"Get unread notification count","description":"Returns just the number of unread notifications for the current user.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Unread count","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/UnreadCount"}},"required":["success","message","data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}}},"/api/notifications/:notificationId/read":{"patch":{"tags":["Notifications"],"summary":"Mark a notification as read","description":"Marks a single notification as read for the current user.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","pattern":"^[a-fA-F0-9]{24}$"},"required":true,"name":"notificationId","in":"path"}],"responses":{"200":{"description":"Notification","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/NotificationResult"}},"required":["success","message","data"]}}}},"404":{"description":"Notification not found","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}}},"/api/notifications/read-all":{"patch":{"tags":["Notifications"],"summary":"Mark all notifications as read","description":"Marks every unread notification for the current user as read.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"All marked as read","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/AllReadResult"}},"required":["success","message","data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}}},"/api/upload/avatar":{"post":{"tags":["Uploads"],"summary":"Upload avatar image","description":"Upload an avatar image (PNG, JPEG, WebP, GIF, max 2 MB). Returns the public URL.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Avatar uploaded","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"type":"object","properties":{"url":{"type":"string","example":"/api/users/000000000000000000000000/avatar"}},"required":["url"]}},"required":["success","message","data"]}}}},"400":{"description":"Invalid file or size","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}}},"/api/tasks/:taskId/attachments":{"get":{"tags":["Attachments"],"summary":"List task attachments","description":"Returns every attachment uploaded to a task the user can access.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","pattern":"^[a-fA-F0-9]{24}$"},"required":true,"name":"taskId","in":"path"}],"responses":{"200":{"description":"Attachments","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"taskId":{"type":"string"},"fileName":{"type":"string"},"mimeType":{"type":"string"},"fileSize":{"type":"integer"},"uploadedBy":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"username":{"type":"string"},"avatar":{"type":"string","nullable":true}},"required":["id","name","username"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"downloadUrl":{"type":"string"}},"required":["id","taskId","fileName","mimeType","fileSize","uploadedBy","createdAt","updatedAt","downloadUrl"]}}},"required":["success","message","data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"403":{"description":"Not a member","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"404":{"description":"Task not found","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}},"post":{"tags":["Attachments"],"summary":"Upload a task attachment","description":"Upload a PDF, DOC, DOCX, PNG, JPG, JPEG, or ZIP file up to 10 MB. Viewers cannot upload.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","pattern":"^[a-fA-F0-9]{24}$"},"required":true,"name":"taskId","in":"path"}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary"}}}}}},"responses":{"201":{"description":"Attachment uploaded","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string"},"taskId":{"type":"string"},"fileName":{"type":"string"},"mimeType":{"type":"string"},"fileSize":{"type":"integer"},"uploadedBy":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"username":{"type":"string"},"avatar":{"type":"string","nullable":true}},"required":["id","name","username"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"downloadUrl":{"type":"string"}},"required":["id","taskId","fileName","mimeType","fileSize","uploadedBy","createdAt","updatedAt","downloadUrl"]}},"required":["success","message","data"]}}}},"400":{"description":"Unsupported file type or maximum file size exceeded","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"404":{"description":"Task not found","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}}},"/api/tasks/:taskId/attachments/:attachmentId/download":{"get":{"tags":["Attachments"],"summary":"Download a task attachment","description":"Streams the attachment binary with Content-Disposition: attachment.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","pattern":"^[a-fA-F0-9]{24}$"},"required":true,"name":"taskId","in":"path"},{"schema":{"type":"string","pattern":"^[a-fA-F0-9]{24}$"},"required":true,"name":"attachmentId","in":"path"}],"responses":{"200":{"description":"File binary","content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"404":{"description":"Attachment not found","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}}},"/api/tasks/:taskId/attachments/:attachmentId":{"delete":{"tags":["Attachments"],"summary":"Delete a task attachment","description":"Removes the attachment metadata and file from storage.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","pattern":"^[a-fA-F0-9]{24}$"},"required":true,"name":"taskId","in":"path"},{"schema":{"type":"string","pattern":"^[a-fA-F0-9]{24}$"},"required":true,"name":"attachmentId","in":"path"}],"responses":{"200":{"description":"Attachment deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"type":"object","properties":{"deleted":{"type":"boolean"}},"required":["deleted"]}},"required":["success","message","data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"404":{"description":"Attachment not found","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}}},"/api/projects/:projectId/health":{"get":{"tags":["Project Health Intelligence"],"summary":"Get current project health intelligence","description":"Returns a 0-100 health score, risk level, weighted metrics, top risk factors, recommended actions, predicted completion date, on-track probability, score trend, and a per-signal breakdown. Computed deterministically from live project data — no LLM required.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","pattern":"^[a-fA-F0-9]{24}$"},"required":true,"name":"projectId","in":"path"}],"responses":{"200":{"description":"Project health","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"type":"object","properties":{"score":{"type":"integer","minimum":0,"maximum":100},"riskLevel":{"type":"string","enum":["ON_TRACK","AT_RISK","CRITICAL"]},"metrics":{"type":"object","properties":{"totalTasks":{"type":"integer"},"completedTasks":{"type":"integer"},"overdueTasks":{"type":"integer"},"inProgressTasks":{"type":"integer"},"todoTasks":{"type":"integer"},"overdueRatio":{"type":"number"},"velocity7d":{"type":"integer"},"daysToDeadline":{"type":"integer","nullable":true},"activeMembers":{"type":"integer"},"totalMembers":{"type":"integer"},"taskDistribution":{"type":"number"},"overdueScore":{"type":"integer"},"velocityScore":{"type":"integer"},"deadlineScore":{"type":"integer"},"engagementScore":{"type":"integer"},"distributionScore":{"type":"integer"}},"required":["totalTasks","completedTasks","overdueTasks","inProgressTasks","todoTasks","overdueRatio","velocity7d","daysToDeadline","activeMembers","totalMembers","taskDistribution","overdueScore","velocityScore","deadlineScore","engagementScore","distributionScore"]},"insights":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["warning","info","success","critical","action"]},"title":{"type":"string"},"text":{"type":"string"}},"required":["type","title","text"]}},"topRiskFactors":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["warning","info","success","critical","action"]},"title":{"type":"string"},"text":{"type":"string"}},"required":["type","title","text"]}},"recommendations":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["warning","info","success","critical","action"]},"title":{"type":"string"},"text":{"type":"string"}},"required":["type","title","text"]}},"signalContributions":{"type":"array","items":{"type":"object","properties":{"signal":{"type":"string","enum":["overdue","velocity","deadline","engagement","distribution"]},"label":{"type":"string"},"points":{"type":"integer"},"maxPoints":{"type":"integer"}},"required":["signal","label","points","maxPoints"]}},"predictedCompletionDate":{"type":"string","nullable":true,"format":"date-time"},"onTrackProbability":{"type":"number","minimum":0,"maximum":1},"previousScore":{"type":"integer","nullable":true},"scoreTrend":{"type":"integer","nullable":true},"trendDirection":{"type":"string","nullable":true,"enum":["up","down","flat"]},"computedAt":{"type":"string","format":"date-time"}},"required":["score","riskLevel","metrics","insights","topRiskFactors","recommendations","signalContributions","predictedCompletionDate","onTrackProbability","previousScore","scoreTrend","trendDirection","computedAt"]}},"required":["success","message","data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"403":{"description":"Not a project member","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"404":{"description":"Project not found","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}}},"/api/projects/:projectId/health/refresh":{"post":{"tags":["Project Health Intelligence"],"summary":"Force a fresh health computation and persist a snapshot","description":"Recomputes the health score, saves a new ProjectHealthSnapshot, and emits a PROJECT_HEALTH_COMPUTED activity log entry. Use this after a meaningful change to refresh the dashboard immediately.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","pattern":"^[a-fA-F0-9]{24}$"},"required":true,"name":"projectId","in":"path"}],"responses":{"200":{"description":"Project health refreshed","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"type":"object","properties":{"score":{"type":"integer","minimum":0,"maximum":100},"riskLevel":{"type":"string","enum":["ON_TRACK","AT_RISK","CRITICAL"]},"metrics":{"type":"object","properties":{"totalTasks":{"type":"integer"},"completedTasks":{"type":"integer"},"overdueTasks":{"type":"integer"},"inProgressTasks":{"type":"integer"},"todoTasks":{"type":"integer"},"overdueRatio":{"type":"number"},"velocity7d":{"type":"integer"},"daysToDeadline":{"type":"integer","nullable":true},"activeMembers":{"type":"integer"},"totalMembers":{"type":"integer"},"taskDistribution":{"type":"number"},"overdueScore":{"type":"integer"},"velocityScore":{"type":"integer"},"deadlineScore":{"type":"integer"},"engagementScore":{"type":"integer"},"distributionScore":{"type":"integer"}},"required":["totalTasks","completedTasks","overdueTasks","inProgressTasks","todoTasks","overdueRatio","velocity7d","daysToDeadline","activeMembers","totalMembers","taskDistribution","overdueScore","velocityScore","deadlineScore","engagementScore","distributionScore"]},"insights":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["warning","info","success","critical","action"]},"title":{"type":"string"},"text":{"type":"string"}},"required":["type","title","text"]}},"topRiskFactors":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["warning","info","success","critical","action"]},"title":{"type":"string"},"text":{"type":"string"}},"required":["type","title","text"]}},"recommendations":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["warning","info","success","critical","action"]},"title":{"type":"string"},"text":{"type":"string"}},"required":["type","title","text"]}},"signalContributions":{"type":"array","items":{"type":"object","properties":{"signal":{"type":"string","enum":["overdue","velocity","deadline","engagement","distribution"]},"label":{"type":"string"},"points":{"type":"integer"},"maxPoints":{"type":"integer"}},"required":["signal","label","points","maxPoints"]}},"predictedCompletionDate":{"type":"string","nullable":true,"format":"date-time"},"onTrackProbability":{"type":"number","minimum":0,"maximum":1},"previousScore":{"type":"integer","nullable":true},"scoreTrend":{"type":"integer","nullable":true},"trendDirection":{"type":"string","nullable":true,"enum":["up","down","flat"]},"computedAt":{"type":"string","format":"date-time"}},"required":["score","riskLevel","metrics","insights","topRiskFactors","recommendations","signalContributions","predictedCompletionDate","onTrackProbability","previousScore","scoreTrend","trendDirection","computedAt"]}},"required":["success","message","data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"403":{"description":"Not a project member","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}}},"/api/projects/:projectId/health/history":{"get":{"tags":["Project Health Intelligence"],"summary":"Get historical health snapshots for a project","description":"Returns up to `limit` most recent snapshots (default 30) ordered oldest-to-newest for trend charts.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","pattern":"^[a-fA-F0-9]{24}$"},"required":true,"name":"projectId","in":"path"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":30},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Health history","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"score":{"type":"integer"},"riskLevel":{"type":"string","enum":["ON_TRACK","AT_RISK","CRITICAL"]},"onTrackProbability":{"type":"number"},"computedAt":{"type":"string","format":"date-time"}},"required":["score","riskLevel","onTrackProbability","computedAt"]}}},"required":["items"]}},"required":["success","message","data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}},"403":{"description":"Not a project member","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}}},"/api/dashboard/health":{"get":{"tags":["Project Health Intelligence"],"summary":"Workspace-wide health intelligence overview","description":"Returns the average health score, on-track / at-risk / critical counts, and the 10 worst- and 10 best-scoring projects. Powers the dashboard Workspace Health Overview widget.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Workspace health","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"type":"object","properties":{"totalProjects":{"type":"integer"},"averageScore":{"type":"integer"},"onTrackCount":{"type":"integer"},"atRiskCount":{"type":"integer"},"criticalCount":{"type":"integer"},"atRisk":{"type":"array","items":{"type":"object","properties":{"projectId":{"type":"string"},"projectName":{"type":"string"},"projectColor":{"type":"string"},"projectStatus":{"type":"string"},"deadline":{"type":"string","nullable":true,"format":"date-time"},"score":{"type":"integer"},"riskLevel":{"type":"string","enum":["ON_TRACK","AT_RISK","CRITICAL"]},"onTrackProbability":{"type":"number"},"computedAt":{"type":"string","format":"date-time"}},"required":["projectId","projectName","projectColor","projectStatus","deadline","score","riskLevel","onTrackProbability","computedAt"]}},"topHealthy":{"type":"array","items":{"type":"object","properties":{"projectId":{"type":"string"},"projectName":{"type":"string"},"projectColor":{"type":"string"},"projectStatus":{"type":"string"},"deadline":{"type":"string","nullable":true,"format":"date-time"},"score":{"type":"integer"},"riskLevel":{"type":"string","enum":["ON_TRACK","AT_RISK","CRITICAL"]},"onTrackProbability":{"type":"number"},"computedAt":{"type":"string","format":"date-time"}},"required":["projectId","projectName","projectColor","projectStatus","deadline","score","riskLevel","onTrackProbability","computedAt"]}}},"required":["totalProjects","averageScore","onTrackCount","atRiskCount","criticalCount","atRisk","topHealthy"]}},"required":["success","message","data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}}},"/api/dashboard/portfolio":{"get":{"tags":["Portfolio Dashboard"],"summary":"Get the full Executive Dashboard payload in a single call","description":"Returns the workspace executive summary, delivery forecast, team intelligence, project risk matrix, and rule-based enterprise insights. Powers the Executive Dashboard section at the top of `/dashboard`. Deterministic — no LLM, runs on free Deno Deploy tier.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Portfolio dashboard","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"type":"object","properties":{"executive":{"type":"object","properties":{"totalProjects":{"type":"integer"},"healthScore":{"type":"integer","minimum":0,"maximum":100},"healthyCount":{"type":"integer"},"atRiskCount":{"type":"integer"},"criticalCount":{"type":"integer"},"weeklyTrend":{"type":"integer"},"weeklyTrendDirection":{"type":"string","enum":["up","down","flat"]},"narrative":{"type":"object","properties":{"headline":{"type":"string"},"body":{"type":"string"}},"required":["headline","body"]}},"required":["totalProjects","healthScore","healthyCount","atRiskCount","criticalCount","weeklyTrend","weeklyTrendDirection","narrative"]},"forecast":{"type":"object","properties":{"predictedCompletionDate":{"type":"string","nullable":true,"format":"date-time"},"onTimeProbability":{"type":"number","minimum":0,"maximum":1},"riskLevel":{"type":"string","enum":["ON_TRACK","AT_RISK","CRITICAL"]},"completionTrend":{"type":"integer"},"completionTrendDirection":{"type":"string","enum":["up","down","flat"]},"totalRemainingTasks":{"type":"integer"},"averageVelocity7d":{"type":"number"},"projects":{"type":"array","items":{"type":"object","properties":{"projectId":{"type":"string"},"projectName":{"type":"string"},"predictedCompletionDate":{"type":"string","nullable":true,"format":"date-time"},"onTimeProbability":{"type":"number","minimum":0,"maximum":1},"riskLevel":{"type":"string","enum":["ON_TRACK","AT_RISK","CRITICAL"]}},"required":["projectId","projectName","predictedCompletionDate","onTimeProbability","riskLevel"]}}},"required":["predictedCompletionDate","onTimeProbability","riskLevel","completionTrend","completionTrendDirection","totalRemainingTasks","averageVelocity7d","projects"]},"team":{"type":"object","properties":{"memberCount":{"type":"integer"},"teamEfficiency":{"type":"integer","minimum":0,"maximum":100},"topContributors":{"type":"array","items":{"type":"object","properties":{"userId":{"type":"string"},"name":{"type":"string"},"username":{"type":"string"},"avatar":{"type":"string","nullable":true},"tasksCompleted":{"type":"integer"},"tasksCompleted7d":{"type":"integer"},"productivityScore":{"type":"integer","minimum":0,"maximum":100}},"required":["userId","name","username","avatar","tasksCompleted","tasksCompleted7d","productivityScore"]}},"workload":{"type":"object","properties":{"balanced":{"type":"integer"},"overloaded":{"type":"integer"},"underutilized":{"type":"integer"},"meanOpenTasks":{"type":"number"},"stdDev":{"type":"number"}},"required":["balanced","overloaded","underutilized","meanOpenTasks","stdDev"]}},"required":["memberCount","teamEfficiency","topContributors","workload"]},"matrix":{"type":"array","items":{"type":"object","properties":{"projectId":{"type":"string"},"projectName":{"type":"string"},"projectColor":{"type":"string"},"projectStatus":{"type":"string"},"healthScore":{"type":"integer"},"riskLevel":{"type":"string","enum":["ON_TRACK","AT_RISK","CRITICAL"]},"completionPct":{"type":"integer"},"overdueTasks":{"type":"integer"},"totalTasks":{"type":"integer"},"completedTasks":{"type":"integer"},"deadline":{"type":"string","nullable":true,"format":"date-time"},"deadlineStatus":{"type":"string","enum":["ON_TRACK","AT_RISK","OVERDUE","NO_DEADLINE"]},"daysToDeadline":{"type":"integer","nullable":true}},"required":["projectId","projectName","projectColor","projectStatus","healthScore","riskLevel","completionPct","overdueTasks","totalTasks","completedTasks","deadline","deadlineStatus","daysToDeadline"]}},"insights":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["warning","info","success"]},"title":{"type":"string"},"projectId":{"type":"string"}},"required":["type","title"]}}},"required":["executive","forecast","team","matrix","insights"]}},"required":["success","message","data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}}},"/api/dashboard/executive":{"get":{"tags":["Portfolio Dashboard"],"summary":"Executive summary (workspace health, counts, weekly trend, narrative)","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Executive summary","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"type":"object","properties":{"totalProjects":{"type":"integer"},"healthScore":{"type":"integer","minimum":0,"maximum":100},"healthyCount":{"type":"integer"},"atRiskCount":{"type":"integer"},"criticalCount":{"type":"integer"},"weeklyTrend":{"type":"integer"},"weeklyTrendDirection":{"type":"string","enum":["up","down","flat"]},"narrative":{"type":"object","properties":{"headline":{"type":"string"},"body":{"type":"string"}},"required":["headline","body"]}},"required":["totalProjects","healthScore","healthyCount","atRiskCount","criticalCount","weeklyTrend","weeklyTrendDirection","narrative"]}},"required":["success","message","data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}}},"/api/dashboard/forecast":{"get":{"tags":["Portfolio Dashboard"],"summary":"Delivery forecast (predicted completion, on-time probability, trend)","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Delivery forecast","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"type":"object","properties":{"predictedCompletionDate":{"type":"string","nullable":true,"format":"date-time"},"onTimeProbability":{"type":"number","minimum":0,"maximum":1},"riskLevel":{"type":"string","enum":["ON_TRACK","AT_RISK","CRITICAL"]},"completionTrend":{"type":"integer"},"completionTrendDirection":{"type":"string","enum":["up","down","flat"]},"totalRemainingTasks":{"type":"integer"},"averageVelocity7d":{"type":"number"},"projects":{"type":"array","items":{"type":"object","properties":{"projectId":{"type":"string"},"projectName":{"type":"string"},"predictedCompletionDate":{"type":"string","nullable":true,"format":"date-time"},"onTimeProbability":{"type":"number","minimum":0,"maximum":1},"riskLevel":{"type":"string","enum":["ON_TRACK","AT_RISK","CRITICAL"]}},"required":["projectId","projectName","predictedCompletionDate","onTimeProbability","riskLevel"]}}},"required":["predictedCompletionDate","onTimeProbability","riskLevel","completionTrend","completionTrendDirection","totalRemainingTasks","averageVelocity7d","projects"]}},"required":["success","message","data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}}},"/api/dashboard/team":{"get":{"tags":["Portfolio Dashboard"],"summary":"Team intelligence (top contributors, workload distribution, efficiency)","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Team intelligence","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"type":"object","properties":{"memberCount":{"type":"integer"},"teamEfficiency":{"type":"integer","minimum":0,"maximum":100},"topContributors":{"type":"array","items":{"type":"object","properties":{"userId":{"type":"string"},"name":{"type":"string"},"username":{"type":"string"},"avatar":{"type":"string","nullable":true},"tasksCompleted":{"type":"integer"},"tasksCompleted7d":{"type":"integer"},"productivityScore":{"type":"integer","minimum":0,"maximum":100}},"required":["userId","name","username","avatar","tasksCompleted","tasksCompleted7d","productivityScore"]}},"workload":{"type":"object","properties":{"balanced":{"type":"integer"},"overloaded":{"type":"integer"},"underutilized":{"type":"integer"},"meanOpenTasks":{"type":"number"},"stdDev":{"type":"number"}},"required":["balanced","overloaded","underutilized","meanOpenTasks","stdDev"]}},"required":["memberCount","teamEfficiency","topContributors","workload"]}},"required":["success","message","data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}}},"/api/dashboard/risk-matrix":{"get":{"tags":["Portfolio Dashboard"],"summary":"Project risk matrix (sorted by health, ascending)","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Risk matrix","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"type":"array","items":{"type":"object","properties":{"projectId":{"type":"string"},"projectName":{"type":"string"},"projectColor":{"type":"string"},"projectStatus":{"type":"string"},"healthScore":{"type":"integer"},"riskLevel":{"type":"string","enum":["ON_TRACK","AT_RISK","CRITICAL"]},"completionPct":{"type":"integer"},"overdueTasks":{"type":"integer"},"totalTasks":{"type":"integer"},"completedTasks":{"type":"integer"},"deadline":{"type":"string","nullable":true,"format":"date-time"},"deadlineStatus":{"type":"string","enum":["ON_TRACK","AT_RISK","OVERDUE","NO_DEADLINE"]},"daysToDeadline":{"type":"integer","nullable":true}},"required":["projectId","projectName","projectColor","projectStatus","healthScore","riskLevel","completionPct","overdueTasks","totalTasks","completedTasks","deadline","deadlineStatus","daysToDeadline"]}}},"required":["success","message","data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}}},"/api/dashboard/insights":{"get":{"tags":["Portfolio Dashboard"],"summary":"Rule-based enterprise insights (deterministic, no LLM)","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Enterprise insights","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["warning","info","success"]},"title":{"type":"string"},"projectId":{"type":"string"}},"required":["type","title"]}}},"required":["success","message","data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}}},"/api/analytics/trends":{"get":{"tags":["Analytics"],"summary":"All 5 trend series in one call (completion, velocity, overdue, activity, health)","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"integer","minimum":7,"maximum":90,"default":30},"required":false,"name":"days","in":"query"}],"responses":{"200":{"description":"All trends","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"type":"object","properties":{"completion":{"type":"object","properties":{"completed":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string"},"label":{"type":"string"},"value":{"type":"number"}},"required":["date","label","value"]}},"created":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string"},"label":{"type":"string"},"value":{"type":"number"}},"required":["date","label","value"]}}},"required":["completed","created"]},"velocity":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string"},"label":{"type":"string"},"value":{"type":"number"}},"required":["date","label","value"]}},"overdue":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string"},"label":{"type":"string"},"value":{"type":"number"}},"required":["date","label","value"]}},"activity":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string"},"label":{"type":"string"},"value":{"type":"number"}},"required":["date","label","value"]}},"health":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string"},"label":{"type":"string"},"value":{"type":"number"}},"required":["date","label","value"]}},"days":{"type":"integer"}},"required":["completion","velocity","overdue","activity","health","days"]}},"required":["success","message","data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}}},"/api/analytics/trends/completion":{"get":{"tags":["Analytics"],"summary":"Completion trend (daily created vs completed)","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"integer","minimum":7,"maximum":90,"default":30},"required":false,"name":"days","in":"query"}],"responses":{"200":{"description":"Completion trend","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"type":"object","properties":{"completed":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string"},"label":{"type":"string"},"value":{"type":"number"}},"required":["date","label","value"]}},"created":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string"},"label":{"type":"string"},"value":{"type":"number"}},"required":["date","label","value"]}}},"required":["completed","created"]}},"required":["success","message","data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}}},"/api/analytics/trends/velocity":{"get":{"tags":["Analytics"],"summary":"7-day rolling velocity (tasks completed per day, smoothed)","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"integer","minimum":7,"maximum":90,"default":30},"required":false,"name":"days","in":"query"}],"responses":{"200":{"description":"Velocity trend","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string"},"label":{"type":"string"},"value":{"type":"number"}},"required":["date","label","value"]}}},"required":["items"]}},"required":["success","message","data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}}},"/api/analytics/trends/overdue":{"get":{"tags":["Analytics"],"summary":"Daily count of overdue tasks (time-machine snapshot)","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"integer","minimum":7,"maximum":90,"default":30},"required":false,"name":"days","in":"query"}],"responses":{"200":{"description":"Overdue trend","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string"},"label":{"type":"string"},"value":{"type":"number"}},"required":["date","label","value"]}}},"required":["items"]}},"required":["success","message","data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}}},"/api/analytics/trends/team-activity":{"get":{"tags":["Analytics"],"summary":"Daily count of activity log entries","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"integer","minimum":7,"maximum":90,"default":30},"required":false,"name":"days","in":"query"}],"responses":{"200":{"description":"Team activity trend","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string"},"label":{"type":"string"},"value":{"type":"number"}},"required":["date","label","value"]}}},"required":["items"]}},"required":["success","message","data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}}},"/api/analytics/trends/project-health":{"get":{"tags":["Analytics"],"summary":"Daily average project health score (0–100)","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"integer","minimum":7,"maximum":90,"default":30},"required":false,"name":"days","in":"query"}],"responses":{"200":{"description":"Project health trend","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"},"data":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string"},"label":{"type":"string"},"value":{"type":"number"}},"required":["date","label","value"]}}},"required":["items"]}},"required":["success","message","data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"details":{"nullable":true}}}},"required":["success","message"]}}}}}}}}}