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: API specification servers: - url: /api description: Base API path tags: - name: health description: Health operations - name: scans description: Skill scans and audit reports - name: providers description: Configurable external AI providers - name: prompts description: Configurable AI analysis prompts - name: rules description: Static rule catalog configuration - name: dashboard description: Dashboard summaries 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" /dashboard: get: operationId: getDashboard tags: [dashboard] summary: Dashboard summary description: Aggregated statistics across all scans. responses: "200": description: Dashboard summary content: application/json: schema: $ref: "#/components/schemas/DashboardSummary" /scans: get: operationId: listScans tags: [scans] summary: List scan history responses: "200": description: List of scans (most recent first) content: application/json: schema: type: array items: $ref: "#/components/schemas/Scan" post: operationId: createScan tags: [scans] summary: Upload a skill and run an audit description: >- Accepts a skill as a base64 ZIP archive, a single base64 file, or pasted text, runs the static rule engine and (optionally) the configured AI analysis, and returns the completed report. requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/SkillScanInput" responses: "201": description: Completed scan report content: application/json: schema: $ref: "#/components/schemas/ScanDetail" "400": description: Invalid input content: application/json: schema: $ref: "#/components/schemas/ApiError" /scans/{id}: get: operationId: getScan tags: [scans] summary: Get a scan report with findings parameters: - name: id in: path required: true schema: type: integer responses: "200": description: Scan report content: application/json: schema: $ref: "#/components/schemas/ScanDetail" "404": description: Not found content: application/json: schema: $ref: "#/components/schemas/ApiError" delete: operationId: deleteScan tags: [scans] summary: Delete a scan report parameters: - name: id in: path required: true schema: type: integer responses: "204": description: Deleted /providers: get: operationId: listProviders tags: [providers] summary: List configured AI providers responses: "200": description: List of providers (tokens masked) content: application/json: schema: type: array items: $ref: "#/components/schemas/AiProvider" post: operationId: createProvider tags: [providers] summary: Create an AI provider requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/AiProviderInput" responses: "201": description: Created provider content: application/json: schema: $ref: "#/components/schemas/AiProvider" /providers/{id}: patch: operationId: updateProvider tags: [providers] summary: Update an AI provider parameters: - name: id in: path required: true schema: type: integer requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/AiProviderUpdate" responses: "200": description: Updated provider content: application/json: schema: $ref: "#/components/schemas/AiProvider" delete: operationId: deleteProvider tags: [providers] summary: Delete an AI provider parameters: - name: id in: path required: true schema: type: integer responses: "204": description: Deleted /providers/{id}/test: post: operationId: testProvider tags: [providers] summary: Test the connection to an AI provider parameters: - name: id in: path required: true schema: type: integer responses: "200": description: Test result content: application/json: schema: $ref: "#/components/schemas/ProviderTestResult" /prompts: get: operationId: listPrompts tags: [prompts] summary: List configurable AI prompts responses: "200": description: List of prompts content: application/json: schema: type: array items: $ref: "#/components/schemas/Prompt" /prompts/{id}: patch: operationId: updatePrompt tags: [prompts] summary: Update an AI prompt parameters: - name: id in: path required: true schema: type: integer requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/PromptUpdate" responses: "200": description: Updated prompt content: application/json: schema: $ref: "#/components/schemas/Prompt" /rules: get: operationId: listRules tags: [rules] summary: List the static rule catalog responses: "200": description: List of rules content: application/json: schema: type: array items: $ref: "#/components/schemas/Rule" /rules/{id}: patch: operationId: updateRule tags: [rules] summary: Update a rule's severity or enabled state parameters: - name: id in: path required: true schema: type: integer requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/RuleUpdate" responses: "200": description: Updated rule content: application/json: schema: $ref: "#/components/schemas/Rule" components: schemas: HealthStatus: type: object properties: status: type: string required: - status ApiError: type: object required: [error] properties: error: type: string SkillScanInput: type: object required: [source, useAi] properties: name: type: ["string", "null"] description: Optional display name for the scan source: type: string enum: [zip, file, text] useAi: type: boolean description: Whether to also run the configured AI analysis contentBase64: type: ["string", "null"] description: Base64 content for source=zip or source=file filename: type: ["string", "null"] description: Original filename for source=file or source=zip text: type: ["string", "null"] description: Raw skill text for source=text Scan: type: object required: - id - name - source - status - verdict - riskScore - fileCount - aiUsed - findingCounts - createdAt properties: id: type: integer name: type: string source: type: string enum: [zip, file, text] status: type: string enum: [completed, failed] verdict: type: string enum: [pass, review, block] riskScore: type: integer fileCount: type: integer aiUsed: type: boolean aiError: type: ["string", "null"] findingCounts: $ref: "#/components/schemas/FindingCounts" createdAt: type: string FindingCounts: type: object required: [critical, high, medium, low, info, security, privacy, total] properties: critical: type: integer high: type: integer medium: type: integer low: type: integer info: type: integer security: type: integer privacy: type: integer total: type: integer ScanFile: type: object required: [path, kind, size] properties: path: type: string kind: type: string enum: [instruction, script, resource] language: type: ["string", "null"] size: type: integer Finding: type: object required: - id - ruleId - axis - severity - title - description - detectedBy properties: id: type: integer ruleId: type: string axis: type: string enum: [security, privacy] severity: type: string enum: [critical, high, medium, low, info] title: type: string description: type: string remediation: type: ["string", "null"] file: type: ["string", "null"] line: type: ["integer", "null"] snippet: type: ["string", "null"] detectedBy: type: string enum: [static, ai] ScanDetail: allOf: - $ref: "#/components/schemas/Scan" - type: object required: [files, findings] properties: files: type: array items: $ref: "#/components/schemas/ScanFile" findings: type: array items: $ref: "#/components/schemas/Finding" AiProvider: type: object required: - id - name - apiType - baseUrl - model - enabled - hasToken - tokenPreview - createdAt properties: id: type: integer name: type: string apiType: type: string enum: [openai, anthropic, custom] baseUrl: type: string model: type: string enabled: type: boolean hasToken: type: boolean tokenPreview: type: string description: Masked preview of the stored token (e.g. "sk-...abcd") createdAt: type: string AiProviderInput: type: object required: [name, apiType, baseUrl, model] properties: name: type: string minLength: 1 apiType: type: string enum: [openai, anthropic, custom] baseUrl: type: string minLength: 1 model: type: string minLength: 1 apiToken: type: string enabled: type: boolean AiProviderUpdate: type: object properties: name: type: string minLength: 1 apiType: type: string enum: [openai, anthropic, custom] baseUrl: type: string minLength: 1 model: type: string minLength: 1 apiToken: type: string description: Provide to replace the stored token; omit to keep existing enabled: type: boolean ProviderTestResult: type: object required: [ok] properties: ok: type: boolean message: type: ["string", "null"] Prompt: type: object required: [id, key, name, content, updatedAt] properties: id: type: integer key: type: string name: type: string content: type: string updatedAt: type: string PromptUpdate: type: object properties: name: type: string minLength: 1 content: type: string minLength: 1 Rule: type: object required: - id - ruleId - axis - category - title - description - severity - detectionType - enabled properties: id: type: integer ruleId: type: string axis: type: string enum: [security, privacy] category: type: string title: type: string description: type: string severity: type: string enum: [critical, high, medium, low, info] detectionType: type: string enum: [regex, heuristic, ai] enabled: type: boolean RuleUpdate: type: object properties: severity: type: string enum: [critical, high, medium, low, info] enabled: type: boolean DashboardSummary: type: object required: - totalScans - avgRiskScore - verdictCounts - severityTotals - axisTotals - recentScans - topRules properties: totalScans: type: integer avgRiskScore: type: integer verdictCounts: $ref: "#/components/schemas/VerdictCounts" severityTotals: $ref: "#/components/schemas/SeverityTotals" axisTotals: $ref: "#/components/schemas/AxisTotals" recentScans: type: array items: $ref: "#/components/schemas/Scan" topRules: type: array items: $ref: "#/components/schemas/RuleStat" VerdictCounts: type: object required: [pass, review, block] properties: pass: type: integer review: type: integer block: type: integer SeverityTotals: type: object required: [critical, high, medium, low, info] properties: critical: type: integer high: type: integer medium: type: integer low: type: integer info: type: integer AxisTotals: type: object required: [security, privacy] properties: security: type: integer privacy: type: integer RuleStat: type: object required: [ruleId, title, axis, count] properties: ruleId: type: string title: type: string axis: type: string enum: [security, privacy] count: type: integer