{
  "openapi": "3.1.0",
  "info": {
    "title": "VoteShip API",
    "version": "1.0.0",
    "description": "AI-native feature request management API. Collect, prioritize, and ship features your users actually want. Works with humans and AI agents equally.",
    "contact": {
      "name": "VoteShip Support",
      "url": "https://voteship.app/contact",
      "email": "support@voteship.app"
    },
    "license": {
      "name": "Proprietary"
    }
  },
  "externalDocs": {
    "description": "VoteShip Developer Platform: API, authentication, webhooks, MCP, and agent instructions",
    "url": "https://voteship.app/developers"
  },
  "servers": [
    {
      "url": "https://app.voteship.app/api/v1",
      "description": "Production"
    }
  ],
  "security": [
    {
      "BearerAuth": []
    }
  ],
  "components": {
    "securitySchemes": {
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "API key from project settings. Format: `Bearer sk_...`"
      }
    },
    "schemas": {
      "Post": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "example": "V1StGXR8_Z5jdHi6B-myT" },
          "projectId": { "type": "string" },
          "title": { "type": "string", "example": "PDF export support" },
          "description": { "type": ["string", "null"] },
          "status": { "type": "string", "enum": ["PENDING", "APPROVED", "IN_PROGRESS", "COMPLETE", "CLOSED", "ARCHIVED"] },
          "voteCount": { "type": "integer", "example": 42 },
          "metadata": { "type": ["object", "null"] },
          "createdAt": { "type": "string", "format": "date-time" },
          "updatedAt": { "type": "string", "format": "date-time" }
        }
      },
      "PostWithTags": {
        "allOf": [
          { "$ref": "#/components/schemas/Post" },
          {
            "type": "object",
            "properties": {
              "tags": {
                "type": "array",
                "items": { "$ref": "#/components/schemas/Tag" }
              }
            }
          }
        ]
      },
      "Tag": {
        "type": "object",
        "properties": {
          "id": { "type": "string" },
          "label": { "type": "string", "example": "Mobile" },
          "theme": { "type": "string", "example": "#6366f1" },
          "createdAt": { "type": "string", "format": "date-time" }
        }
      },
      "Vote": {
        "type": "object",
        "properties": {
          "id": { "type": "string" },
          "boardUserId": { "type": ["string", "null"] },
          "anonymousId": { "type": ["string", "null"] },
          "createdAt": { "type": "string", "format": "date-time" },
          "boardUserName": { "type": ["string", "null"] },
          "boardUserEmail": { "type": ["string", "null"] }
        }
      },
      "Comment": {
        "type": "object",
        "properties": {
          "id": { "type": "string" },
          "postId": { "type": "string" },
          "body": { "type": "string" },
          "authorName": { "type": ["string", "null"] },
          "isInternalNote": { "type": "boolean" },
          "createdAt": { "type": "string", "format": "date-time" }
        }
      },
      "Release": {
        "type": "object",
        "properties": {
          "id": { "type": "string" },
          "title": { "type": "string", "example": "v2.1 — PDF Export" },
          "content": { "type": ["string", "null"] },
          "isDraft": { "type": "boolean" },
          "publishedAt": { "type": ["string", "null"], "format": "date-time" },
          "createdAt": { "type": "string", "format": "date-time" },
          "updatedAt": { "type": "string", "format": "date-time" }
        }
      },
      "BoardUser": {
        "type": "object",
        "properties": {
          "id": { "type": "string" },
          "email": { "type": ["string", "null"] },
          "name": { "type": ["string", "null"] },
          "avatarUrl": { "type": ["string", "null"] },
          "userSpend": { "type": "string" },
          "createdAt": { "type": "string", "format": "date-time" }
        }
      },
      "ActivityEntry": {
        "type": "object",
        "properties": {
          "id": { "type": "string" },
          "action": { "type": "string" },
          "entityType": { "type": "string" },
          "entityId": { "type": ["string", "null"] },
          "metadata": { "type": ["object", "null"] },
          "createdAt": { "type": "string", "format": "date-time" }
        }
      },
      "Pagination": {
        "type": "object",
        "properties": {
          "page": { "type": "integer" },
          "limit": { "type": "integer" },
          "total": { "type": "integer" },
          "totalPages": { "type": "integer" }
        }
      },
      "ApiError": {
        "type": "object",
        "required": ["error_code", "message", "resolution_hint", "retry", "docs_url"],
        "properties": {
          "error": { "type": "string", "description": "Legacy alias of message for backward compatibility" },
          "error_code": { "type": "string", "example": "POST_NOT_FOUND" },
          "message": { "type": "string", "example": "Post not found" },
          "resolution_hint": { "type": "string", "example": "Use GET /api/v1/posts to list valid post IDs" },
          "suggestion": { "type": "string", "description": "Alias of resolution_hint" },
          "available_actions": { "type": "array", "items": { "type": "string" } },
          "retry": { "type": "boolean" },
          "docs_url": { "type": "string", "example": "https://voteship.app/docs" }
        }
      }
    }
  },
  "paths": {
    "/": {
      "get": {
        "operationId": "apiDiscovery",
        "summary": "API Discovery",
        "description": "Returns API metadata, available endpoints, authentication info, and rate limits. No authentication required.",
        "security": [],
        "responses": {
          "200": {
            "description": "API discovery information",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "name": { "type": "string" },
                    "version": { "type": "string" },
                    "description": { "type": "string" },
                    "docs_url": { "type": "string" },
                    "openapi_url": { "type": "string" },
                    "mcp_package": { "type": "string" }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/posts": {
      "get": {
        "operationId": "listPosts",
        "summary": "List posts",
        "description": "List feature requests with optional filters for status, sorting, and pagination.",
        "parameters": [
          { "name": "status", "in": "query", "schema": { "type": "string", "enum": ["PENDING", "APPROVED", "IN_PROGRESS", "COMPLETE", "CLOSED", "ARCHIVED"] }, "description": "Filter by status" },
          { "name": "sort", "in": "query", "schema": { "type": "string", "enum": ["newest", "oldest", "most_votes", "least_votes"], "default": "newest" }, "description": "Sort order" },
          { "name": "page", "in": "query", "schema": { "type": "integer", "default": 1 }, "description": "Page number" },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "default": 50, "minimum": 1, "maximum": 100 }, "description": "Results per page" },
          { "name": "tag", "in": "query", "schema": { "type": "string" }, "description": "Filter by tag ID" },
          { "name": "search", "in": "query", "schema": { "type": "string" }, "description": "Search in title and description" }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of posts",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": { "type": "array", "items": { "$ref": "#/components/schemas/PostWithTags" } },
                    "pagination": { "$ref": "#/components/schemas/Pagination" }
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "createPost",
        "summary": "Create a post",
        "description": "Create a new feature request. Posts are created with PENDING status by default.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["title"],
                "properties": {
                  "title": { "type": "string", "maxLength": 200, "description": "Post title (required, max 200 chars)" },
                  "description": { "type": "string", "description": "Post description (markdown supported)" },
                  "status": { "type": "string", "enum": ["PENDING", "APPROVED", "IN_PROGRESS", "COMPLETE", "CLOSED", "ARCHIVED"], "default": "PENDING" },
                  "tagIds": { "type": "array", "items": { "type": "string" }, "description": "Array of tag IDs to attach" }
                }
              },
              "example": { "title": "PDF export support", "description": "Allow users to export reports as PDF", "tagIds": ["tag_abc123"] }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Post created",
            "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/Post" } } } } }
          },
          "400": { "description": "Validation error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" } } } }
        }
      }
    },
    "/posts/{postId}": {
      "get": {
        "operationId": "getPost",
        "summary": "Get a post",
        "description": "Retrieve a single post with its tags.",
        "parameters": [{ "name": "postId", "in": "path", "required": true, "schema": { "type": "string" } }],
        "responses": {
          "200": { "description": "Post details", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/PostWithTags" } } } } } },
          "404": { "description": "Post not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" } } } }
        }
      },
      "patch": {
        "operationId": "updatePost",
        "summary": "Update a post",
        "description": "Update a post's title, description, status, or tags. Only provided fields are updated.",
        "parameters": [{ "name": "postId", "in": "path", "required": true, "schema": { "type": "string" } }],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "title": { "type": "string", "maxLength": 200 },
                  "description": { "type": "string" },
                  "status": { "type": "string", "enum": ["PENDING", "APPROVED", "IN_PROGRESS", "COMPLETE", "CLOSED", "ARCHIVED"] },
                  "tagIds": { "type": "array", "items": { "type": "string" } }
                }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "Updated post", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/Post" } } } } } },
          "404": { "description": "Post not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" } } } }
        }
      },
      "delete": {
        "operationId": "deletePost",
        "summary": "Delete a post",
        "description": "Permanently delete a feature request and all its votes, comments, and tags.",
        "parameters": [{ "name": "postId", "in": "path", "required": true, "schema": { "type": "string" } }],
        "responses": {
          "200": { "description": "Post deleted", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean" } } } } } },
          "404": { "description": "Post not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" } } } }
        }
      }
    },
    "/posts/similar": {
      "get": {
        "operationId": "searchSimilarPosts",
        "summary": "Semantic search for similar posts",
        "description": "Search for posts semantically similar to the given query text using pgvector embeddings.",
        "parameters": [
          { "name": "query", "in": "query", "required": true, "schema": { "type": "string" }, "description": "Text to search for" },
          { "name": "threshold", "in": "query", "schema": { "type": "number", "default": 0.7 }, "description": "Minimum similarity score (0-1)" },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "default": 5 }, "description": "Maximum results to return" }
        ],
        "responses": {
          "200": {
            "description": "Similar posts ranked by similarity",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": { "type": "string" },
                          "title": { "type": "string" },
                          "similarity": { "type": "number", "example": 0.92 },
                          "voteCount": { "type": "integer" },
                          "status": { "type": "string" }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/posts/from-text": {
      "post": {
        "operationId": "submitFromText",
        "summary": "Submit feedback from natural language",
        "description": "Submit unstructured text. AI extracts a structured post, checks for duplicates, and auto-categorizes. Requires Growth plan or higher.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["text"],
                "properties": {
                  "text": { "type": "string", "description": "Raw text from any source (Slack, email, support ticket, etc.)" },
                  "source": { "type": "string", "description": "Where the feedback came from (e.g., 'slack', 'email')" },
                  "source_url": { "type": "string", "description": "Link back to the original message" }
                }
              },
              "example": { "text": "It would be great if we could export data as PDF. Multiple customers asked.", "source": "slack" }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Post created from text",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "action": { "type": "string", "enum": ["created", "matched_duplicate"] },
                        "post": { "$ref": "#/components/schemas/Post" },
                        "similar_posts": { "type": "array", "items": { "type": "object" } }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/posts/{postId}/votes": {
      "get": {
        "operationId": "getVoters",
        "summary": "List voters for a post",
        "parameters": [{ "name": "postId", "in": "path", "required": true, "schema": { "type": "string" } }],
        "responses": {
          "200": { "description": "List of votes", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/Vote" } } } } } } }
        }
      },
      "post": {
        "operationId": "addVote",
        "summary": "Vote on a post",
        "description": "Add a vote to a feature request. Each user can only vote once per post.",
        "parameters": [{ "name": "postId", "in": "path", "required": true, "schema": { "type": "string" } }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "boardUserId": { "type": "string", "description": "ID of a registered board user" },
                  "anonymousId": { "type": "string", "description": "Anonymous identifier (if no board user)" }
                }
              }
            }
          }
        },
        "responses": {
          "201": { "description": "Vote created", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/Vote" } } } } } },
          "409": { "description": "Duplicate vote", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" } } } }
        }
      }
    },
    "/posts/{postId}/comments": {
      "get": {
        "operationId": "getComments",
        "summary": "List comments on a post",
        "description": "List comments for a post. By default returns only public comments. Use the `internal` query parameter to include internal notes.",
        "parameters": [
          { "name": "postId", "in": "path", "required": true, "schema": { "type": "string" } },
          { "name": "internal", "in": "query", "schema": { "type": "string", "enum": ["true", "all"] }, "description": "Filter by visibility: 'true' for internal notes only, 'all' for both public and internal. Omit for public comments only." }
        ],
        "responses": {
          "200": { "description": "List of comments", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/Comment" } } } } } } }
        }
      },
      "post": {
        "operationId": "addComment",
        "summary": "Add a comment to a post",
        "parameters": [{ "name": "postId", "in": "path", "required": true, "schema": { "type": "string" } }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["body"],
                "properties": {
                  "body": { "type": "string", "description": "Comment text" },
                  "authorName": { "type": "string", "description": "Display name of the commenter" },
                  "isInternalNote": { "type": "boolean", "default": false, "description": "If true, creates an internal note visible only to team members" }
                }
              }
            }
          }
        },
        "responses": {
          "201": { "description": "Comment created", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/Comment" } } } } } }
        }
      }
    },
    "/tags": {
      "get": {
        "operationId": "listTags",
        "summary": "List tags",
        "description": "List all tags for the project.",
        "responses": {
          "200": { "description": "List of tags", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/Tag" } } } } } } }
        }
      },
      "post": {
        "operationId": "createTag",
        "summary": "Create a tag",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["label"],
                "properties": {
                  "label": { "type": "string", "maxLength": 50 },
                  "theme": { "type": "string", "description": "Hex color code", "default": "#6366f1" }
                }
              }
            }
          }
        },
        "responses": {
          "201": { "description": "Tag created", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/Tag" } } } } } }
        }
      }
    },
    "/tags/{tagId}": {
      "patch": {
        "operationId": "updateTag",
        "summary": "Update a tag",
        "parameters": [{ "name": "tagId", "in": "path", "required": true, "schema": { "type": "string" } }],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "label": { "type": "string", "maxLength": 50 },
                  "theme": { "type": "string" }
                }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "Updated tag", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/Tag" } } } } } },
          "404": { "description": "Tag not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" } } } }
        }
      },
      "delete": {
        "operationId": "deleteTag",
        "summary": "Delete a tag",
        "parameters": [{ "name": "tagId", "in": "path", "required": true, "schema": { "type": "string" } }],
        "responses": {
          "200": { "description": "Tag deleted", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean" } } } } } }
        }
      }
    },
    "/users": {
      "get": {
        "operationId": "listUsers",
        "summary": "List board users",
        "description": "List users who have submitted feedback, voted, or interacted with the board.",
        "parameters": [
          { "name": "page", "in": "query", "schema": { "type": "integer", "default": 1 } },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "default": 50, "maximum": 100 } }
        ],
        "responses": {
          "200": { "description": "Paginated list of board users", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/BoardUser" } }, "pagination": { "$ref": "#/components/schemas/Pagination" } } } } } }
        }
      }
    },
    "/users/{userId}": {
      "get": {
        "operationId": "getUser",
        "summary": "Get a board user",
        "description": "Retrieve a single board user by ID.",
        "parameters": [{ "name": "userId", "in": "path", "required": true, "schema": { "type": "string" } }],
        "responses": {
          "200": { "description": "Board user details", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/BoardUser" } } } } } },
          "404": { "description": "User not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" } } } }
        }
      },
      "patch": {
        "operationId": "updateUser",
        "summary": "Update a board user",
        "description": "Update a board user's spend/revenue value, name, or email. Only provided fields are updated.",
        "parameters": [{ "name": "userId", "in": "path", "required": true, "schema": { "type": "string" } }],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "userSpend": { "type": "number", "minimum": 0, "description": "Monthly spend/revenue for prioritization (e.g. 49.99)" },
                  "name": { "type": ["string", "null"], "description": "Display name" },
                  "email": { "type": ["string", "null"], "description": "Email address" }
                }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "Updated board user", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/BoardUser" } } } } } },
          "400": { "description": "Validation error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" } } } },
          "404": { "description": "User not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" } } } }
        }
      }
    },
    "/roadmap": {
      "get": {
        "operationId": "getRoadmap",
        "summary": "Get the product roadmap",
        "description": "Returns posts grouped by status (APPROVED, IN_PROGRESS, COMPLETE) sorted by vote count.",
        "responses": {
          "200": {
            "description": "Roadmap data",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "approved": { "type": "array", "items": { "$ref": "#/components/schemas/PostWithTags" } },
                        "in_progress": { "type": "array", "items": { "$ref": "#/components/schemas/PostWithTags" } },
                        "complete": { "type": "array", "items": { "$ref": "#/components/schemas/PostWithTags" } }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/releases": {
      "get": {
        "operationId": "listReleases",
        "summary": "List releases",
        "description": "List published changelog releases, newest first.",
        "parameters": [
          { "name": "page", "in": "query", "schema": { "type": "integer", "default": 1 } },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "default": 20, "maximum": 100 } }
        ],
        "responses": {
          "200": { "description": "Paginated releases", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/Release" } }, "pagination": { "$ref": "#/components/schemas/Pagination" } } } } } }
        }
      },
      "post": {
        "operationId": "createRelease",
        "summary": "Create a release",
        "description": "Create a changelog release. Set isDraft to false to publish immediately.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["title"],
                "properties": {
                  "title": { "type": "string" },
                  "content": { "type": "string", "description": "Release notes content (markdown)" },
                  "isDraft": { "type": "boolean", "default": true },
                  "label": { "type": "string", "description": "Optional version label" }
                }
              }
            }
          }
        },
        "responses": {
          "201": { "description": "Release created", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/Release" } } } } } }
        }
      }
    },
    "/releases/{releaseId}": {
      "get": {
        "operationId": "getRelease",
        "summary": "Get a release",
        "parameters": [{ "name": "releaseId", "in": "path", "required": true, "schema": { "type": "string" } }],
        "responses": {
          "200": { "description": "Release details", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/Release" } } } } } },
          "404": { "description": "Release not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" } } } }
        }
      },
      "patch": {
        "operationId": "updateRelease",
        "summary": "Update a release",
        "parameters": [{ "name": "releaseId", "in": "path", "required": true, "schema": { "type": "string" } }],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "title": { "type": "string" },
                  "content": { "type": "string" },
                  "isDraft": { "type": "boolean" }
                }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "Updated release", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/Release" } } } } } }
        }
      },
      "delete": {
        "operationId": "deleteRelease",
        "summary": "Delete a release",
        "parameters": [{ "name": "releaseId", "in": "path", "required": true, "schema": { "type": "string" } }],
        "responses": {
          "200": { "description": "Release deleted", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean" } } } } } }
        }
      }
    },
    "/analytics": {
      "get": {
        "operationId": "getAnalyticsSummary",
        "summary": "Analytics summary",
        "description": "Get aggregated analytics for a time period including stats, top posts, and trending tags.",
        "parameters": [
          { "name": "period", "in": "query", "schema": { "type": "string", "enum": ["week", "month", "quarter"], "default": "week" }, "description": "Time period for aggregation" }
        ],
        "responses": {
          "200": {
            "description": "Analytics data",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "period": { "type": "string" },
                        "stats": {
                          "type": "object",
                          "properties": {
                            "new_posts": { "type": "integer" },
                            "total_votes": { "type": "integer" },
                            "new_comments": { "type": "integer" },
                            "page_views": { "type": "integer" }
                          }
                        },
                        "top_posts": { "type": "array", "items": { "$ref": "#/components/schemas/Post" } },
                        "trending_tags": { "type": "array", "items": { "type": "object", "properties": { "label": { "type": "string" }, "post_count": { "type": "integer" } } } }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/activity": {
      "get": {
        "operationId": "listActivity",
        "summary": "Activity log",
        "description": "List recent activity entries for the project.",
        "parameters": [
          { "name": "page", "in": "query", "schema": { "type": "integer", "default": 1 } },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "default": 50 } },
          { "name": "entityType", "in": "query", "schema": { "type": "string" }, "description": "Filter by entity type (post, tag, release, etc.)" }
        ],
        "responses": {
          "200": { "description": "Activity entries", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/ActivityEntry" } }, "pagination": { "$ref": "#/components/schemas/Pagination" } } } } } }
        }
      }
    },
    "/ai/triage": {
      "post": {
        "operationId": "triageInbox",
        "summary": "AI-powered inbox triage",
        "description": "AI analyzes unreviewed (PENDING) posts and suggests statuses, tags, and identifies duplicates. Requires Growth plan or higher.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "limit": { "type": "integer", "default": 20, "description": "Max posts to triage" }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Triage recommendations",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "triaged": { "type": "integer" },
                        "recommendations": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "postId": { "type": "string" },
                              "title": { "type": "string" },
                              "suggested_status": { "type": "string" },
                              "suggested_tags": { "type": "array", "items": { "type": "string" } },
                              "duplicate_of": { "type": ["string", "null"] },
                              "priority": { "type": "string", "enum": ["high", "medium", "low"] },
                              "reason": { "type": "string" }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "403": { "description": "Plan required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" } } } }
        }
      }
    },
    "/ai/summary": {
      "post": {
        "operationId": "getFeedbackSummary",
        "summary": "AI feedback summary",
        "description": "Generate a natural language summary of recent feedback trends. Requires Growth plan or higher.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "period": { "type": "string", "enum": ["week", "month", "quarter"], "default": "week" }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "AI-generated summary",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "summary": { "type": "string" },
                        "highlights": { "type": "array", "items": { "type": "string" } },
                        "top_themes": { "type": "array", "items": { "type": "string" } },
                        "stats": { "type": "object" }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/ai/sprint-plan": {
      "post": {
        "operationId": "planSprint",
        "summary": "AI sprint planning",
        "description": "AI suggests what to build next based on votes, themes, and strategy. Requires Growth plan or higher.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "capacity": { "type": "integer", "default": 5, "description": "Max items for the sprint" },
                  "strategy": { "type": "string", "enum": ["balanced", "revenue", "popular", "quick-wins"], "default": "balanced" }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Sprint plan",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "strategy": { "type": "string" },
                        "items": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "postId": { "type": "string" },
                              "title": { "type": "string" },
                              "voteCount": { "type": "integer" },
                              "reason": { "type": "string" }
                            }
                          }
                        },
                        "summary": { "type": "string" }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/import": {
      "post": {
        "operationId": "importCSV",
        "summary": "Import from CSV / Canny",
        "description": "Import feature requests from a CSV file or Canny export.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["data"],
                "properties": {
                  "data": { "type": "array", "items": { "type": "object" }, "description": "Array of rows, each containing at least a 'title' field" },
                  "source": { "type": "string", "enum": ["csv", "canny"], "default": "csv" }
                }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "Import results" }
        }
      }
    },
    "/integrations/slack": {
      "get": {
        "operationId": "getSlackIntegration",
        "summary": "Get Slack integration",
        "responses": { "200": { "description": "Slack config" } }
      },
      "put": {
        "operationId": "upsertSlackIntegration",
        "summary": "Configure Slack integration",
        "requestBody": { "content": { "application/json": { "schema": { "type": "object", "required": ["webhookUrl"], "properties": { "webhookUrl": { "type": "string" }, "channel": { "type": "string" }, "enabled": { "type": "boolean" } } } } } },
        "responses": { "200": { "description": "Updated" }, "201": { "description": "Created" } }
      },
      "delete": {
        "operationId": "deleteSlackIntegration",
        "summary": "Remove Slack integration",
        "responses": { "200": { "description": "Removed" } }
      }
    },
    "/integrations/github": {
      "get": {
        "operationId": "getGitHubIntegration",
        "summary": "Get GitHub integration",
        "responses": { "200": { "description": "GitHub config" } }
      },
      "put": {
        "operationId": "upsertGitHubIntegration",
        "summary": "Configure GitHub integration",
        "requestBody": { "content": { "application/json": { "schema": { "type": "object", "required": ["repoOwner", "repoName"], "properties": { "repoOwner": { "type": "string" }, "repoName": { "type": "string" }, "accessToken": { "type": "string" }, "enabled": { "type": "boolean" }, "syncLabels": { "type": "boolean" }, "autoCreateIssues": { "type": "boolean" } } } } } },
        "responses": { "200": { "description": "Updated" }, "201": { "description": "Created" } }
      },
      "delete": {
        "operationId": "deleteGitHubIntegration",
        "summary": "Remove GitHub integration",
        "responses": { "200": { "description": "Removed" } }
      }
    },
    "/integrations/linear": {
      "get": {
        "operationId": "getLinearIntegration",
        "summary": "Get Linear integration",
        "responses": { "200": { "description": "Linear config" } }
      },
      "put": {
        "operationId": "upsertLinearIntegration",
        "summary": "Configure Linear integration",
        "requestBody": { "content": { "application/json": { "schema": { "type": "object", "required": ["apiKey"], "properties": { "apiKey": { "type": "string" }, "teamId": { "type": "string" }, "enabled": { "type": "boolean" }, "autoCreateIssues": { "type": "boolean" } } } } } },
        "responses": { "200": { "description": "Updated" }, "201": { "description": "Created" } }
      },
      "delete": {
        "operationId": "deleteLinearIntegration",
        "summary": "Remove Linear integration",
        "responses": { "200": { "description": "Removed" } }
      }
    },
    "/integrations/discord": {
      "get": {
        "operationId": "getDiscordIntegration",
        "summary": "Get Discord integration",
        "responses": { "200": { "description": "Discord config" } }
      },
      "put": {
        "operationId": "upsertDiscordIntegration",
        "summary": "Configure Discord integration",
        "requestBody": { "content": { "application/json": { "schema": { "type": "object", "required": ["webhookUrl"], "properties": { "webhookUrl": { "type": "string" }, "enabled": { "type": "boolean" } } } } } },
        "responses": { "200": { "description": "Updated" }, "201": { "description": "Created" } }
      },
      "delete": {
        "operationId": "deleteDiscordIntegration",
        "summary": "Remove Discord integration",
        "responses": { "200": { "description": "Removed" } }
      }
    },
    "/integrations/webhooks": {
      "get": {
        "operationId": "listWebhooks",
        "summary": "List webhook endpoints",
        "responses": { "200": { "description": "List of webhook endpoints" } }
      },
      "post": {
        "operationId": "createWebhook",
        "summary": "Create webhook endpoint",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["url", "events"],
                "properties": {
                  "url": { "type": "string", "description": "HTTPS endpoint URL" },
                  "events": { "type": "array", "items": { "type": "string" }, "description": "Event types to subscribe to, or '*' for all" },
                  "description": { "type": "string" }
                }
              }
            }
          }
        },
        "responses": { "201": { "description": "Webhook created (includes full secret)" } }
      }
    },
    "/integrations/stripe/sync": {
      "post": {
        "operationId": "syncStripeRevenue",
        "summary": "Sync customer MRR from Stripe",
        "description": "Triggers a sync of customer MRR data from the connected Stripe account into board user spend values. Requires Growth plan or higher.",
        "responses": {
          "200": {
            "description": "Sync result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "status": { "type": "string", "enum": ["success", "error", "partial"] },
                        "message": { "type": "string" },
                        "totalCustomers": { "type": "integer" },
                        "matchedUsers": { "type": "integer" },
                        "createdUsers": { "type": "integer" },
                        "skippedNoEmail": { "type": "integer" },
                        "errors": { "type": "array", "items": { "type": "string" } }
                      }
                    }
                  }
                }
              }
            }
          },
          "403": { "description": "Plan upgrade required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" } } } },
          "404": { "description": "No Stripe integration configured", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" } } } }
        }
      }
    },
    "/integrations/webhooks/{webhookId}": {
      "get": {
        "operationId": "getWebhook",
        "summary": "Get webhook with delivery history",
        "parameters": [{ "name": "webhookId", "in": "path", "required": true, "schema": { "type": "string" } }],
        "responses": {
          "200": { "description": "Webhook details with recent deliveries" },
          "404": { "description": "Webhook not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" } } } }
        }
      },
      "patch": {
        "operationId": "updateWebhook",
        "summary": "Update webhook endpoint",
        "parameters": [{ "name": "webhookId", "in": "path", "required": true, "schema": { "type": "string" } }],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": { "type": "string" },
                  "events": { "type": "array", "items": { "type": "string" } },
                  "enabled": { "type": "boolean" },
                  "description": { "type": "string" }
                }
              }
            }
          }
        },
        "responses": { "200": { "description": "Updated webhook" } }
      },
      "delete": {
        "operationId": "deleteWebhook",
        "summary": "Delete webhook endpoint",
        "parameters": [{ "name": "webhookId", "in": "path", "required": true, "schema": { "type": "string" } }],
        "responses": { "200": { "description": "Webhook deleted" } }
      }
    }
  }
}
