{
  "openapi": "3.1.0",
  "info": {
    "title": "Civicamente Civic Data & Research Analytics API (DaaS)",
    "version": "1.0.0",
    "description": "Programmatic REST API providing university researchers (ETH Zürich, USI, HSG St. Gallen, etc.), sociological observatories, and public administrations with aggregated, anonymized, and georeferenced datasets. Features cognitive bias trends, intertemporal foresight indices, and cantonal polarization metrics under Swiss Federal Data Protection Act (FADP / LPD RS 235.1) and EU GDPR privacy-by-design standards (k-anonymity >= 5).",
    "termsOfService": "https://civicamente.ch/informazioni/privacy",
    "contact": {
      "name": "Civicamente Data Intelligence Group",
      "url": "https://civicamente.ch/informazioni/partnership",
      "email": "daas-support@civicamente.ch"
    },
    "license": {
      "name": "Creative Commons Attribution-NonCommercial 4.0 International (CC BY-NC 4.0) for Research Use",
      "url": "https://creativecommons.org/licenses/by-nc/4.0/"
    }
  },
  "externalDocs": {
    "description": "Full Academic & Methodological Documentation",
    "url": "https://civicamente.ch/informazioni/metodologia"
  },
  "servers": [
    {
      "url": "https://civicamente.ch/api/v1/datasets",
      "description": "Production Federal Node (Switzerland)"
    },
    {
      "url": "http://localhost:3000/api/v1/datasets",
      "description": "Local Sandbox Development Server"
    }
  ],
  "security": [
    {
      "ApiKeyAuth": []
    },
    {
      "BearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Datasets",
      "description": "Aggregated and anonymized research datasets on cognitive bias, foresight, and territorial polarization."
    }
  ],
  "paths": {
    "/overview": {
      "get": {
        "tags": ["Datasets"],
        "summary": "Macro Overview Dataset",
        "description": "Returns national synthesis metrics, participating cantons count, and aggregated territorial breakdown for macro-level civic benchmarking.",
        "operationId": "getOverviewDataset",
        "parameters": [
          {
            "name": "country_iso",
            "in": "query",
            "required": false,
            "description": "ISO 3166-1 alpha-2 country code (default: 'CH').",
            "schema": {
              "type": "string",
              "pattern": "^[A-Z]{2}$",
              "default": "CH",
              "example": "CH"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful operation returning macro indicators and cantonal overview.",
            "headers": {
              "X-RateLimit-Limit": { "$ref": "#/components/headers/X-RateLimit-Limit" },
              "X-RateLimit-Remaining": { "$ref": "#/components/headers/X-RateLimit-Remaining" },
              "X-RateLimit-Reset": { "$ref": "#/components/headers/X-RateLimit-Reset" },
              "X-Civicamente-DaaS": { "$ref": "#/components/headers/X-Civicamente-DaaS" },
              "Cache-Control": { "$ref": "#/components/headers/Cache-Control" }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OverviewResponse"
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/InternalServerError" }
        }
      }
    },
    "/cognitive-biases": {
      "get": {
        "tags": ["Datasets"],
        "summary": "Cognitive Biases Trends Dataset",
        "description": "Returns aggregated measurements of Confirmation Bias, Framing Bias, and Concision Bias grouped by canton and country. Strictly enforces k-anonymity (k >= 5) by suppressing granular data when sample size is below threshold.",
        "operationId": "getCognitiveBiasesDataset",
        "parameters": [
          {
            "name": "country_iso",
            "in": "query",
            "required": false,
            "description": "ISO 3166-1 alpha-2 country code.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Z]{2}$",
              "example": "CH"
            }
          },
          {
            "name": "canton_iso",
            "in": "query",
            "required": false,
            "description": "Swiss Canton code (ISO 3166-2:CH). Valid values include TI, ZH, BE, GE, AG, VD, etc.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Z]{2}$",
              "example": "TI"
            }
          },
          {
            "name": "from_date",
            "in": "query",
            "required": false,
            "description": "Start date for chronological filter in ISO 8601 format.",
            "schema": {
              "type": "string",
              "format": "date-time",
              "example": "2026-01-01T00:00:00Z"
            }
          },
          {
            "name": "to_date",
            "in": "query",
            "required": false,
            "description": "End date for chronological filter in ISO 8601 format.",
            "schema": {
              "type": "string",
              "format": "date-time",
              "example": "2026-09-01T23:59:59Z"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful operation returning cognitive bias distributions.",
            "headers": {
              "X-RateLimit-Limit": { "$ref": "#/components/headers/X-RateLimit-Limit" },
              "X-RateLimit-Remaining": { "$ref": "#/components/headers/X-RateLimit-Remaining" },
              "X-RateLimit-Reset": { "$ref": "#/components/headers/X-RateLimit-Reset" },
              "X-Civicamente-DaaS": { "$ref": "#/components/headers/X-Civicamente-DaaS" },
              "Cache-Control": { "$ref": "#/components/headers/Cache-Control" }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CognitiveBiasesResponse"
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/InternalServerError" }
        }
      }
    },
    "/foresight": {
      "get": {
        "tags": ["Datasets"],
        "summary": "Foresight & Intertemporal Discounting Dataset",
        "description": "Returns empirical metrics on long-term intertemporal choice, delay discounting ratios (immediate gratification vs deferred collective reward), and Civic Effort indices across territories.",
        "operationId": "getForesightDataset",
        "parameters": [
          {
            "name": "country_iso",
            "in": "query",
            "required": false,
            "description": "ISO 3166-1 alpha-2 country code.",
            "schema": { "type": "string", "pattern": "^[A-Z]{2}$", "example": "CH" }
          },
          {
            "name": "canton_iso",
            "in": "query",
            "required": false,
            "description": "Swiss Canton code (ISO 3166-2:CH).",
            "schema": { "type": "string", "pattern": "^[A-Z]{2}$", "example": "ZH" }
          },
          {
            "name": "from_date",
            "in": "query",
            "required": false,
            "schema": { "type": "string", "format": "date-time" }
          },
          {
            "name": "to_date",
            "in": "query",
            "required": false,
            "schema": { "type": "string", "format": "date-time" }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful operation returning foresight metrics.",
            "headers": {
              "X-RateLimit-Limit": { "$ref": "#/components/headers/X-RateLimit-Limit" },
              "X-RateLimit-Remaining": { "$ref": "#/components/headers/X-RateLimit-Remaining" },
              "X-RateLimit-Reset": { "$ref": "#/components/headers/X-RateLimit-Reset" },
              "X-Civicamente-DaaS": { "$ref": "#/components/headers/X-Civicamente-DaaS" },
              "Cache-Control": { "$ref": "#/components/headers/Cache-Control" }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ForesightResponse"
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/InternalServerError" }
        }
      }
    },
    "/territorial-polarization": {
      "get": {
        "tags": ["Datasets"],
        "summary": "Territorial Polarization & Consensus Dataset",
        "description": "Quantifies ideological dispersion, normalized competence variance, and the territorial consensus index across cantons and regions.",
        "operationId": "getPolarizationDataset",
        "parameters": [
          {
            "name": "country_iso",
            "in": "query",
            "required": false,
            "schema": { "type": "string", "pattern": "^[A-Z]{2}$", "example": "CH" }
          },
          {
            "name": "canton_iso",
            "in": "query",
            "required": false,
            "schema": { "type": "string", "pattern": "^[A-Z]{2}$", "example": "GE" }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful operation returning polarization and consensus metrics.",
            "headers": {
              "X-RateLimit-Limit": { "$ref": "#/components/headers/X-RateLimit-Limit" },
              "X-RateLimit-Remaining": { "$ref": "#/components/headers/X-RateLimit-Remaining" },
              "X-RateLimit-Reset": { "$ref": "#/components/headers/X-RateLimit-Reset" },
              "X-Civicamente-DaaS": { "$ref": "#/components/headers/X-Civicamente-DaaS" },
              "Cache-Control": { "$ref": "#/components/headers/Cache-Control" }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PolarizationResponse"
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/InternalServerError" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-Key",
        "description": "Zero-Knowledge API Key (Format: 'cv_live_...' for production or 'cv_test_...' for sandbox)."
      },
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "API Key",
        "description": "Standard Bearer authorization header with API Key."
      }
    },
    "headers": {
      "X-RateLimit-Limit": {
        "description": "Maximum allowed requests per minute allocated to the key's tier.",
        "schema": { "type": "integer", "example": 60 }
      },
      "X-RateLimit-Remaining": {
        "description": "Remaining request capacity within the current 60-second sliding window.",
        "schema": { "type": "integer", "example": 59 }
      },
      "X-RateLimit-Reset": {
        "description": "Seconds until the sliding window quota fully resets.",
        "schema": { "type": "integer", "example": 60 }
      },
      "X-Civicamente-DaaS": {
        "description": "Civicamente DaaS API version identifier.",
        "schema": { "type": "string", "example": "v1" }
      },
      "Cache-Control": {
        "description": "HTTP caching directive for CDN edge caching and stale-while-revalidate.",
        "schema": { "type": "string", "example": "public, s-maxage=60, stale-while-revalidate=300" }
      }
    },
    "schemas": {
      "DatasetMeta": {
        "type": "object",
        "required": ["generated_at", "k_anonymity_threshold", "tier", "client", "total_records_analyzed"],
        "properties": {
          "generated_at": { "type": "string", "format": "date-time", "example": "2026-09-06T14:30:00.000Z" },
          "k_anonymity_threshold": { "type": "integer", "default": 5, "example": 5 },
          "tier": { "type": "string", "enum": ["sandbox", "tier1", "enterprise"], "example": "tier1" },
          "client": { "type": "string", "example": "ETH Zürich - Civic Lab" },
          "total_records_analyzed": { "type": "integer", "example": 1420 },
          "filtered_by": { "type": "object", "additionalProperties": true }
        }
      },
      "ProblemDetails": {
        "type": "object",
        "description": "RFC 7807 Problem Details for HTTP APIs",
        "required": ["type", "title", "status", "detail"],
        "properties": {
          "type": { "type": "string", "format": "uri", "example": "https://civicamente.ch/errors/401" },
          "title": { "type": "string", "example": "Errore di Autenticazione API" },
          "status": { "type": "integer", "example": 401 },
          "detail": { "type": "string", "example": "Autenticazione richiesta. Fornisci l'header 'X-API-Key: cv_live_...'." },
          "instance": { "type": "string", "format": "uri" }
        }
      },
      "OverviewResponse": {
        "type": "object",
        "required": ["status", "meta", "summary", "cantons"],
        "properties": {
          "status": { "type": "string", "example": "success" },
          "meta": { "$ref": "#/components/schemas/DatasetMeta" },
          "summary": {
            "type": "object",
            "properties": {
              "total_evaluations": { "type": "integer", "example": 1420 },
              "cantons_active": { "type": "integer", "example": 18 },
              "national_foresight_mean": { "type": ["number", "null"], "example": 68.4 },
              "national_voting_capacity_mean": { "type": ["number", "null"], "example": 74.2 },
              "national_consensus_index": { "type": ["number", "null"], "example": 78.4 }
            }
          },
          "cantons": {
            "type": "array",
            "items": {
              "type": "object",
              "required": ["country_iso", "region_name", "sample_size", "insufficient_sample"],
              "properties": {
                "country_iso": { "type": "string", "example": "CH" },
                "region_name": { "type": "string", "example": "Canton TI" },
                "canton_iso": { "type": ["string", "null"], "example": "TI" },
                "sample_size": { "type": "integer", "example": 182 },
                "insufficient_sample": { "type": "boolean", "example": false },
                "civic_wisdom_score": { "type": ["number", "null"], "example": 76.5 },
                "foresight_mean": { "type": ["number", "null"], "example": 71.2 },
                "bias_overall_mean": { "type": ["number", "null"], "example": 32.1 },
                "consensus_level": { "type": "string", "enum": ["high", "moderate", "polarized", "unknown"], "example": "high" }
              }
            }
          }
        }
      },
      "CognitiveBiasesRecord": {
        "type": "object",
        "required": ["country_iso", "region_name", "sample_size", "insufficient_sample"],
        "properties": {
          "country_iso": { "type": "string", "example": "CH" },
          "region_name": { "type": "string", "example": "Canton TI" },
          "canton_iso": { "type": ["string", "null"], "example": "TI" },
          "sample_size": { "type": "integer", "example": 182 },
          "insufficient_sample": { "type": "boolean", "example": false },
          "confirmation_bias_mean": { "type": ["number", "null"], "minimum": 0, "maximum": 100, "example": 34.2 },
          "framing_bias_mean": { "type": ["number", "null"], "minimum": 0, "maximum": 100, "example": 41.5 },
          "concision_bias_mean": { "type": ["number", "null"], "minimum": 0, "maximum": 100, "example": 28.0 },
          "coverage_bias_mean": { "type": ["number", "null"], "minimum": 0, "maximum": 100, "example": 41.5 },
          "dominant_bias_breakdown": {
            "type": ["object", "null"],
            "additionalProperties": { "type": "number" },
            "example": { "confirmation": 45, "framing": 35, "concision": 20 }
          }
        }
      },
      "CognitiveBiasesResponse": {
        "type": "object",
        "required": ["status", "meta", "data"],
        "properties": {
          "status": { "type": "string", "example": "success" },
          "meta": { "$ref": "#/components/schemas/DatasetMeta" },
          "data": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/CognitiveBiasesRecord" }
          }
        }
      },
      "ForesightRecord": {
        "type": "object",
        "required": ["country_iso", "region_name", "sample_size", "insufficient_sample"],
        "properties": {
          "country_iso": { "type": "string", "example": "CH" },
          "region_name": { "type": "string", "example": "Canton ZH" },
          "canton_iso": { "type": ["string", "null"], "example": "ZH" },
          "sample_size": { "type": "integer", "example": 240 },
          "insufficient_sample": { "type": "boolean", "example": false },
          "foresight_index_mean": { "type": ["number", "null"], "minimum": 0, "maximum": 100, "example": 72.8 },
          "civic_effort_index_mean": { "type": ["number", "null"], "minimum": 0, "maximum": 100, "example": 68.4 },
          "intertemporal_preference_ratio": { "type": ["number", "null"], "example": 1.06 }
        }
      },
      "ForesightResponse": {
        "type": "object",
        "required": ["status", "meta", "data"],
        "properties": {
          "status": { "type": "string", "example": "success" },
          "meta": { "$ref": "#/components/schemas/DatasetMeta" },
          "data": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/ForesightRecord" }
          }
        }
      },
      "PolarizationRecord": {
        "type": "object",
        "required": ["country_iso", "region_name", "sample_size", "insufficient_sample"],
        "properties": {
          "country_iso": { "type": "string", "example": "CH" },
          "region_name": { "type": "string", "example": "Canton GE" },
          "canton_iso": { "type": ["string", "null"], "example": "GE" },
          "sample_size": { "type": "integer", "example": 115 },
          "insufficient_sample": { "type": "boolean", "example": false },
          "voting_capacity_mean": { "type": ["number", "null"], "example": 71.4 },
          "competence_score_mean": { "type": ["number", "null"], "example": 69.2 },
          "competence_score_variance": { "type": ["number", "null"], "example": 42.1 },
          "polarization_index": { "type": ["number", "null"], "minimum": 0, "maximum": 100, "example": 25.9 },
          "consensus_index": { "type": ["number", "null"], "minimum": 0, "maximum": 100, "example": 74.1 }
        }
      },
      "PolarizationResponse": {
        "type": "object",
        "required": ["status", "meta", "data"],
        "properties": {
          "status": { "type": "string", "example": "success" },
          "meta": { "$ref": "#/components/schemas/DatasetMeta" },
          "data": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/PolarizationRecord" }
          }
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Missing or invalid API Key in request headers.",
        "content": {
          "application/problem+json": {
            "schema": { "$ref": "#/components/schemas/ProblemDetails" },
            "example": {
              "type": "https://civicamente.ch/errors/401",
              "title": "Errore di Autenticazione API",
              "status": 401,
              "detail": "Autenticazione richiesta. Fornisci l'header 'X-API-Key: cv_live_...' oppure 'Authorization: Bearer cv_live_...'."
            }
          }
        }
      },
      "Forbidden": {
        "description": "API Key is revoked, inactive or expired.",
        "content": {
          "application/problem+json": {
            "schema": { "$ref": "#/components/schemas/ProblemDetails" },
            "example": {
              "type": "https://civicamente.ch/errors/403",
              "title": "Errore di Autenticazione API",
              "status": 403,
              "detail": "La chiave API specificata è stata revocata o disattivata dall'amministratore."
            }
          }
        }
      },
      "RateLimited": {
        "description": "Request rate limit or daily quota exceeded.",
        "headers": {
          "Retry-After": {
            "description": "Number of seconds to wait before retrying.",
            "schema": { "type": "integer", "example": 60 }
          }
        },
        "content": {
          "application/problem+json": {
            "schema": { "$ref": "#/components/schemas/ProblemDetails" },
            "example": {
              "type": "https://civicamente.ch/errors/429",
              "title": "Errore di Autenticazione API",
              "status": 429,
              "detail": "Limite di richieste al minuto superato. Riprova tra qualche istante."
            }
          }
        }
      },
      "InternalServerError": {
        "description": "Unexpected error during aggregation computation.",
        "content": {
          "application/problem+json": {
            "schema": { "$ref": "#/components/schemas/ProblemDetails" },
            "example": {
              "type": "https://civicamente.ch/errors/500",
              "title": "Internal Server Error",
              "status": 500,
              "detail": "Si è verificato un errore imprevisto durante l'elaborazione della richiesta del dataset."
            }
          }
        }
      }
    }
  }
}
