{"openapi":"3.0.0","paths":{"/boards/my":{"get":{"operationId":"BoardsController_getMyBoards","parameters":[],"responses":{"200":{"description":"200 (Board list successfully retrieved)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetMyBoardsOutput"}}}},"403":{"description":"Maximum board count exceeded (403 Forbidden)"}},"security":[{"basic":[]},{"bearer":[]},{"bearer":[]},{"api-key":[]}],"summary":"Get list of my created boards","tags":["Boards Owner"]}},"/boards":{"post":{"operationId":"BoardsController_createBoard","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardCreateInputDTO"}}}},"responses":{"201":{"description":"Board successfully created (201 Created)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetBoardOutput"}}}},"403":{"description":"Maximum board count (10) exceeded (403 Forbidden)"}},"security":[{"basic":[]},{"bearer":[]},{"bearer":[]},{"api-key":[]}],"summary":"Create new board","tags":["Boards Owner"]},"get":{"operationId":"BoardsPublicController_getBoards","parameters":[{"name":"pageNumber","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"pageSize","required":false,"in":"query","schema":{"minimum":1,"maximum":20,"default":10,"type":"number"}}],"responses":{"200":{"description":"JSON:API list of boards with pagination","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetBoardsOutput"}}}}},"summary":"Get list of all boards created by all users","tags":["Boards Public"],"security":[{"api-key":[]}]}},"/boards/{boardId}":{"put":{"operationId":"BoardsController_updateBoard","parameters":[{"name":"boardId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardUpdateInputDTO"}}}},"responses":{"200":{"description":"Board successfully updated (200 OK)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetBoardOutput"}}}},"403":{"description":"You are not the board owner (403 Forbidden)"},"404":{"description":"Board not found (404 Not Found)"}},"security":[{"basic":[]},{"bearer":[]},{"bearer":[]},{"api-key":[]}],"summary":"Update one of your boards","tags":["Boards Owner"]},"delete":{"operationId":"BoardsController_deleteBoard","parameters":[{"name":"boardId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":"Board successfully deleted (204 No Content)"},"403":{"description":"You are not the board owner (403 Forbidden)"},"404":{"description":"Board not found (404 Not Found)"}},"security":[{"basic":[]},{"bearer":[]},{"bearer":[]},{"api-key":[]}],"summary":"Delete one of your boards","tags":["Boards Owner"]},"get":{"operationId":"BoardsPublicController_getBoardById","parameters":[{"name":"boardId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Board successfully found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetBoardOutput"}}}},"404":{"description":"Board with this ID not found"}},"summary":"Get one board by ID","tags":["Boards Public"],"security":[{"api-key":[]}]}},"/boards/{boardId}/reorder":{"put":{"operationId":"BoardsController_reorder","parameters":[{"name":"boardId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReorderBoardsRequestPayload"}}}},"responses":{"204":{"description":"Board order successfully changed (204 No Content)"}},"security":[{"basic":[]},{"bearer":[]},{"bearer":[]},{"api-key":[]}],"summary":"Change board order","tags":["Boards Owner"]}},"/boards/{boardId}/images/main":{"post":{"description":"\n- Only images in landscape orientation are supported\n- Minimum image height is 500 pixels\n- Recommended format: JPG or PNG\n- Form-data field: `file`\n","operationId":"BoardsController_upload","parameters":[{"name":"boardId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary"}},"required":["file"]}}}},"responses":{"200":{"description":"200 (Image successfully uploaded and saved)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardImagesOutputDTO"}}}},"400":{"description":"400 (Invalid file: cannot determine dimensions, wrong orientation or height too small)"},"403":{"description":"403 (You are not the board owner)"},"404":{"description":"404 (Board not found)"}},"security":[{"basic":[]},{"bearer":[]},{"bearer":[]},{"api-key":[]}],"summary":"Upload main cover photo for board","tags":["Boards Owner"]}},"/boards/tasks":{"get":{"operationId":"TasksPublicController_getAllTasks","parameters":[{"name":"pageNumber","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"pageSize","required":false,"in":"query","schema":{"minimum":1,"maximum":20,"default":10,"type":"number"}}],"responses":{"200":{"description":"Paginated list of tasks","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GlobalTaskListResponse"}}}}},"summary":"Get list of all tasks from all boards created by all users","tags":["Tasks Public"],"security":[{"api-key":[]}]}},"/boards/{boardId}/tasks":{"get":{"operationId":"TasksPublicController_getBoardTasks","parameters":[{"name":"boardId","required":true,"in":"path","schema":{"type":"string"}},{"name":"pageNumber","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"pageSize","required":false,"in":"query","schema":{"minimum":1,"maximum":20,"default":10,"type":"number"}}],"responses":{"200":{"description":"Paginated list of tasks for specific board","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardTaskListResponse"}}}},"404":{"description":"Board not found (if applicable)"}},"summary":"Get list of tasks belonging to the specified board","tags":["Tasks Public"],"security":[{"api-key":[]}]},"post":{"operationId":"TasksController_createTask","parameters":[{"name":"boardId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTaskRequestPayload"}}}},"responses":{"201":{"description":"Task successfully created (201 Created)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetTaskOutput"}}}},"403":{"description":"Access forbidden or task limit exceeded (403 Forbidden)"},"404":{"description":"Board not found (404 Not Found)"}},"security":[{"basic":[]},{"bearer":[]},{"bearer":[]},{"api-key":[]}],"summary":"Add a new task to your board","tags":["Tasks Owner"]}},"/boards/{boardId}/tasks/{taskId}":{"get":{"operationId":"TasksPublicController_getTaskDetails","parameters":[{"name":"boardId","required":true,"in":"path","schema":{"type":"string"}},{"name":"taskId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Task details with attachments","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetTaskOutput"}}}},"404":{"description":"Task with this ID not found"}},"summary":"Get task details by ID","tags":["Tasks Public"],"security":[{"api-key":[]}]},"put":{"operationId":"TasksController_updateTask","parameters":[{"name":"boardId","required":true,"in":"path","schema":{"type":"string"}},{"name":"taskId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateTaskRequestPayload"}}}},"responses":{"200":{"description":"Task successfully updated (200 OK)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetTaskOutput"}}}},"403":{"description":"Access forbidden (403 Forbidden)"},"404":{"description":"Task not found (404 Not Found)"}},"security":[{"basic":[]},{"bearer":[]},{"bearer":[]},{"api-key":[]}],"summary":"Update task","tags":["Tasks Owner"]},"delete":{"operationId":"TasksController_deleteTask","parameters":[{"name":"boardId","required":true,"in":"path","schema":{"type":"string"}},{"name":"taskId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Task successfully deleted (200 OK)"},"403":{"description":"Access forbidden (403 Forbidden)"},"404":{"description":"Task not found (404 Not Found)"}},"security":[{"basic":[]},{"bearer":[]},{"bearer":[]},{"api-key":[]}],"summary":"Delete task from your board","tags":["Tasks Owner"]}},"/boards/{boardId}/tasks/{taskId}/reorder":{"put":{"operationId":"TasksController_reorderTask","parameters":[{"name":"boardId","required":true,"in":"path","schema":{"type":"string"}},{"name":"taskId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReorderBoardsRequestPayload"}}}},"responses":{"200":{"description":"Task order successfully updated (200 OK)"},"400":{"description":"Invalid request: cannot move task after itself (400 Bad Request)"},"403":{"description":"Access forbidden (403 Forbidden)"},"404":{"description":"Task or specified putAfterItemId not found (404 Not Found)"}},"security":[{"basic":[]},{"bearer":[]},{"bearer":[]},{"api-key":[]}],"summary":"Change task order within your board","tags":["Tasks Owner"]}},"/boards/{boardId}/tasks/{taskId}/move":{"put":{"operationId":"TasksController_moveTaskToBoard","parameters":[{"name":"boardId","required":true,"in":"path","schema":{"type":"string"}},{"name":"taskId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MoveTaskToBoardRequestPayload"}}}},"responses":{"204":{"description":"Task successfully moved (204 No Content)"},"400":{"description":"Invalid request: cannot move task after itself (400 Bad Request)"},"403":{"description":"Access forbidden (403 Forbidden)"},"404":{"description":"Task or target board not found (404 Not Found)"}},"security":[{"basic":[]},{"bearer":[]},{"bearer":[]},{"api-key":[]}],"summary":"Move task to another board","tags":["Tasks Owner"]}},"/boards/{boardId}/tasks/{taskId}/attachments":{"post":{"operationId":"TasksController_uploadAttachment","parameters":[{"name":"taskId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":"Files successfully uploaded (201 Created)"},"403":{"description":"Maximum attachment count exceeded (403 Forbidden)"},"404":{"description":"Task not found (404 Not Found)"}},"security":[{"basic":[]},{"bearer":[]},{"bearer":[]},{"api-key":[]}],"summary":"Upload one or more files for task","tags":["Tasks Owner"]}},"/boards/{boardId}/tasks/{taskId}/attachments/{attachmentId}":{"delete":{"operationId":"TasksController_deleteAttachment","parameters":[{"name":"taskId","required":true,"in":"path","schema":{"type":"string"}},{"name":"attachmentId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Attachment successfully deleted (200 OK)"},"403":{"description":"Access forbidden (403 Forbidden)"},"404":{"description":"Task or attachment not found (404 Not Found)"}},"security":[{"basic":[]},{"bearer":[]},{"bearer":[]},{"api-key":[]}],"summary":"Delete file for task","tags":["Tasks Owner"]}},"/auth/oauth-redirect":{"get":{"description":"The callback URL to redirect after granting access, <a target=\"_blank\" href=\"https://oauth.apihub.it-incubator.io/realms/apihub/protocol/openid-connect/auth?client_id=trelly&response_type=code&redirect_uri=http://localhost:3000/oauth2/callback&scope=openid\">https://oauth.apihub.it-incubator.io/realms/apihub/protocol/openid-connect/auth?client_id=trelly&response_type=code&redirect_uri=http://localhost:3000/oauth2/callback&scope=openid</a>","operationId":"AuthController_OauthRedirect","parameters":[{"name":"callbackUrl","required":true,"in":"query","description":"The callback URL to redirect after grand access,\n     https://oauth.apihub.it-incubator.io/realms/apihub/protocol/openid-connect/auth?client_id=trelly&response_type=code&redirect_uri=http://localhost:3000/oauth2/callback&scope=openid","schema":{"example":"http://localhost:3000/oauth2/callback","type":"string"}}],"responses":{"200":{"description":"OK: Redirect executed successfully"}},"summary":"OAuth redirect","tags":["Authentication"],"security":[{"api-key":[]}]}},"/auth/login":{"post":{"operationId":"AuthController_login","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRequestPayload"}}}},"responses":{"200":{"description":"OK: Token pair retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefreshOutput"}}}},"400":{"description":"Bad Request: Invalid request format or required parameters are missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestException"}}}},"401":{"description":"Unauthorized: Code is invalid, expired, missing, or redirectUri does not match","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedException"}}}}},"summary":"Log in using the code received after OAuth authorization redirect","tags":["Authentication"],"security":[{"api-key":[]}]}},"/auth/refresh":{"post":{"operationId":"AuthController_refresh","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefreshRequestPayload"}}}},"responses":{"200":{"description":"OK: Token pair refreshed successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefreshOutput"}}}},"401":{"description":"Unauthorized: Refresh token is invalid, expired, or missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedException"}}}}},"summary":"Refresh refresh/access token pair","tags":["Authentication"],"security":[{"api-key":[]}]}},"/auth/logout":{"post":{"operationId":"AuthController_logout","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LogoutRequestPayload"}}}},"responses":{"204":{"description":"No Content: Refresh token deactivated; access token remains valid."}},"summary":"Deactivate refresh token","tags":["Authentication"],"security":[{"api-key":[]}]}},"/auth/me":{"get":{"operationId":"AuthController_getMe","parameters":[],"responses":{"200":{"description":"OK: Successfully retrieved user information","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetMeOutput"}}}},"401":{"description":"Unauthorized: access token is missing or invalid"}},"security":[{"basic":[]},{"bearer":[]},{"api-key":[]}],"summary":"Get current user by access token","tags":["Authentication"]}}},"info":{"title":"Trelly API","description":"API for learning. Create your own analogue of a popular music service, such as Trello, Jira...","version":"1.0","contact":{}},"tags":[{"name":"Tasks Public"},{"name":"Boards Public"},{"name":"Authentication"},{"name":"Boards Owner"},{"name":"Tasks Owner"},{"name":"Tags"}],"servers":[{"url":"https://trelly.it-incubator.app/api/1.0"}],"components":{"securitySchemes":{"api-key":{"type":"apiKey","in":"header","name":"api-key","description":"API key required to access endpoints"},"basic":{"type":"http","scheme":"basic"},"bearer":{"scheme":"bearer","bearerFormat":"JWT","type":"http"}},"schemas":{"BoardImagesOutputDTO":{"type":"object","properties":{"main":{"description":"Must contain original photo size (156x156) and small photo size (48x48)","type":"array","items":{"type":"array"}}}},"BoardOutputDTO":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"addedAt":{"type":"string"},"updatedAt":{"type":"string"},"order":{"type":"number"},"isImportant":{"type":"boolean"},"images":{"$ref":"#/components/schemas/BoardImagesOutputDTO"}},"required":["id","title","description","addedAt","updatedAt","order","isImportant","images"]},"BoardListItemJsonApiData":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","example":"boards"},"attributes":{"$ref":"#/components/schemas/BoardOutputDTO"}},"required":["id","type","attributes"]},"GetMyBoardsOutput":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/BoardListItemJsonApiData"}}},"required":["data"]},"BoardCreateInputDTO":{"type":"object","properties":{"title":{"type":"string","minLength":1,"maxLength":100},"description":{"type":"string","maxLength":1000}},"required":["title"]},"BoardSingleData":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","example":"boards"},"attributes":{"$ref":"#/components/schemas/BoardOutputDTO"}},"required":["id","type","attributes"]},"GetBoardOutput":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/BoardSingleData"}},"required":["data"]},"BoardUpdateInputDTO":{"type":"object","properties":{"title":{"type":"string","minLength":1,"maxLength":100},"description":{"type":"string","maxLength":1000},"isImportant":{"type":"boolean"}},"required":["title","isImportant"]},"ReorderBoardsRequestPayload":{"type":"object","properties":{"putAfterItemId":{"type":"object","description":"ID of the element after which to place the current one"}}},"GlobalTaskListItemDto":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"boardId":{"type":"string"},"status":{"type":"number","enum":[0,1,2,3]},"priority":{"type":"number","enum":[0,1,2,3,4]},"addedAt":{"type":"string"},"attachmentsCount":{"type":"number"}},"required":["id","boardId","status","priority","addedAt","attachmentsCount"]},"GlobalTaskListItemJsonApiData":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","example":"tasks"},"attributes":{"$ref":"#/components/schemas/GlobalTaskListItemDto"}},"required":["id","type","attributes"]},"JsonApiMeta":{"type":"object","properties":{"page":{"type":"number"},"pageSize":{"type":"number"},"totalCount":{"type":"number"},"pagesCount":{"type":"number"}},"required":["page","pageSize","totalCount","pagesCount"]},"GlobalTaskListResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/GlobalTaskListItemJsonApiData"}},"meta":{"$ref":"#/components/schemas/JsonApiMeta"}},"required":["data","meta"]},"BoardTaskListItemDto":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"addedAt":{"format":"date-time","type":"string"},"order":{"type":"number"},"status":{"type":"number","enum":[0,1,2,3]},"priority":{"type":"number","enum":[0,1,2,3,4]},"startDate":{"type":"object"},"deadline":{"type":"object"},"updatedAt":{"format":"date-time","type":"string"},"attachments":{"type":"array","items":{"type":"string"}}},"required":["id","title","addedAt","order","status","priority","updatedAt","attachments"]},"BoardTaskListItemJsonApiData":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","example":"tasks"},"attributes":{"$ref":"#/components/schemas/BoardTaskListItemDto"}},"required":["id","type","attributes"]},"BoardTaskListResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/BoardTaskListItemJsonApiData"}},"meta":{"$ref":"#/components/schemas/JsonApiMeta"}},"required":["data","meta"]},"TaskDetailsDto":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"description":{"type":"object"},"boardId":{"type":"string"},"boardTitle":{"type":"string"},"order":{"type":"number"},"status":{"type":"number","enum":[0,1,2,3]},"priority":{"type":"number","enum":[0,1,2,3,4]},"startDate":{"type":"object"},"deadline":{"type":"object"},"addedAt":{"format":"date-time","type":"string"},"updatedAt":{"type":"string"},"attachments":{"type":"array","items":{"type":"string"}}},"required":["id","boardId","boardTitle","order","status","priority","addedAt","updatedAt","attachments"]},"TaskDetailsData":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","example":"tasks"},"attributes":{"$ref":"#/components/schemas/TaskDetailsDto"}},"required":["id","type","attributes"]},"GetTaskOutput":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/TaskDetailsData"}},"required":["data"]},"GetBoardsOutput":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/BoardListItemJsonApiData"}},"meta":{"$ref":"#/components/schemas/JsonApiMeta"}},"required":["data","meta"]},"CreateTaskRequestPayload":{"type":"object","properties":{"title":{"type":"string","maxLength":100}},"required":["title"]},"MoveTaskToBoardRequestPayload":{"type":"object","properties":{"targetBoardId":{"type":"string","description":"ID of the new board to move the task to"},"putAfterItemId":{"type":"string","description":"ID of the task after which to insert the moved task"}},"required":["targetBoardId"]},"UpdateTaskRequestPayload":{"type":"object","properties":{"title":{"type":"string","maxLength":100},"description":{"type":"string","maxLength":1000},"status":{"type":"number","enum":[0,1,2,3]},"priority":{"type":"number","enum":[0,1,2,3,4]},"startDate":{"type":"string"},"deadline":{"type":"string"}},"required":["title"]},"LoginRequestPayload":{"type":"object","properties":{"code":{"type":"string","description":"Authorization code received from OAuth server after redirect"},"redirectUri":{"type":"string","description":"Specify the same redirect URI used in the initial OAuth server request","example":"http://localhost:3000/oauth2/callback"},"accessTokenTTL":{"type":"string","description":"Access token lifetime (default \"3m\"); must be a string like \"60s\", \"3m\", \"2h\", or \"1d\"","example":"3m"},"rememberMe":{"type":"boolean","description":"Refresh token lifetime: if true, 30 days; if false, 30 minutes. accessTokenTTL must not exceed the refresh token lifetime"}},"required":["code","redirectUri","rememberMe"]},"RefreshOutput":{"type":"object","properties":{"refreshToken":{"type":"string"},"accessToken":{"type":"string"}},"required":["refreshToken","accessToken"]},"BadRequestException":{"type":"object","properties":{}},"UnauthorizedException":{"type":"object","properties":{}},"RefreshRequestPayload":{"type":"object","properties":{"refreshToken":{"type":"string"}},"required":["refreshToken"]},"LogoutRequestPayload":{"type":"object","properties":{"refreshToken":{"type":"string"}},"required":["refreshToken"]},"GetMeOutput":{"type":"object","properties":{"userId":{"type":"string"},"login":{"type":"string"}},"required":["userId","login"]},"BinaryFile":{"type":"string","format":"binary","description":"Файл в multipart/form-data"}}}}