{
  "openapi": "3.1.0",
  "info": {
    "title": "Cosmotech Public API",
    "version": "1.0.0",
    "summary": "Read-only JSON access to Cosmotech content.",
    "description": "Public, unauthenticated, read-only API for Cosmotech (Oslo, Norway). Exposes company information, service lines with published starting prices, articles, research reports, the curated Nordic digital stack directory, and the full site content as Markdown. No personal data is exposed and no write operations exist. Every response, including every error, is JSON. Rate limit: 120 requests per minute per IP, reported in the X-RateLimit-* headers.",
    "termsOfService": "https://cosmotech.no/personvern",
    "contact": {
      "name": "Cosmotech developer support",
      "email": "hei@cosmotech.no",
      "url": "https://cosmotech.no/developers"
    },
    "license": { "name": "Attribution required", "url": "https://cosmotech.no/developers" }
  },
  "servers": [
    {
      "url": "https://gfpstfdicsuuhwcttuqc.supabase.co/functions/v1/public-api",
      "description": "Production"
    }
  ],
  "externalDocs": {
    "description": "Developer and agent portal",
    "url": "https://cosmotech.no/developers"
  },
  "tags": [
    { "name": "Meta", "description": "Service discovery." },
    { "name": "Company", "description": "Company and contact information." },
    { "name": "Services", "description": "Cosmotech service lines and published pricing." },
    { "name": "Content", "description": "Articles, research reports and site content." },
    { "name": "Directory", "description": "Curated Nordic B2B digital stack directory." }
  ],
  "paths": {
    "/": {
      "get": {
        "operationId": "getApiIndex",
        "tags": ["Meta"],
        "summary": "List API endpoints",
        "description": "Returns the API name, version, documentation links, rate limit and the list of available endpoint paths.",
        "responses": {
          "200": {
            "description": "API index.",
            "content": {
              "application/json": { "schema": { "$ref": "#/components/schemas/ApiIndex" } }
            }
          },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/company": {
      "get": {
        "operationId": "getCompany",
        "tags": ["Company"],
        "summary": "Get Cosmotech company information",
        "description": "Returns the legal name, founding year, description, postal address in Oslo, e-mail, phone, supported languages and canonical contact channels.",
        "responses": {
          "200": {
            "description": "Company profile.",
            "content": {
              "application/json": { "schema": { "$ref": "#/components/schemas/Company" } }
            }
          },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/services": {
      "get": {
        "operationId": "listServices",
        "tags": ["Services"],
        "summary": "List Cosmotech service lines",
        "description": "Returns every service line with a stable id, a summary, the canonical page URL and the published starting price where one is published.",
        "responses": {
          "200": {
            "description": "Service collection.",
            "content": {
              "application/json": { "schema": { "$ref": "#/components/schemas/ServiceList" } }
            }
          },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/services/{serviceId}": {
      "get": {
        "operationId": "getService",
        "tags": ["Services"],
        "summary": "Get one service line",
        "description": "Returns a single service line by its stable id. Ids are listed by listServices.",
        "parameters": [
          {
            "name": "serviceId",
            "in": "path",
            "required": true,
            "description": "Stable service id, for example web-design.",
            "schema": { "type": "string", "pattern": "^[a-z0-9-]+$", "examples": ["web-design"] }
          }
        ],
        "responses": {
          "200": {
            "description": "Service.",
            "content": {
              "application/json": { "schema": { "$ref": "#/components/schemas/Service" } }
            }
          },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/blog": {
      "get": {
        "operationId": "listBlogPosts",
        "tags": ["Content"],
        "summary": "List published articles",
        "description": "Returns published Cosmotech articles with slug, title, summary, language and canonical URL.",
        "responses": {
          "200": {
            "description": "Article collection.",
            "content": {
              "application/json": { "schema": { "$ref": "#/components/schemas/ArticleList" } }
            }
          },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/reports": {
      "get": {
        "operationId": "listReports",
        "tags": ["Content"],
        "summary": "List research reports",
        "description": "Returns Cosmotech research reports with slug, title, summary, language and canonical URL.",
        "responses": {
          "200": {
            "description": "Report collection.",
            "content": {
              "application/json": { "schema": { "$ref": "#/components/schemas/ArticleList" } }
            }
          },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/directory/categories": {
      "get": {
        "operationId": "listDirectoryCategories",
        "tags": ["Directory"],
        "summary": "List directory categories",
        "description": "Returns the categories used by the Nordic digital stack directory. Values are valid inputs for the category filter of listDirectoryTools.",
        "responses": {
          "200": {
            "description": "Category collection.",
            "content": {
              "application/json": { "schema": { "$ref": "#/components/schemas/CategoryList" } }
            }
          },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/directory/tools": {
      "get": {
        "operationId": "listDirectoryTools",
        "tags": ["Directory"],
        "summary": "List directory tools",
        "description": "Returns curated B2B tools from the Nordic digital stack directory, optionally filtered by category.",
        "parameters": [
          {
            "name": "category",
            "in": "query",
            "required": false,
            "description": "Restrict the result to one directory category.",
            "schema": { "$ref": "#/components/schemas/DirectoryCategory" }
          }
        ],
        "responses": {
          "200": {
            "description": "Tool collection.",
            "content": {
              "application/json": { "schema": { "$ref": "#/components/schemas/ToolList" } }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/content.md": {
      "get": {
        "operationId": "getSiteContentMarkdown",
        "tags": ["Content"],
        "summary": "Get the full site content as Markdown",
        "description": "Returns the whole public Cosmotech site (services, pricing, articles, reports) as a single Markdown document for language-model context.",
        "responses": {
          "200": {
            "description": "Site content as Markdown.",
            "content": { "text/markdown": { "schema": { "type": "string" } } }
          },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    }
  },
  "components": {
    "responses": {
      "BadRequest": {
        "description": "A parameter was missing or invalid.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "NotFound": {
        "description": "The requested resource does not exist.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "RateLimited": {
        "description": "Rate limit exceeded. Retry after the window resets.",
        "headers": {
          "Retry-After": {
            "description": "Seconds until the rate-limit window resets.",
            "schema": { "type": "integer" }
          }
        },
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "description": "Structured error payload returned for every non-success response.",
        "required": ["error"],
        "properties": {
          "error": {
            "type": "object",
            "required": ["code", "message", "status"],
            "properties": {
              "code": {
                "type": "string",
                "description": "Stable machine-readable error code.",
                "enum": [
                  "invalid_parameter",
                  "not_found",
                  "service_not_found",
                  "method_not_allowed",
                  "rate_limited",
                  "internal_error"
                ]
              },
              "message": { "type": "string", "description": "Human-readable explanation." },
              "hint": { "type": "string", "description": "How to resolve the error." },
              "status": { "type": "integer", "description": "HTTP status code." },
              "documentation": { "type": "string", "format": "uri" },
              "specification": { "type": "string", "format": "uri" }
            }
          }
        }
      },
      "ApiIndex": {
        "type": "object",
        "required": ["name", "version", "endpoints"],
        "properties": {
          "name": { "type": "string" },
          "version": { "type": "string" },
          "description": { "type": "string" },
          "documentation": { "type": "string", "format": "uri" },
          "specification": { "type": "string", "format": "uri" },
          "rateLimit": {
            "type": "object",
            "properties": {
              "requests": { "type": "integer" },
              "windowSeconds": { "type": "integer" }
            }
          },
          "endpoints": { "type": "array", "items": { "type": "string" } }
        }
      },
      "Company": {
        "type": "object",
        "required": ["name", "legalName", "url", "email", "address"],
        "properties": {
          "name": { "type": "string" },
          "legalName": { "type": "string" },
          "founded": { "type": "integer" },
          "description": { "type": "string" },
          "url": { "type": "string", "format": "uri" },
          "email": { "type": "string", "format": "email" },
          "phone": { "type": "string" },
          "address": {
            "type": "object",
            "required": ["street", "postalCode", "city", "country"],
            "properties": {
              "street": { "type": "string" },
              "postalCode": { "type": "string" },
              "city": { "type": "string" },
              "country": { "type": "string", "description": "ISO 3166-1 alpha-2 country code." }
            }
          },
          "languages": {
            "type": "array",
            "items": { "type": "string", "description": "BCP 47 language tag." }
          },
          "channels": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "label": { "type": "string" },
                "url": { "type": "string", "format": "uri" }
              }
            }
          }
        }
      },
      "Price": {
        "type": "object",
        "description": "Published starting price. Null when pricing is scoped per engagement.",
        "required": ["amount", "currency", "unit"],
        "properties": {
          "amount": { "type": "number" },
          "currency": { "type": "string", "enum": ["NOK"] },
          "unit": {
            "type": "string",
            "description": "What the amount applies to, for example month or project."
          }
        }
      },
      "Service": {
        "type": "object",
        "required": ["id", "name", "summary", "url"],
        "properties": {
          "id": { "type": "string" },
          "name": { "type": "string" },
          "summary": { "type": "string" },
          "url": { "type": "string", "format": "uri" },
          "language": { "type": "string", "enum": ["nb", "en"] },
          "priceFrom": {
            "oneOf": [{ "$ref": "#/components/schemas/Price" }, { "type": "null" }]
          }
        }
      },
      "Article": {
        "type": "object",
        "required": ["slug", "title", "summary", "url"],
        "properties": {
          "slug": { "type": "string" },
          "title": { "type": "string" },
          "summary": { "type": "string" },
          "url": { "type": "string", "format": "uri" },
          "language": { "type": "string", "enum": ["nb", "en"] }
        }
      },
      "DirectoryCategory": {
        "type": "string",
        "enum": [
          "CRM & Sales",
          "Accounting & Finance",
          "Legal & Compliance",
          "Project Management",
          "Cloud & Infrastructure",
          "Cybersecurity",
          "HR & People",
          "Business Intelligence",
          "Marketing Automation",
          "Communication",
          "E-Signature",
          "AI & Automation"
        ]
      },
      "DirectoryTool": {
        "type": "object",
        "required": ["id", "name", "description", "category", "website"],
        "properties": {
          "id": { "type": "string" },
          "name": { "type": "string" },
          "description": { "type": "string" },
          "category": { "$ref": "#/components/schemas/DirectoryCategory" },
          "website": { "type": "string", "format": "uri" }
        }
      },
      "ServiceList": {
        "type": "object",
        "required": ["count", "items"],
        "properties": {
          "count": { "type": "integer" },
          "items": { "type": "array", "items": { "$ref": "#/components/schemas/Service" } }
        }
      },
      "ArticleList": {
        "type": "object",
        "required": ["count", "items"],
        "properties": {
          "count": { "type": "integer" },
          "items": { "type": "array", "items": { "$ref": "#/components/schemas/Article" } }
        }
      },
      "CategoryList": {
        "type": "object",
        "required": ["count", "items"],
        "properties": {
          "count": { "type": "integer" },
          "items": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/DirectoryCategory" }
          }
        }
      },
      "ToolList": {
        "type": "object",
        "required": ["count", "items"],
        "properties": {
          "count": { "type": "integer" },
          "category": {
            "oneOf": [
              { "$ref": "#/components/schemas/DirectoryCategory" },
              { "type": "null" }
            ]
          },
          "items": { "type": "array", "items": { "$ref": "#/components/schemas/DirectoryTool" } }
        }
      }
    }
  }
}
