openapi: 3.1.0 info: # Do not change the title, if the title changes, the import paths will be broken title: Api version: 0.1.0 description: ToolRate API — Tool listing and rating platform servers: - url: /api description: Base API path tags: - name: health description: Health operations - name: tools description: Tool management - name: ratings description: Tool ratings - name: analytics description: Analytics and aggregated statistics - name: auth description: Authentication - name: users description: User management (admin only) - name: audit description: Audit log paths: /healthz: get: operationId: healthCheck tags: [health] summary: Health check description: Returns server health status responses: "200": description: Healthy content: application/json: schema: $ref: "#/components/schemas/HealthStatus" /tools: get: operationId: listTools tags: [tools] summary: List all tools parameters: - name: category in: query required: false schema: type: string - name: search in: query required: false schema: type: string - name: sort in: query required: false schema: type: string enum: [newest, top_rated, most_reviewed] responses: "200": description: List of tools content: application/json: schema: type: array items: $ref: "#/components/schemas/ToolWithStats" post: operationId: createTool tags: [tools] summary: Create a new tool requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/ToolInput" responses: "201": description: Created tool content: application/json: schema: $ref: "#/components/schemas/Tool" "400": description: Validation error content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" /tools/{id}: get: operationId: getTool tags: [tools] summary: Get a tool by ID parameters: - name: id in: path required: true schema: type: integer responses: "200": description: Tool details content: application/json: schema: $ref: "#/components/schemas/ToolWithStats" "404": description: Not found content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" patch: operationId: updateTool tags: [tools] summary: Update a tool parameters: - name: id in: path required: true schema: type: integer requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/ToolUpdate" responses: "200": description: Updated tool content: application/json: schema: $ref: "#/components/schemas/Tool" "404": description: Not found content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" delete: operationId: deleteTool tags: [tools] summary: Delete a tool parameters: - name: id in: path required: true schema: type: integer responses: "204": description: Deleted "404": description: Not found content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" /tools/{id}/ratings: get: operationId: listToolRatings tags: [ratings] summary: List ratings for a tool parameters: - name: id in: path required: true schema: type: integer responses: "200": description: Ratings list content: application/json: schema: type: array items: $ref: "#/components/schemas/Rating" post: operationId: createRating tags: [ratings] summary: Submit a rating for a tool parameters: - name: id in: path required: true schema: type: integer requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/RatingInput" responses: "201": description: Created rating content: application/json: schema: $ref: "#/components/schemas/Rating" "400": description: Validation error content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" "404": description: Tool not found content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" /analytics/summary: get: operationId: getAnalyticsSummary tags: [analytics] summary: Overall platform statistics responses: "200": description: Platform-level summary stats content: application/json: schema: $ref: "#/components/schemas/AnalyticsSummary" /analytics/top-tools: get: operationId: getTopTools tags: [analytics] summary: Top-rated tools parameters: - name: limit in: query required: false schema: type: integer - name: metric in: query required: false schema: type: string enum: [usefulness, usability, combined] responses: "200": description: Top tools list content: application/json: schema: type: array items: $ref: "#/components/schemas/TopToolEntry" /analytics/by-category: get: operationId: getAnalyticsByCategory tags: [analytics] summary: Rating statistics grouped by category responses: "200": description: Per-category statistics content: application/json: schema: type: array items: $ref: "#/components/schemas/CategoryStats" /analytics/rating-distribution: get: operationId: getRatingDistribution tags: [analytics] summary: Distribution of rating scores across the platform parameters: - name: toolId in: query required: false schema: type: integer responses: "200": description: Rating score distribution content: application/json: schema: $ref: "#/components/schemas/RatingDistribution" /categories: get: operationId: listCategories tags: [tools] summary: List all distinct tool categories responses: "200": description: Categories list content: application/json: schema: type: array items: type: string /features/all: get: operationId: listAllFeatures tags: [tools] summary: List all distinct feature strings across all tools responses: "200": description: All known features content: application/json: schema: type: array items: type: string /auth/mode: get: operationId: getAuthMode tags: [auth] summary: Get authentication mode (oidc or local) responses: "200": description: Auth mode content: application/json: schema: $ref: "#/components/schemas/AuthMode" /auth/login: post: operationId: localLogin tags: [auth] summary: Local username/password login requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/LocalLoginInput" responses: "200": description: Logged in successfully content: application/json: schema: $ref: "#/components/schemas/AuthUser" "401": description: Invalid credentials content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" /auth/me: get: operationId: getMe tags: [auth] summary: Get current authenticated user responses: "200": description: Current user info content: application/json: schema: $ref: "#/components/schemas/AuthUser" "401": description: Not authenticated content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" /users: get: operationId: listUsers tags: [users] summary: List all local users (admin only) responses: "200": description: User list content: application/json: schema: type: array items: $ref: "#/components/schemas/User" "401": description: Not authenticated content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" "403": description: Forbidden content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" post: operationId: createUser tags: [users] summary: Create a new local user (admin only) requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/UserCreateInput" responses: "201": description: Created user content: application/json: schema: $ref: "#/components/schemas/User" "400": description: Validation error content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" "409": description: Username already exists content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" /users/{id}: patch: operationId: updateUser tags: [users] summary: Update user role (admin only) parameters: - name: id in: path required: true schema: type: integer requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/UserRoleUpdate" responses: "200": description: Updated user content: application/json: schema: $ref: "#/components/schemas/User" "404": description: User not found content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" delete: operationId: deleteUser tags: [users] summary: Delete a user (admin only) parameters: - name: id in: path required: true schema: type: integer responses: "204": description: Deleted /audit-logs: get: operationId: listAuditLogs tags: [audit] summary: List audit log entries (admin only) parameters: - name: entityType in: query required: false schema: type: string - name: entityId in: query required: false schema: type: integer - name: limit in: query required: false schema: type: integer responses: "200": description: Audit log entries content: application/json: schema: type: array items: $ref: "#/components/schemas/AuditLog" components: schemas: HealthStatus: type: object properties: status: type: string required: - status AuthMode: type: object required: [mode] properties: mode: type: string enum: [oidc, local] LocalLoginInput: type: object required: [username, password] properties: username: type: string password: type: string User: type: object required: [id, username, role, createdAt] properties: id: type: integer username: type: string email: type: ["string", "null"] role: type: string enum: [admin, user] createdAt: type: string format: date-time UserCreateInput: type: object required: [username, password] properties: username: type: string minLength: 2 password: type: string minLength: 6 email: type: string role: type: string enum: [admin, user] UserRoleUpdate: type: object required: [role] properties: role: type: string enum: [admin, user] AuditLog: type: object required: [id, entityType, action, userId, username, createdAt] properties: id: type: integer entityType: type: string entityId: type: ["integer", "null"] action: type: string userId: type: string username: type: string changes: type: ["string", "null"] createdAt: type: string format: date-time Tool: type: object required: [id, name, description, category, createdAt, updatedAt] properties: id: type: integer name: type: string description: type: string category: type: string websiteUrl: type: ["string", "null"] iconUrl: type: ["string", "null"] createdBy: type: ["string", "null"] features: type: array items: type: string tags: type: array items: type: string createdAt: type: string format: date-time updatedAt: type: string format: date-time ToolWithStats: type: object required: [id, name, description, category, createdAt, updatedAt, ratingCount, avgUsefulness, avgUsability, avgCombined] properties: id: type: integer name: type: string description: type: string category: type: string websiteUrl: type: ["string", "null"] iconUrl: type: ["string", "null"] createdBy: type: ["string", "null"] features: type: array items: type: string tags: type: array items: type: string createdAt: type: string format: date-time updatedAt: type: string format: date-time ratingCount: type: integer avgUsefulness: type: ["number", "null"] avgUsability: type: ["number", "null"] avgCombined: type: ["number", "null"] ToolInput: type: object required: [name, description, category] properties: name: type: string minLength: 1 description: type: string minLength: 1 category: type: string minLength: 1 websiteUrl: type: string iconUrl: type: string features: type: array items: type: string tags: type: array items: type: string ToolUpdate: type: object properties: name: type: string minLength: 1 description: type: string category: type: string websiteUrl: type: string iconUrl: type: string features: type: array items: type: string tags: type: array items: type: string Rating: type: object required: [id, toolId, usefulness, usability, createdAt] properties: id: type: integer toolId: type: integer usefulness: type: integer minimum: 1 maximum: 5 usability: type: integer minimum: 1 maximum: 5 comment: type: ["string", "null"] reviewerName: type: ["string", "null"] createdAt: type: string format: date-time RatingInput: type: object required: [usefulness, usability] properties: usefulness: type: integer minimum: 1 maximum: 5 usability: type: integer minimum: 1 maximum: 5 comment: type: string reviewerName: type: string AnalyticsSummary: type: object required: [totalTools, totalRatings, avgUsefulness, avgUsability, avgCombined, categoriesCount] properties: totalTools: type: integer totalRatings: type: integer avgUsefulness: type: ["number", "null"] avgUsability: type: ["number", "null"] avgCombined: type: ["number", "null"] categoriesCount: type: integer mostRatedTool: $ref: "#/components/schemas/ToolWithStats" TopToolEntry: type: object required: [tool, score, ratingCount] properties: tool: $ref: "#/components/schemas/ToolWithStats" score: type: number ratingCount: type: integer CategoryStats: type: object required: [category, toolCount, totalRatings, avgUsefulness, avgUsability] properties: category: type: string toolCount: type: integer totalRatings: type: integer avgUsefulness: type: ["number", "null"] avgUsability: type: ["number", "null"] RatingDistribution: type: object required: [usefulness, usability] properties: usefulness: type: array items: $ref: "#/components/schemas/ScoreBucket" usability: type: array items: $ref: "#/components/schemas/ScoreBucket" ScoreBucket: type: object required: [score, count] properties: score: type: integer count: type: integer AuthUser: type: object required: [sub] properties: sub: type: string email: type: ["string", "null"] name: type: ["string", "null"] preferredUsername: type: ["string", "null"] role: type: string enum: [admin, user] isLocal: type: boolean ErrorResponse: type: object required: [error] properties: error: type: string