Files
tool-evaluator/lib/api-spec/openapi.yaml
T
opencode 01c70085db
Build & Push Docker Image / build (push) Successful in 6m52s
feat: tiered costs feature + admin tier management + tag selection
- costs: nullable notes (fix create without notes), drop renewalDate
  (schema + API + UI), gate POST/PATCH/DELETE to admin + costs feature
- feature middleware: admin-aware hasFeature + getEntitlements union;
  /auth/me and login return resolved entitlements
- users: tier enum (free/premium/enterprise) in create/update/list,
  admin UI tier select + tier badge
- tags: GET /tags/all, TagInput autocomplete in new/edit tool forms,
  feature suggestions on focus, query invalidation on create/update
- openapi: nullable ToolUpdate urls, ToolUpdate tier fields, listAllTags
- Dockerfile: push-force to drop renewal_date column
2026-08-02 01:10:47 +02:00

891 lines
21 KiB
YAML

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
/tags/all:
get:
operationId: listAllTags
tags: [tools]
summary: List all distinct tag strings across all tools
responses:
"200":
description: All known tags
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]
tier:
type: string
enum: [free, premium, enterprise]
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]
tier:
type: string
enum: [free, premium, enterprise]
UserRoleUpdate:
type: object
properties:
role:
type: string
enum: [admin, user]
tier:
type: string
enum: [free, premium, enterprise]
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", "null"]
iconUrl:
type: ["string", "null"]
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]
tier:
type: string
enum: [free, premium, enterprise]
entitlements:
type: array
items:
type: string
isLocal:
type: boolean
ErrorResponse:
type: object
required: [error]
properties:
error:
type: string