{
  "openapi": "3.1.1",
  "info": {
    "title": "Fizikplatformu.net Public Content API",
    "version": "1.0.0",
    "description": "Read published science-community blog and module content. These operations do not require sign-in. Account, messaging, moderation, and write operations are not part of this public API. Requests are subject to the site's general rate limit."
  },
  "servers": [{ "url": "https://fizikplatformu.net" }],
  "tags": [
    { "name": "Blog", "description": "Published blog articles." },
    { "name": "Modules", "description": "Published educational and project content." },
    { "name": "Search", "description": "Search published content." }
  ],
  "paths": {
    "/api/content/blog": {
      "get": {
        "tags": ["Blog"],
        "operationId": "listPublicBlogEntries",
        "summary": "List published blog entries",
        "description": "Returns a paginated list of publicly visible blog entries. Signed-in authors may see their own unpublished entries; anonymous clients see only published entries and scheduled entries whose publication time has passed.",
        "parameters": [
          { "$ref": "#/components/parameters/Query" },
          { "$ref": "#/components/parameters/Tag" },
          { "$ref": "#/components/parameters/Category" },
          { "$ref": "#/components/parameters/Page" },
          { "$ref": "#/components/parameters/Limit" }
        ],
        "responses": {
          "200": { "description": "Paginated entries and viewer permissions.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EntryList" } } } },
          "429": { "description": "Request rate limit exceeded; retry after the reset time.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" } } } },
          "500": { "description": "The server could not complete the request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" } } } }
        }
      }
    },
    "/api/content/blog/{slug}": {
      "get": {
        "tags": ["Blog"],
        "operationId": "getPublicBlogEntry",
        "summary": "Get a published blog entry",
        "description": "Returns one publicly visible blog entry, its comments and related entries. Reading an entry increments its view count.",
        "parameters": [{ "$ref": "#/components/parameters/Slug" }],
        "responses": {
          "200": { "description": "Entry detail with comments and related content.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EntryDetail" } } } },
          "404": { "description": "Module or entry not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" } } } },
          "429": { "description": "Request rate limit exceeded; retry after the reset time.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" } } } },
          "500": { "description": "The server could not complete the request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" } } } }
        }
      }
    },
    "/api/content/modules-summary": {
      "get": {
        "tags": ["Modules"],
        "operationId": "listPublicModuleSummaries",
        "summary": "List module summaries",
        "description": "Returns the available modules, their published-entry counts, and their latest published entries.",
        "responses": {
          "200": { "description": "Module catalog with published-entry counts.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ModuleSummaryList" } } } },
          "429": { "description": "Request rate limit exceeded; retry after the reset time.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" } } } },
          "500": { "description": "The server could not complete the request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" } } } }
        }
      }
    },
    "/api/content/modules/{moduleKey}": {
      "get": {
        "tags": ["Modules"],
        "operationId": "listPublicModuleEntries",
        "summary": "List entries in a module",
        "description": "Returns paginated, publicly visible entries for the specified module.",
        "parameters": [
          { "$ref": "#/components/parameters/ModuleKey" },
          { "$ref": "#/components/parameters/Query" },
          { "$ref": "#/components/parameters/Tag" },
          { "$ref": "#/components/parameters/Category" },
          { "$ref": "#/components/parameters/Page" },
          { "$ref": "#/components/parameters/Limit" }
        ],
        "responses": {
          "200": { "description": "Paginated entries and viewer permissions.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EntryList" } } } },
          "404": { "description": "Module or entry not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" } } } },
          "429": { "description": "Request rate limit exceeded; retry after the reset time.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" } } } },
          "500": { "description": "The server could not complete the request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" } } } }
        }
      }
    },
    "/api/content/modules/{moduleKey}/{slug}": {
      "get": {
        "tags": ["Modules"],
        "operationId": "getPublicModuleEntry",
        "summary": "Get a module entry",
        "description": "Returns one publicly visible module entry, comments and related entries. Reading an entry increments its view count.",
        "parameters": [{ "$ref": "#/components/parameters/ModuleKey" }, { "$ref": "#/components/parameters/Slug" }],
        "responses": {
          "200": { "description": "Entry detail with comments and related content.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EntryDetail" } } } },
          "404": { "description": "Module or entry not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" } } } },
          "429": { "description": "Request rate limit exceeded; retry after the reset time.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" } } } },
          "500": { "description": "The server could not complete the request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" } } } }
        }
      }
    },
    "/api/content/search/all": {
      "get": {
        "tags": ["Search"],
        "operationId": "searchPublicContent",
        "summary": "Search published entries",
        "description": "Searches published blog and module entries by title, summary, content, or tag. Queries shorter than two characters return an empty list.",
        "parameters": [{ "$ref": "#/components/parameters/SearchQuery" }, { "$ref": "#/components/parameters/Limit" }],
        "responses": {
          "200": { "description": "Matching published entries.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SearchResults" } } } },
          "429": { "description": "Request rate limit exceeded; retry after the reset time.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" } } } },
          "500": { "description": "The server could not complete the request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" } } } }
        }
      }
    }
  },
  "components": {
    "parameters": {
      "Query": { "name": "q", "in": "query", "description": "Case-insensitive content search term (up to 100 characters).", "schema": { "type": "string", "maxLength": 100 } },
      "SearchQuery": { "name": "q", "in": "query", "description": "Case-insensitive search term; at least two characters for results. Omission returns an empty list.", "schema": { "type": "string", "maxLength": 100 } },
      "Tag": { "name": "tag", "in": "query", "description": "Exact tag filter, normalized to lowercase.", "schema": { "type": "string", "maxLength": 32 } },
      "Category": { "name": "category", "in": "query", "description": "Exact category filter.", "schema": { "type": "string", "maxLength": 80 } },
      "Page": { "name": "page", "in": "query", "description": "One-based page number.", "schema": { "type": "integer", "minimum": 1, "default": 1 } },
      "Limit": { "name": "limit", "in": "query", "description": "Results per page, clamped to 1–50.", "schema": { "type": "integer", "minimum": 1, "maximum": 50, "default": 20 } },
      "Slug": { "name": "slug", "in": "path", "required": true, "description": "Published entry slug.", "schema": { "type": "string", "minLength": 1 } },
      "ModuleKey": { "name": "moduleKey", "in": "path", "required": true, "description": "Module identifier.", "schema": { "type": "string", "enum": ["academy", "labs", "library", "summit"] } }
    },
    "schemas": {
      "ApiError": { "type": "object", "required": ["error", "message", "hint"], "properties": { "error": { "type": "string", "description": "Stable machine-readable error code." }, "message": { "type": "string", "description": "Human-readable error message." }, "hint": { "type": "string", "description": "Suggested resolution." } }, "additionalProperties": true },
      "Author": { "type": "object", "properties": { "id": { "type": ["string", "null"] }, "name": { "type": "string" }, "username": { "type": "string" }, "profileImage": { "type": "string" } }, "additionalProperties": true },
      "Entry": { "type": "object", "required": ["title", "slug", "kind", "tags", "author"], "properties": { "id": { "type": "string" }, "kind": { "type": "string", "enum": ["blog", "module"] }, "moduleKey": { "type": ["string", "null"] }, "title": { "type": "string" }, "slug": { "type": "string" }, "summary": { "type": "string" }, "content": { "type": "string", "description": "Present on detail responses only." }, "status": { "type": "string" }, "coverImage": { "type": "string" }, "tags": { "type": "array", "items": { "type": "string" } }, "category": { "type": "string" }, "metadata": { "type": "object", "additionalProperties": true }, "viewCount": { "type": "integer" }, "likeCount": { "type": "integer" }, "bookmarkCount": { "type": "integer" }, "commentCount": { "type": "integer" }, "publishedAt": { "type": ["string", "null"], "format": "date-time" }, "updatedAt": { "type": ["string", "null"], "format": "date-time" }, "author": { "$ref": "#/components/schemas/Author" }, "canManage": { "type": "boolean" }, "canEdit": { "type": "boolean" }, "canDelete": { "type": "boolean" } }, "additionalProperties": true },
      "Module": { "type": "object", "required": ["key", "name"], "properties": { "key": { "type": "string" }, "name": { "type": "string" }, "icon": { "type": "string" }, "description": { "type": "string" }, "count": { "type": "integer" }, "latest": { "oneOf": [{ "$ref": "#/components/schemas/LatestEntry" }, { "type": "null" }] } } },
      "LatestEntry": { "type": "object", "properties": { "_id": { "type": "string" }, "title": { "type": "string" }, "slug": { "type": "string" }, "summary": { "type": "string" }, "publishedAt": { "type": ["string", "null"], "format": "date-time" } } },
      "Permissions": { "type": "object", "required": ["authenticated", "blogWriter", "moduleWriter", "proAdmin"], "properties": { "authenticated": { "type": "boolean" }, "blogWriter": { "type": "boolean" }, "moduleWriter": { "type": "boolean" }, "proAdmin": { "type": "boolean" } } },
      "Comment": { "type": "object", "properties": { "_id": { "type": "string" }, "entry": { "type": "string" }, "user": { "$ref": "#/components/schemas/Author" }, "text": { "type": "string" }, "isDeleted": { "type": "boolean" }, "createdAt": { "type": "string", "format": "date-time" }, "updatedAt": { "type": "string", "format": "date-time" } }, "additionalProperties": true },
      "Pagination": { "type": "object", "required": ["page", "limit", "total", "totalPages", "hasMore"], "properties": { "page": { "type": "integer" }, "limit": { "type": "integer" }, "total": { "type": "integer" }, "totalPages": { "type": "integer" }, "hasMore": { "type": "boolean" } } },
      "EntryList": { "type": "object", "required": ["entries", "permissions", "module", "pagination", "filters"], "properties": { "entries": { "type": "array", "items": { "$ref": "#/components/schemas/Entry" } }, "permissions": { "$ref": "#/components/schemas/Permissions" }, "module": { "oneOf": [{ "$ref": "#/components/schemas/Module" }, { "type": "null" }] }, "pagination": { "$ref": "#/components/schemas/Pagination" }, "filters": { "type": "object", "properties": { "q": { "type": "string" }, "tag": { "type": "string" }, "category": { "type": "string" } } } } },
      "EntryDetail": { "type": "object", "required": ["entry", "permissions", "module", "comments", "viewerInteractions", "related"], "properties": { "entry": { "$ref": "#/components/schemas/Entry" }, "permissions": { "$ref": "#/components/schemas/Permissions" }, "module": { "oneOf": [{ "$ref": "#/components/schemas/Module" }, { "type": "null" }] }, "comments": { "type": "array", "items": { "$ref": "#/components/schemas/Comment" } }, "viewerInteractions": { "type": "array", "items": { "type": "string" } }, "related": { "type": "array", "items": { "$ref": "#/components/schemas/Entry" } } } },
      "ModuleSummaryList": { "type": "object", "required": ["modules", "permissions"], "properties": { "modules": { "type": "array", "items": { "$ref": "#/components/schemas/Module" } }, "permissions": { "$ref": "#/components/schemas/Permissions" } } },
      "SearchResults": { "type": "object", "required": ["entries"], "properties": { "entries": { "type": "array", "items": { "$ref": "#/components/schemas/Entry" } } } }
    }
  }
}
