{
  "openapi": "3.1.0",
  "info": {
    "title": "Stellaris Companion Public Feedback API",
    "version": "1.0.0",
    "description": "Public endpoints used by galacticfilingcabinet.com to submit website feedback and read the public feedback board. Submit only after explicit user intent; do not create test, duplicate, or speculative reports.",
    "contact": {
      "name": "Stellaris Companion support routes",
      "url": "https://galacticfilingcabinet.com/contact"
    }
  },
  "externalDocs": {
    "description": "Stellaris Companion developer and agent resources",
    "url": "https://galacticfilingcabinet.com/developers"
  },
  "servers": [
    {
      "url": "https://feedback.galacticfilingcabinet.com",
      "description": "Production feedback worker"
    }
  ],
  "paths": {
    "/submit-web": {
      "post": {
        "operationId": "submitWebsiteFeedback",
        "summary": "Submit a website bug report or feature request",
        "description": "Creates or deduplicates a public GitHub issue from website feedback. Requests are rate limited and may require Turnstile depending on deployment settings.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebsiteFeedbackSubmission"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Feedback was accepted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubmissionResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "500": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/health": {
      "get": {
        "operationId": "getHealth",
        "summary": "Check feedback API health",
        "description": "Returns a simple no-store health response for automated availability checks.",
        "responses": {
          "200": {
            "description": "The feedback worker is available.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HealthResponse"
                }
              }
            }
          },
          "405": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/public-feedback": {
      "get": {
        "operationId": "listPublicFeedback",
        "summary": "List public feedback issues",
        "description": "Returns a cached view of recent public GitHub issues tagged as user reports.",
        "parameters": [
          {
            "name": "state",
            "in": "query",
            "description": "Issue state filter.",
            "schema": {
              "type": "string",
              "enum": [
                "open",
                "closed",
                "all"
              ],
              "default": "open"
            }
          },
          {
            "name": "type",
            "in": "query",
            "description": "Feedback category filter.",
            "schema": {
              "type": "string",
              "enum": [
                "all",
                "bug",
                "feature",
                "website"
              ],
              "default": "all"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Maximum number of GitHub issues to request before filtering.",
            "schema": {
              "type": "integer",
              "minimum": 3,
              "maximum": 30,
              "default": 20
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Public feedback feed.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicFeedbackFeed"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "405": {
            "$ref": "#/components/responses/Error"
          },
          "502": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    }
  },
  "components": {
    "responses": {
      "Error": {
        "description": "JSON error response.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      }
    },
    "schemas": {
      "WebsiteFeedbackSubmission": {
        "type": "object",
        "required": [
          "category",
          "summary",
          "description"
        ],
        "properties": {
          "category": {
            "type": "string",
            "enum": [
              "Bug",
              "Strange LLM Response",
              "Missing Content",
              "Suggestion",
              "Feature Request",
              "Other"
            ]
          },
          "summary": {
            "type": "string",
            "minLength": 6,
            "maxLength": 120
          },
          "description": {
            "type": "string",
            "minLength": 16,
            "maxLength": 5000
          },
          "submitted_at_ms": {
            "type": "number"
          },
          "context": {
            "$ref": "#/components/schemas/WebsiteFeedbackContext"
          }
        },
        "additionalProperties": true
      },
      "WebsiteFeedbackContext": {
        "type": "object",
        "properties": {
          "pageUrl": {
            "type": "string",
            "maxLength": 500
          },
          "referrer": {
            "type": "string",
            "maxLength": 500
          },
          "userAgent": {
            "type": "string",
            "maxLength": 260
          },
          "viewport": {
            "type": "string",
            "maxLength": 60,
            "examples": [
              "1440x900"
            ]
          },
          "timezone": {
            "type": "string",
            "maxLength": 80
          },
          "language": {
            "type": "string",
            "maxLength": 40
          },
          "email": {
            "type": "string",
            "format": "email",
            "maxLength": 160
          },
          "allowContact": {
            "type": "boolean"
          },
          "captchaToken": {
            "type": "string",
            "maxLength": 2048
          },
          "honeypot": {
            "type": "string",
            "maxLength": 120
          }
        },
        "additionalProperties": true
      },
      "SubmissionResult": {
        "type": "object",
        "required": [
          "issueUrl",
          "deduplicated"
        ],
        "properties": {
          "issueUrl": {
            "type": "string",
            "format": "uri"
          },
          "deduplicated": {
            "type": "boolean"
          },
          "totalReports": {
            "type": "integer",
            "minimum": 2
          }
        }
      },
      "PublicFeedbackFeed": {
        "type": "object",
        "required": [
          "generated_at",
          "query",
          "count",
          "items"
        ],
        "properties": {
          "generated_at": {
            "type": "string",
            "format": "date-time"
          },
          "query": {
            "$ref": "#/components/schemas/PublicFeedbackQuery"
          },
          "count": {
            "type": "integer",
            "minimum": 0
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PublicFeedbackItem"
            }
          }
        }
      },
      "PublicFeedbackQuery": {
        "type": "object",
        "required": [
          "state",
          "type",
          "limit"
        ],
        "properties": {
          "state": {
            "type": "string",
            "enum": [
              "open",
              "closed",
              "all"
            ]
          },
          "type": {
            "type": "string",
            "enum": [
              "all",
              "bug",
              "feature",
              "website"
            ]
          },
          "limit": {
            "type": "integer",
            "minimum": 3,
            "maximum": 30
          }
        }
      },
      "PublicFeedbackItem": {
        "type": "object",
        "required": [
          "number",
          "title",
          "state",
          "created_at",
          "updated_at",
          "html_url",
          "comments",
          "labels",
          "reactions",
          "category",
          "excerpt",
          "source"
        ],
        "properties": {
          "number": {
            "type": "integer"
          },
          "title": {
            "type": "string"
          },
          "state": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "html_url": {
            "type": "string",
            "format": "uri"
          },
          "comments": {
            "type": "integer",
            "minimum": 0
          },
          "labels": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "reactions": {
            "$ref": "#/components/schemas/ReactionSummary"
          },
          "category": {
            "type": "string"
          },
          "excerpt": {
            "type": "string"
          },
          "source": {
            "type": "string",
            "enum": [
              "website",
              "app"
            ]
          }
        }
      },
      "ReactionSummary": {
        "type": "object",
        "required": [
          "plus1",
          "hearts"
        ],
        "properties": {
          "plus1": {
            "type": "integer",
            "minimum": 0
          },
          "hearts": {
            "type": "integer",
            "minimum": 0
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "required": [
          "error",
          "code",
          "message",
          "resolution"
        ],
        "properties": {
          "error": {
            "type": "string",
            "description": "Backward-compatible human-readable error string."
          },
          "code": {
            "type": "string",
            "description": "Stable machine-readable error code.",
            "examples": [
              "invalid_request",
              "rate_limited",
              "method_not_allowed"
            ]
          },
          "message": {
            "type": "string",
            "description": "Human-readable description of the failure."
          },
          "resolution": {
            "type": "string",
            "description": "Actionable guidance for resolving or recovering from the failure."
          }
        }
      },
      "HealthResponse": {
        "type": "object",
        "required": [
          "status",
          "service",
          "timestamp"
        ],
        "properties": {
          "status": {
            "type": "string",
            "const": "ok"
          },
          "service": {
            "type": "string",
            "const": "stellaris-feedback"
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          }
        }
      }
    }
  }
}
