{
  "openapi": "3.0.3",
  "info": {
    "title": "Whop Trends API",
    "version": "1.0.0",
    "description": "This spec is a menu. It is not a key. Paid routes stay gated. Whop Trends Pro is $29/month or $228/year. Public REST needs no account. Paid REST in this spec is marked security: apiKey and Pro required. Live REST authenticates with a Clerk session cookie, not a wt_ key. MCP authenticates with Authorization: Bearer wt_.... A key works on /api/mcp only. Versioning: spec version 1.0.0 documents unversioned /api/* URLs. Breaking changes will ship under /api/v2/* with Deprecation and Sunset response headers on v1. Rate limits: responses include X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset; 429 responses include Retry-After (seconds). Errors: JSON bodies use ErrorResponse with error, message, and code fields."
  },
  "servers": [
    {
      "url": "https://whoptrends.com"
    }
  ],
  "tags": [
    {
      "name": "Public",
      "description": "No account. Anonymous callers are limited."
    },
    {
      "name": "MCP",
      "description": "Model Context Protocol. Pro required. Bearer wt_ key."
    },
    {
      "name": "Paid",
      "description": "Pro required. Live auth is a Clerk session cookie."
    }
  ],
  "paths": {
    "/api/health": {
      "get": {
        "tags": [
          "Public"
        ],
        "summary": "Healthcheck",
        "description": "Railway deploy healthcheck. Confirms the process can reach Postgres.",
        "operationId": "getHealth",
        "security": [],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HealthResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/products/paginated": {
      "get": {
        "tags": [
          "Public"
        ],
        "summary": "Paginated products",
        "description": "Public catalogue page. Anonymous callers get page 1 with a row cap. Paid sessions can page further.",
        "operationId": "listProductsPaginated",
        "security": [],
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "description": "Page number. Default 1."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "description": "Page size. Default 100."
          },
          {
            "name": "search",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Text search in title or headline."
          },
          {
            "name": "industry_type",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Category slug."
          },
          {
            "name": "business_type",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Business model slug."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedProductsResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/tools/calculator": {
      "get": {
        "tags": [
          "Public"
        ],
        "summary": "Category revenue calculator",
        "description": "Public category revenue benchmarks for the free calculator tool.",
        "operationId": "getCategoryCalculator",
        "security": [],
        "parameters": [
          {
            "name": "category",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Industry type slug."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CategoryCalculatorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/tools/checker": {
      "get": {
        "tags": [
          "Public"
        ],
        "summary": "Product checker",
        "description": "Public product health report for the free checker tool.",
        "operationId": "getProductChecker",
        "security": [],
        "parameters": [
          {
            "name": "route",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Product route slug."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProductCheckerResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/mcp": {
      "post": {
        "tags": [
          "MCP"
        ],
        "summary": "MCP server",
        "description": "Pro required. Streamable HTTP Model Context Protocol. Send Authorization: Bearer wt_.... Tools: search_products, get_product, market_trends, category_metrics, check_product. A wt_ key authenticates this path only. Discovery manifest: https://whoptrends.com/.well-known/mcp.json.",
        "operationId": "postMcp",
        "security": [
          {
            "apiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "MCP JSON-RPC response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/McpJsonRpcResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "MCP"
        ],
        "summary": "MCP server (GET transport)",
        "description": "Pro required. Same MCP server as POST. Streamable HTTP GET. Send Authorization: Bearer wt_....",
        "operationId": "getMcp",
        "security": [
          {
            "apiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "MCP transport response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/McpJsonRpcResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/products": {
      "get": {
        "tags": [
          "Paid"
        ],
        "summary": "Products with estimates",
        "description": "Pro required. Dashboard product list. Revenue fields stay gated for unpaid sessions.",
        "operationId": "listProducts",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedProductsResponse"
                }
              }
            }
          },
          "401": {
            "description": "Authentication required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Access denied",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/products/count": {
      "get": {
        "tags": [
          "Paid"
        ],
        "summary": "Filtered product count",
        "description": "Pro required. Count matching the current dashboard filters.",
        "operationId": "countProducts",
        "security": [
          {
            "apiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericApiResponse"
                }
              }
            }
          },
          "401": {
            "description": "Authentication required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Access denied",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/products/stats": {
      "get": {
        "tags": [
          "Paid"
        ],
        "summary": "Product aggregate stats",
        "description": "Pro required. Paid-only aggregates for the dashboard.",
        "operationId": "getProductStats",
        "security": [
          {
            "apiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericApiResponse"
                }
              }
            }
          },
          "401": {
            "description": "Authentication required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Access denied",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/products/categories": {
      "get": {
        "tags": [
          "Paid"
        ],
        "summary": "Product categories",
        "description": "Pro required. Distinct industry types. Paid only.",
        "operationId": "listProductCategories",
        "security": [
          {
            "apiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericApiResponse"
                }
              }
            }
          },
          "401": {
            "description": "Authentication required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Access denied",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/products/business-types": {
      "get": {
        "tags": [
          "Paid"
        ],
        "summary": "Business types",
        "description": "Pro required. Distinct business models. Paid only.",
        "operationId": "listBusinessTypes",
        "security": [
          {
            "apiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericApiResponse"
                }
              }
            }
          },
          "401": {
            "description": "Authentication required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Access denied",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/apps": {
      "get": {
        "tags": [
          "Paid"
        ],
        "summary": "Whop apps directory",
        "description": "Pro required. Marketplace apps for the dashboard directory.",
        "operationId": "listApps",
        "security": [
          {
            "apiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericApiResponse"
                }
              }
            }
          },
          "401": {
            "description": "Authentication required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Access denied",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/trends": {
      "get": {
        "tags": [
          "Paid"
        ],
        "summary": "Market trends",
        "description": "Pro required. Category launch and growth data. Unpaid sessions receive a sanitized payload.",
        "operationId": "getTrends",
        "security": [
          {
            "apiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericApiResponse"
                }
              }
            }
          },
          "401": {
            "description": "Authentication required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Access denied",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/api-keys": {
      "get": {
        "tags": [
          "Paid"
        ],
        "summary": "List MCP API keys",
        "description": "Pro required. Lists the caller's MCP keys. Live auth is a Clerk session.",
        "operationId": "listApiKeys",
        "security": [
          {
            "apiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericApiResponse"
                }
              }
            }
          },
          "401": {
            "description": "Authentication required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Access denied",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Paid"
        ],
        "summary": "Create MCP API key",
        "description": "Pro required. Mints a wt_ key. The raw key is returned once. Live auth is a Clerk session.",
        "operationId": "createApiKey",
        "security": [
          {
            "apiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiKeyCreateResponse"
                }
              }
            }
          },
          "401": {
            "description": "Authentication required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Access denied",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/api-keys/{id}": {
      "delete": {
        "tags": [
          "Paid"
        ],
        "summary": "Revoke MCP API key",
        "description": "Pro required. Revokes one key. Live auth is a Clerk session.",
        "operationId": "revokeApiKey",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericApiResponse"
                }
              }
            }
          },
          "401": {
            "description": "Authentication required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Access denied",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "ErrorResponse": {
        "type": "object",
        "required": [
          "error",
          "code"
        ],
        "properties": {
          "error": {
            "type": "string",
            "description": "Short error label."
          },
          "message": {
            "type": "string",
            "description": "Human-readable detail."
          },
          "code": {
            "type": "string",
            "description": "Machine-readable code, e.g. AUTH_REQUIRED."
          },
          "details": {
            "description": "Optional validation or debug details."
          }
        }
      },
      "AccessMeta": {
        "type": "object",
        "required": [
          "hasAccess",
          "tier"
        ],
        "properties": {
          "hasAccess": {
            "type": "boolean"
          },
          "tier": {
            "type": "string",
            "enum": [
              "free",
              "paid",
              "lifetime"
            ]
          },
          "upgradeMessage": {
            "type": "string"
          },
          "preview": {
            "type": "boolean"
          }
        }
      },
      "PaginationMeta": {
        "type": "object",
        "properties": {
          "page": {
            "type": "integer"
          },
          "limit": {
            "type": "integer"
          },
          "total": {
            "type": "integer"
          },
          "totalPages": {
            "type": "integer"
          },
          "hasNext": {
            "type": "boolean"
          },
          "hasPrev": {
            "type": "boolean"
          }
        }
      },
      "HealthResponse": {
        "type": "object",
        "required": [
          "status",
          "database"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "ok",
              "error"
            ]
          },
          "database": {
            "type": "string",
            "enum": [
              "ok",
              "unreachable"
            ]
          }
        }
      },
      "ProductSummary": {
        "type": "object",
        "properties": {
          "whop_id": {
            "type": "string"
          },
          "route": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "headline": {
            "type": "string",
            "nullable": true
          },
          "industry_type": {
            "type": "string",
            "nullable": true
          },
          "monthly_revenue": {
            "type": "number",
            "nullable": true
          },
          "member_count": {
            "type": "integer",
            "nullable": true
          }
        }
      },
      "PaginatedProductsResponse": {
        "type": "object",
        "required": [
          "data",
          "access"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ProductSummary"
            }
          },
          "access": {
            "$ref": "#/components/schemas/AccessMeta"
          },
          "meta": {
            "type": "object",
            "properties": {
              "pagination": {
                "$ref": "#/components/schemas/PaginationMeta"
              }
            }
          }
        }
      },
      "CategoryCalculatorResponse": {
        "type": "object",
        "properties": {
          "category": {
            "type": "string"
          },
          "benchmarks": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "ProductCheckerResponse": {
        "type": "object",
        "properties": {
          "route": {
            "type": "string"
          },
          "score": {
            "type": "number"
          },
          "report": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "McpJsonRpcResponse": {
        "type": "object",
        "description": "JSON-RPC 2.0 envelope from the MCP server.",
        "properties": {
          "jsonrpc": {
            "type": "string",
            "enum": [
              "2.0"
            ]
          },
          "id": {},
          "result": {},
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "integer"
              },
              "message": {
                "type": "string"
              }
            }
          }
        }
      },
      "GenericApiResponse": {
        "type": "object",
        "required": [
          "data",
          "access"
        ],
        "properties": {
          "data": {
            "description": "Endpoint-specific payload."
          },
          "access": {
            "$ref": "#/components/schemas/AccessMeta"
          },
          "meta": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "ApiKeyRecord": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "prefix": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ApiKeyCreateResponse": {
        "type": "object",
        "properties": {
          "data": {
            "type": "object",
            "properties": {
              "key": {
                "type": "string",
                "description": "Raw wt_ key. Shown once."
              },
              "record": {
                "$ref": "#/components/schemas/ApiKeyRecord"
              }
            }
          },
          "access": {
            "$ref": "#/components/schemas/AccessMeta"
          }
        }
      }
    },
    "securitySchemes": {
      "apiKey": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "wt_",
        "description": "Pro required. MCP: Authorization: Bearer wt_.... Paid REST in this spec uses the same marker; live REST uses a Clerk session cookie, not this key."
      }
    }
  }
}