{
  "openapi": "3.1.0",
  "info": {
    "title": "TechSnif API",
    "description": "Public, free, no-auth JSON API for TechSnif — a source-first tech news aggregator. Story clusters aggregate coverage from every outlet with social discussion links. No API keys, no rate limits, no paywall.",
    "version": "1.0.0",
    "contact": { "email": "press@mail.techsnif.com", "url": "https://techsnif.com/agents" }
  },
  "servers": [
    { "url": "https://techsnif.com", "description": "Primary" },
    { "url": "https://api.techsnif.com", "description": "API alias" }
  ],
  "paths": {
    "/api/stories": {
      "get": {
        "operationId": "listStories",
        "summary": "List story clusters",
        "description": "Story clusters with flexible sorting, filtering, and offset pagination. Returns real total counts.",
        "parameters": [
          { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 0, "maximum": 80, "default": 20 } },
          { "name": "offset", "in": "query", "schema": { "type": "integer", "minimum": 0, "default": 0 } },
          { "name": "sort", "in": "query", "schema": { "type": "string", "enum": ["rank", "newest"], "default": "rank" } },
          { "name": "tier", "in": "query", "description": "Headline tier: 1=standard, 2=major, 3=top story", "schema": { "type": "integer", "minimum": 1, "maximum": 3 } },
          { "name": "publisher", "in": "query", "description": "Case-insensitive partial match on lead publisher", "schema": { "type": "string" } },
          { "name": "platform", "in": "query", "description": "Only clusters with discussion on this platform", "schema": { "type": "string", "enum": ["x", "bluesky", "linkedin", "forums", "threads", "mastodon", "other"] } },
          { "name": "has_discussion", "in": "query", "description": "Only clusters with any social discussion", "schema": { "type": "string", "enum": ["true"] } },
          { "name": "since", "in": "query", "description": "ISO 8601 date — only clusters seen since this time", "schema": { "type": "string", "format": "date-time" } },
          { "name": "topic", "in": "query", "description": "Topic slug (e.g. ai, security)", "schema": { "type": "string" } }
        ],
        "responses": {
          "200": {
            "description": "Story cluster list",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/StoryListResponse" } } }
          },
          "400": { "$ref": "#/components/responses/BadRequest" }
        }
      }
    },
    "/api/stories/search": {
      "get": {
        "operationId": "searchStories",
        "summary": "Full-text search story clusters",
        "description": "Relevance-ranked full-text search over headlines and excerpts, with substring fallback for word prefixes.",
        "parameters": [
          { "name": "q", "in": "query", "required": true, "schema": { "type": "string", "minLength": 2 } },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 0, "maximum": 50, "default": 10 } },
          { "name": "offset", "in": "query", "schema": { "type": "integer", "minimum": 0, "default": 0 } },
          { "name": "publisher", "in": "query", "schema": { "type": "string" } }
        ],
        "responses": {
          "200": { "description": "Search results", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/StoryListResponse" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" }
        }
      }
    },
    "/api/stories/{slug}": {
      "get": {
        "operationId": "getStory",
        "summary": "Get a story cluster by slug",
        "parameters": [
          { "name": "slug", "in": "path", "required": true, "schema": { "type": "string" } }
        ],
        "responses": {
          "200": { "description": "Full story cluster", "content": { "application/json": { "schema": { "type": "object", "properties": { "story": { "$ref": "#/components/schemas/StoryCluster" } } } } } },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/api/articles": {
      "get": {
        "operationId": "listArticles",
        "summary": "List TechSnif editorial articles",
        "parameters": [
          { "name": "category", "in": "query", "description": "Category key (AI) or slug (artificial-intelligence)", "schema": { "type": "string" } },
          { "name": "tag", "in": "query", "schema": { "type": "string" } },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 0, "maximum": 50, "default": 10 } },
          { "name": "offset", "in": "query", "schema": { "type": "integer", "minimum": 0, "default": 0 } }
        ],
        "responses": {
          "200": { "description": "Article list", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ArticleListResponse" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" }
        }
      }
    },
    "/api/articles/search": {
      "get": {
        "operationId": "searchArticles",
        "summary": "Full-text search articles",
        "parameters": [
          { "name": "q", "in": "query", "required": true, "schema": { "type": "string", "minLength": 2 } },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 0, "maximum": 50, "default": 20 } },
          { "name": "offset", "in": "query", "schema": { "type": "integer", "minimum": 0, "default": 0 } }
        ],
        "responses": {
          "200": { "description": "Search results", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ArticleListResponse" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" }
        }
      }
    },
    "/api/articles/categories": {
      "get": {
        "operationId": "listCategories",
        "summary": "List article categories",
        "responses": {
          "200": { "description": "Category metadata", "content": { "application/json": { "schema": { "type": "object", "properties": { "categories": { "type": "array", "items": { "type": "object" } } } } } } }
        }
      }
    },
    "/api/articles/tags": {
      "get": {
        "operationId": "listTags",
        "summary": "List article tags",
        "responses": {
          "200": { "description": "Tag metadata", "content": { "application/json": { "schema": { "type": "object", "properties": { "tags": { "type": "array", "items": { "type": "object" } }, "total": { "type": "integer" } } } } } }
        }
      }
    },
    "/api/articles/trending": {
      "get": {
        "operationId": "trendingArticles",
        "summary": "Trending articles (48h window)",
        "parameters": [
          { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 0, "maximum": 20, "default": 5 } }
        ],
        "responses": {
          "200": { "description": "Trending articles", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ArticleListResponse" } } } }
        }
      }
    },
    "/api/articles/{slug}": {
      "get": {
        "operationId": "getArticle",
        "summary": "Get a full article by slug",
        "parameters": [
          { "name": "slug", "in": "path", "required": true, "schema": { "type": "string" } }
        ],
        "responses": {
          "200": { "description": "Full article with body", "content": { "application/json": { "schema": { "type": "object", "properties": { "article": { "$ref": "#/components/schemas/Article" } } } } } },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/api/leaderboard": {
      "get": {
        "operationId": "getLeaderboard",
        "summary": "Source and author leaderboards",
        "description": "Which publications and journalists drive the most tech story coverage.",
        "parameters": [
          { "name": "tab", "in": "query", "schema": { "type": "string", "enum": ["sources", "authors"], "default": "sources" } },
          { "name": "window", "in": "query", "schema": { "type": "string", "enum": ["7d", "30d", "all"], "default": "30d" } },
          { "name": "topic", "in": "query", "schema": { "type": "string" } },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 0, "maximum": 100, "default": 50 } }
        ],
        "responses": {
          "200": { "description": "Leaderboard entries", "content": { "application/json": { "schema": { "type": "object", "properties": { "entries": { "type": "array", "items": { "$ref": "#/components/schemas/LeaderboardEntry" } }, "total": { "type": "integer" }, "tab": { "type": "string" }, "timeWindow": { "type": "string" } } } } } },
          "400": { "$ref": "#/components/responses/BadRequest" }
        }
      }
    },
    "/api/events": {
      "get": {
        "operationId": "listEvents",
        "summary": "Upcoming tech industry events",
        "parameters": [
          { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 0, "maximum": 50, "default": 10 } }
        ],
        "responses": {
          "200": { "description": "Event list", "content": { "application/json": { "schema": { "type": "object", "properties": { "events": { "type": "array", "items": { "$ref": "#/components/schemas/TechEvent" } }, "total": { "type": "integer" } } } } } }
        }
      }
    },
    "/api/mcp": {
      "get": {
        "operationId": "getMcpConfig",
        "summary": "MCP setup metadata",
        "responses": {
          "200": { "description": "Machine-readable MCP configuration", "content": { "application/json": { "schema": { "type": "object" } } } }
        }
      },
      "post": {
        "operationId": "mcpJsonRpc",
        "summary": "Hosted MCP endpoint (Streamable HTTP, stateless)",
        "description": "JSON-RPC 2.0 endpoint implementing the Model Context Protocol. Also served at https://mcp.techsnif.com. No authentication required.",
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "type": "object", "description": "JSON-RPC 2.0 request or batch" } } }
        },
        "responses": {
          "200": { "description": "JSON-RPC 2.0 response", "content": { "application/json": { "schema": { "type": "object" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" }
        }
      }
    }
  },
  "components": {
    "responses": {
      "BadRequest": {
        "description": "Invalid request parameters",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "NotFound": {
        "description": "Resource not found",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": { "error": { "type": "string" } }
      },
      "StoryListResponse": {
        "type": "object",
        "properties": {
          "stories": { "type": "array", "items": { "$ref": "#/components/schemas/StoryCluster" } },
          "total": { "type": "integer", "description": "Total matching clusters (for pagination)" },
          "limit": { "type": "integer" },
          "offset": { "type": "integer" }
        }
      },
      "ArticleListResponse": {
        "type": "object",
        "properties": {
          "articles": { "type": "array", "items": { "$ref": "#/components/schemas/Article" } },
          "total": { "type": "integer" },
          "limit": { "type": "integer" },
          "offset": { "type": "integer" }
        }
      },
      "StoryCluster": {
        "type": "object",
        "properties": {
          "id": { "type": "string" },
          "slug": { "type": "string" },
          "headline": { "type": "string" },
          "excerpt": { "type": "string" },
          "whyItMatters": { "type": "string", "description": "AI-generated 2-3 sentence context blurb (top stories only)" },
          "topics": { "type": "array", "items": { "type": "string" } },
          "rank": { "type": "integer" },
          "headlineTier": { "type": "integer", "description": "1=standard, 2=major, 3=top story" },
          "leadUrl": { "type": "string", "format": "uri" },
          "leadTitle": { "type": "string" },
          "sourcePublisher": { "type": "string" },
          "sourceAuthor": { "type": "string" },
          "imageUrl": { "type": "string", "format": "uri" },
          "publishedAt": { "type": "string", "format": "date-time" },
          "firstSeenAt": { "type": "string", "format": "date-time" },
          "lastSeenAt": { "type": "string", "format": "date-time" },
          "xPostUrl": { "type": "string", "format": "uri" },
          "links": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": { "type": "string" },
                "title": { "type": "string" },
                "url": { "type": "string", "format": "uri" },
                "publisher": { "type": "string" },
                "author": { "type": "string" },
                "linkType": { "type": "string", "enum": ["lead", "sub", "more", "related"] },
                "position": { "type": "integer" }
              }
            }
          },
          "commentaryLinks": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": { "type": "string" },
                "platform": { "type": "string" },
                "label": { "type": "string" },
                "url": { "type": "string", "format": "uri" },
                "position": { "type": "integer" }
              }
            }
          },
          "techsnifArticle": {
            "type": "object",
            "properties": {
              "slug": { "type": "string" },
              "title": { "type": "string" },
              "publishedAt": { "type": "string", "format": "date-time" }
            }
          }
        }
      },
      "Article": {
        "type": "object",
        "properties": {
          "slug": { "type": "string" },
          "title": { "type": "string" },
          "excerpt": { "type": "string" },
          "content": { "type": "string", "description": "HTML body (detail endpoint only)" },
          "category": { "type": "string" },
          "tags": { "type": "array", "items": { "type": "string" } },
          "image": { "type": "string", "format": "uri" },
          "source": { "type": "string" },
          "publishedAt": { "type": "string", "format": "date-time" }
        }
      },
      "LeaderboardEntry": {
        "type": "object",
        "properties": {
          "id": { "type": "string" },
          "slug": { "type": "string" },
          "name": { "type": "string" },
          "count": { "type": "integer" },
          "leadCount": { "type": "integer" },
          "rank": { "type": "integer" }
        }
      },
      "TechEvent": {
        "type": "object",
        "properties": {
          "id": { "type": "string" },
          "name": { "type": "string" },
          "location": { "type": "string" },
          "startDate": { "type": "string" },
          "endDate": { "type": "string" },
          "url": { "type": "string", "format": "uri" },
          "featured": { "type": "boolean" },
          "hybrid": { "type": "boolean" },
          "virtual": { "type": "boolean" }
        }
      }
    }
  }
}
