{
  "openapi": "3.0.3",
  "info": {
    "title": "HICS Open Data API",
    "version": "v1",
    "description": "Public REST API for the Himalayan Institute for Contextual Sciences (HICS). Provides access to environmental sensor readings, lab notes, research programmes, publications, and team profiles.\n\nAll endpoints are read-only and require no authentication. Data ingest (POST /api/v1/ingest/environmental/) requires a station API key. Licensed under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/).",
    "contact": {
      "name": "HICS",
      "email": "info@himalayansciences.org",
      "url": "https://himalayansciences.org"
    },
    "license": {
      "name": "CC BY 4.0",
      "url": "https://creativecommons.org/licenses/by/4.0/"
    }
  },
  "servers": [
    {
      "url": "https://www.himalayansciences.org/api/v1",
      "description": "Production"
    }
  ],
  "paths": {
    "/": {
      "get": {
        "summary": "API discovery",
        "description": "Lists all available endpoints with descriptions.",
        "operationId": "apiIndex",
        "tags": [
          "Meta"
        ],
        "responses": {
          "200": {
            "description": "Endpoint index",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "api": {
                      "type": "string"
                    },
                    "version": {
                      "type": "string"
                    },
                    "base_url": {
                      "type": "string"
                    },
                    "cors": {
                      "type": "string"
                    },
                    "license": {
                      "type": "string"
                    },
                    "endpoints": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/stations/": {
      "get": {
        "summary": "Station list",
        "description": "Lists all HICS monitoring stations with their latest reading summary. Add `?output=geojson` for a GeoJSON FeatureCollection.",
        "operationId": "listStations",
        "tags": [
          "Stations"
        ],
        "parameters": [
          {
            "name": "output",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "json",
                "geojson"
              ]
            },
            "description": "Response format. Default: json"
          }
        ],
        "responses": {
          "200": {
            "description": "Station list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "count": {
                      "type": "integer"
                    },
                    "stations": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "station_id": {
                            "type": "string",
                            "example": "KTM-001"
                          },
                          "name": {
                            "type": "string"
                          },
                          "location_name": {
                            "type": "string"
                          },
                          "altitude_m": {
                            "type": "number"
                          },
                          "latitude": {
                            "type": "number",
                            "nullable": true
                          },
                          "longitude": {
                            "type": "number",
                            "nullable": true
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "active",
                              "offline",
                              "planned"
                            ]
                          },
                          "latest_reading": {
                            "type": "object",
                            "properties": {
                              "timestamp": {
                                "type": "string",
                                "format": "date-time"
                              },
                              "temperature_c": {
                                "type": "number",
                                "description": "Air temperature in \u00b0C (DHT22)"
                              },
                              "humidity_rh": {
                                "type": "number",
                                "description": "Relative humidity % (DHT22)"
                              },
                              "pressure_hpa": {
                                "type": "number",
                                "description": "Barometric pressure in hPa (BMP280)"
                              },
                              "altitude_m": {
                                "type": "number",
                                "description": "Station altitude above sea level (m)"
                              },
                              "pm1_0": {
                                "type": "number",
                                "nullable": true,
                                "description": "PM1.0 ultra-fine particulate matter \u00b5g/m\u00b3"
                              },
                              "pm2_5": {
                                "type": "number",
                                "nullable": true,
                                "description": "PM2.5 fine particulate matter \u00b5g/m\u00b3"
                              },
                              "pm10": {
                                "type": "number",
                                "nullable": true,
                                "description": "PM10 coarse particulate matter \u00b5g/m\u00b3"
                              },
                              "co2_ppm": {
                                "type": "number",
                                "nullable": true,
                                "description": "Carbon dioxide concentration (ppm)"
                              },
                              "co_ppm": {
                                "type": "number",
                                "nullable": true,
                                "description": "Carbon monoxide concentration (ppm)"
                              },
                              "uv_index": {
                                "type": "number",
                                "nullable": true,
                                "description": "UV index (dimensionless, WHO scale 0\u201311+)"
                              },
                              "quality_flag": {
                                "type": "string",
                                "enum": [
                                  "ok",
                                  "suspect",
                                  "invalid"
                                ],
                                "description": "Auto-assigned on ingest; manual override possible"
                              },
                              "firmware_version": {
                                "type": "string",
                                "nullable": true
                              }
                            },
                            "nullable": true
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/data/latest/": {
      "get": {
        "summary": "Latest reading",
        "description": "Most recent sensor reading for a specified station.",
        "operationId": "getLatestReading",
        "tags": [
          "Data"
        ],
        "parameters": [
          {
            "name": "station",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "KTM-001"
          }
        ],
        "responses": {
          "200": {
            "description": "Latest reading",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "timestamp": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "temperature_c": {
                      "type": "number",
                      "description": "Air temperature in \u00b0C (DHT22)"
                    },
                    "humidity_rh": {
                      "type": "number",
                      "description": "Relative humidity % (DHT22)"
                    },
                    "pressure_hpa": {
                      "type": "number",
                      "description": "Barometric pressure in hPa (BMP280)"
                    },
                    "altitude_m": {
                      "type": "number",
                      "description": "Station altitude above sea level (m)"
                    },
                    "pm1_0": {
                      "type": "number",
                      "nullable": true,
                      "description": "PM1.0 ultra-fine particulate matter \u00b5g/m\u00b3"
                    },
                    "pm2_5": {
                      "type": "number",
                      "nullable": true,
                      "description": "PM2.5 fine particulate matter \u00b5g/m\u00b3"
                    },
                    "pm10": {
                      "type": "number",
                      "nullable": true,
                      "description": "PM10 coarse particulate matter \u00b5g/m\u00b3"
                    },
                    "co2_ppm": {
                      "type": "number",
                      "nullable": true,
                      "description": "Carbon dioxide concentration (ppm)"
                    },
                    "co_ppm": {
                      "type": "number",
                      "nullable": true,
                      "description": "Carbon monoxide concentration (ppm)"
                    },
                    "uv_index": {
                      "type": "number",
                      "nullable": true,
                      "description": "UV index (dimensionless, WHO scale 0\u201311+)"
                    },
                    "quality_flag": {
                      "type": "string",
                      "enum": [
                        "ok",
                        "suspect",
                        "invalid"
                      ],
                      "description": "Auto-assigned on ingest; manual override possible"
                    },
                    "firmware_version": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "station parameter missing"
          },
          "404": {
            "description": "Station not found or no data"
          }
        }
      }
    },
    "/data/historical/": {
      "get": {
        "summary": "Historical time-series",
        "description": "Time-series readings for a station over the last N hours (max 720).",
        "operationId": "getHistoricalData",
        "tags": [
          "Data"
        ],
        "parameters": [
          {
            "name": "station",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "hours",
            "in": "query",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 720
            }
          },
          {
            "name": "quality",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "ok",
                "suspect",
                "invalid"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Array of readings ordered by time",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "station": {
                      "type": "string"
                    },
                    "hours": {
                      "type": "integer"
                    },
                    "count": {
                      "type": "integer"
                    },
                    "readings": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "timestamp": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "temperature_c": {
                            "type": "number",
                            "description": "Air temperature in \u00b0C (DHT22)"
                          },
                          "humidity_rh": {
                            "type": "number",
                            "description": "Relative humidity % (DHT22)"
                          },
                          "pressure_hpa": {
                            "type": "number",
                            "description": "Barometric pressure in hPa (BMP280)"
                          },
                          "altitude_m": {
                            "type": "number",
                            "description": "Station altitude above sea level (m)"
                          },
                          "pm1_0": {
                            "type": "number",
                            "nullable": true,
                            "description": "PM1.0 ultra-fine particulate matter \u00b5g/m\u00b3"
                          },
                          "pm2_5": {
                            "type": "number",
                            "nullable": true,
                            "description": "PM2.5 fine particulate matter \u00b5g/m\u00b3"
                          },
                          "pm10": {
                            "type": "number",
                            "nullable": true,
                            "description": "PM10 coarse particulate matter \u00b5g/m\u00b3"
                          },
                          "co2_ppm": {
                            "type": "number",
                            "nullable": true,
                            "description": "Carbon dioxide concentration (ppm)"
                          },
                          "co_ppm": {
                            "type": "number",
                            "nullable": true,
                            "description": "Carbon monoxide concentration (ppm)"
                          },
                          "uv_index": {
                            "type": "number",
                            "nullable": true,
                            "description": "UV index (dimensionless, WHO scale 0\u201311+)"
                          },
                          "quality_flag": {
                            "type": "string",
                            "enum": [
                              "ok",
                              "suspect",
                              "invalid"
                            ],
                            "description": "Auto-assigned on ingest; manual override possible"
                          },
                          "firmware_version": {
                            "type": "string",
                            "nullable": true
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/data/download/": {
      "get": {
        "summary": "CSV download (single or multi-station)",
        "description": "Download all readings as a CSV file with CC BY 4.0 attribution headers. Single station: `?station=KTM-001`. Multi-station: `?stations=KTM-001,DLP-001` \u2014 combined CSV with station_id as first column, sorted by timestamp.",
        "operationId": "downloadCSV",
        "tags": [
          "Data"
        ],
        "parameters": [
          {
            "name": "station",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Single station ID (default KTM-001). Mutually exclusive with stations."
          },
          {
            "name": "stations",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "example": "KTM-001,DLP-001",
            "description": "Comma-separated station IDs for a combined multi-station CSV download."
          },
          {
            "name": "quality",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "ok",
                "suspect",
                "invalid"
              ]
            }
          },
          {
            "name": "from_date",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "example": "2026-01-01"
          },
          {
            "name": "to_date",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "example": "2026-06-01"
          }
        ],
        "responses": {
          "200": {
            "description": "CSV file",
            "content": {
              "text/csv": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/data/aggregate/": {
      "get": {
        "summary": "Statistical aggregates",
        "description": "Mean, min, and max of all sensor metrics grouped by time period.",
        "operationId": "getAggregateData",
        "tags": [
          "Data"
        ],
        "parameters": [
          {
            "name": "station",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "period",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "hour",
                "day",
                "week",
                "month"
              ]
            }
          },
          {
            "name": "from_date",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "to_date",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "quality",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "ok"
              ]
            }
          },
          {
            "name": "output",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "json",
                "csv"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Aggregated statistics by period",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "station": {
                      "type": "string"
                    },
                    "period": {
                      "type": "string"
                    },
                    "count": {
                      "type": "integer"
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "period_start": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "temperature_c_mean": {
                            "type": "number"
                          },
                          "temperature_c_min": {
                            "type": "number"
                          },
                          "temperature_c_max": {
                            "type": "number"
                          },
                          "humidity_rh_mean": {
                            "type": "number"
                          },
                          "pressure_hpa_mean": {
                            "type": "number"
                          },
                          "pm2_5_mean": {
                            "type": "number",
                            "nullable": true
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/data/compare/": {
      "get": {
        "summary": "Multi-station comparison",
        "description": "Time-series readings for multiple stations simultaneously, for altitude gradient analysis.",
        "operationId": "compareStations",
        "tags": [
          "Data"
        ],
        "parameters": [
          {
            "name": "stations",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated station IDs",
            "example": "KTM-001,DLP-001"
          },
          {
            "name": "hours",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 24
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Per-station time-series keyed by station ID"
          }
        }
      }
    },
    "/notes/": {
      "get": {
        "summary": "Lab notes list",
        "description": "Paginated list of published lab notes. Filterable by type and research programme.",
        "operationId": "listLabNotes",
        "tags": [
          "Content"
        ],
        "parameters": [
          {
            "name": "type",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "field",
                "build",
                "observation",
                "analysis",
                "experiment"
              ]
            }
          },
          {
            "name": "programme",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Research programme slug"
          },
          {
            "name": "from_date",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "output",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "json",
                "geojson"
              ]
            },
            "description": "Return GeoJSON FeatureCollection of geotagged notes"
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated lab notes",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "count": {
                      "type": "integer"
                    },
                    "page": {
                      "type": "integer"
                    },
                    "total_pages": {
                      "type": "integer"
                    },
                    "next": {
                      "type": "string",
                      "nullable": true,
                      "description": "URL of next page (also in Link header)"
                    },
                    "previous": {
                      "type": "string",
                      "nullable": true,
                      "description": "URL of previous page (also in Link header)"
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "slug": {
                            "type": "string"
                          },
                          "title": {
                            "type": "string"
                          },
                          "note_type": {
                            "type": "string"
                          },
                          "date": {
                            "type": "string",
                            "format": "date"
                          },
                          "location": {
                            "type": "string"
                          },
                          "summary": {
                            "type": "string"
                          },
                          "latitude": {
                            "type": "number",
                            "nullable": true
                          },
                          "longitude": {
                            "type": "number",
                            "nullable": true
                          },
                          "altitude_m": {
                            "type": "number",
                            "nullable": true
                          },
                          "tags": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          },
                          "url": {
                            "type": "string"
                          },
                          "api_url": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/notes/{slug}/": {
      "get": {
        "summary": "Lab note detail",
        "operationId": "getLabNote",
        "tags": [
          "Content"
        ],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Single lab note"
          },
          "404": {
            "description": "Not found"
          }
        }
      }
    },
    "/publications/": {
      "get": {
        "summary": "Publications list",
        "description": "Paginated list of HICS publications \u2014 peer-reviewed, preprints, data papers, and reports.",
        "operationId": "listPublications",
        "tags": [
          "Content"
        ],
        "parameters": [
          {
            "name": "pub_type",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "peer-reviewed",
                "preprint",
                "report",
                "data-paper",
                "public-writing"
              ]
            }
          },
          {
            "name": "provenance",
            "in": "query",
            "description": "hics = HICS output, collaborative = co-produced, affiliate = prior/external work by a HICS researcher",
            "schema": {
              "type": "string",
              "enum": [
                "hics",
                "collaborative",
                "affiliate"
              ]
            }
          },
          {
            "name": "year",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated publications",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "count": {
                      "type": "integer"
                    },
                    "page": {
                      "type": "integer"
                    },
                    "total_pages": {
                      "type": "integer"
                    },
                    "next": {
                      "type": "string",
                      "nullable": true,
                      "description": "URL of next page (also in Link header)"
                    },
                    "previous": {
                      "type": "string",
                      "nullable": true,
                      "description": "URL of previous page (also in Link header)"
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "integer"
                          },
                          "title": {
                            "type": "string"
                          },
                          "authors": {
                            "type": "string"
                          },
                          "journal": {
                            "type": "string"
                          },
                          "year": {
                            "type": "integer"
                          },
                          "pub_type": {
                            "type": "string"
                          },
                          "pub_type_label": {
                            "type": "string"
                          },
                          "provenance": {
                            "type": "string",
                            "enum": [
                              "hics",
                              "collaborative",
                              "affiliate"
                            ]
                          },
                          "provenance_label": {
                            "type": "string"
                          },
                          "doi": {
                            "type": "string",
                            "nullable": true
                          },
                          "open_access_url": {
                            "type": "string",
                            "nullable": true
                          },
                          "abstract": {
                            "type": "string",
                            "nullable": true
                          },
                          "plain_language_summary": {
                            "type": "string",
                            "nullable": true
                          },
                          "url": {
                            "type": "string",
                            "description": "HTML publication detail page"
                          },
                          "api_url": {
                            "type": "string",
                            "description": "API detail endpoint for this publication"
                          },
                          "ris_url": {
                            "type": "string",
                            "description": "RIS citation download (Zotero/Mendeley)"
                          },
                          "linked_authors": {
                            "type": "array",
                            "items": {
                              "type": "object",
                              "properties": {
                                "name": {
                                  "type": "string"
                                },
                                "role": {
                                  "type": "string"
                                },
                                "orcid": {
                                  "type": "string",
                                  "nullable": true
                                },
                                "url": {
                                  "type": "string"
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/publications/{id}/": {
      "get": {
        "summary": "Publication detail",
        "operationId": "getPublication",
        "tags": [
          "Content"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Single publication with abstract and linked authors"
          },
          "404": {
            "description": "Not found"
          }
        }
      }
    },
    "/team/": {
      "get": {
        "summary": "Team profiles",
        "description": "HICS team member profiles with ORCID, research areas, and linked programmes.",
        "operationId": "listTeam",
        "tags": [
          "Content"
        ],
        "responses": {
          "200": {
            "description": "Team members",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "count": {
                      "type": "integer"
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "name": {
                            "type": "string"
                          },
                          "slug": {
                            "type": "string"
                          },
                          "role": {
                            "type": "string"
                          },
                          "orcid": {
                            "type": "string",
                            "nullable": true
                          },
                          "research_areas": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          },
                          "linked_programmes": {
                            "type": "array"
                          },
                          "url": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/research/": {
      "get": {
        "summary": "Research programmes",
        "description": "All HICS research programmes with status, linked instruments, team members, and publications.",
        "operationId": "listResearch",
        "tags": [
          "Content"
        ],
        "responses": {
          "200": {
            "description": "Research programmes list"
          }
        }
      }
    },
    "/education/": {
      "get": {
        "summary": "Education programmes",
        "description": "HICS education programmes (residency, fellowship, camp, teacher development).",
        "operationId": "listEducation",
        "tags": [
          "Content"
        ],
        "responses": {
          "200": {
            "description": "Education programmes list"
          }
        }
      }
    },
    "/activity/": {
      "get": {
        "summary": "Activity feed",
        "description": "Unified chronological feed of lab notes and publications, newest first.",
        "operationId": "getActivityFeed",
        "tags": [
          "Content"
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 20,
              "maximum": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Mixed activity items sorted by date"
          }
        }
      }
    },
    "/search/": {
      "get": {
        "summary": "Full-text search",
        "description": "Search across all HICS content: pages (research, instruments, lab notes, education, team) and publications. Returns at most 20 results.",
        "operationId": "search",
        "tags": [
          "Meta"
        ],
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Search query"
          }
        ],
        "responses": {
          "200": {
            "description": "Search results",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "query": {
                      "type": "string"
                    },
                    "count": {
                      "type": "integer"
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "type": {
                            "type": "string"
                          },
                          "type_label": {
                            "type": "string"
                          },
                          "title": {
                            "type": "string"
                          },
                          "url": {
                            "type": "string"
                          },
                          "summary": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/stats/": {
      "get": {
        "summary": "Platform statistics",
        "description": "Total readings, active stations, quality percentages, and content counts.",
        "operationId": "getStats",
        "tags": [
          "Meta"
        ],
        "responses": {
          "200": {
            "description": "Platform statistics"
          }
        }
      }
    },
    "/health/": {
      "get": {
        "summary": "Health check",
        "description": "Station health check for uptime monitoring. Returns ok:true if at least one station has posted data in the last 90 minutes.",
        "operationId": "healthCheck",
        "tags": [
          "Meta"
        ],
        "responses": {
          "200": {
            "description": "Health status with per-station reading age"
          }
        }
      }
    },
    "/ingest/environmental/": {
      "post": {
        "summary": "Ingest sensor readings (batch)",
        "description": "Submit a batch of readings from a field station. Auth is `Authorization: Token <station-api-key>`; the body must carry the matching `station_id`. Idempotent on (station, timestamp) \u2014 re-POSTing is safe and is how offline backfill reconciles. Device-native field aliases (air_temp, air_hum, pressure, soil_temp, soil_moist) are accepted; unknown channels fall into module_data. Full contract: docs/INGEST_CONTRACT.md.",
        "operationId": "ingestReadings",
        "tags": [
          "Ingest"
        ],
        "security": [
          {
            "stationApiKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "station_id",
                  "readings"
                ],
                "properties": {
                  "station_id": {
                    "type": "string",
                    "example": "IESH-KMC-001"
                  },
                  "readings": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "required": [
                        "timestamp"
                      ],
                      "properties": {
                        "timestamp": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "temperature_c": {
                          "type": "number",
                          "nullable": true
                        },
                        "humidity_rh": {
                          "type": "number",
                          "nullable": true
                        },
                        "pressure_hpa": {
                          "type": "number",
                          "nullable": true
                        },
                        "altitude_m": {
                          "type": "number",
                          "nullable": true
                        },
                        "pm2_5": {
                          "type": "number",
                          "nullable": true
                        },
                        "firmware_version": {
                          "type": "string",
                          "nullable": true
                        },
                        "module_data": {
                          "type": "object",
                          "nullable": true
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Per-batch tally",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "accepted": {
                      "type": "integer",
                      "description": "New readings stored"
                    },
                    "duplicates": {
                      "type": "integer",
                      "description": "Already had that (station, timestamp)"
                    },
                    "errors": {
                      "type": "integer",
                      "description": "Rows skipped (e.g. missing timestamp)"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Empty readings list or station_id mismatch"
          },
          "401": {
            "description": "Authentication required"
          },
          "403": {
            "description": "Invalid API key or unknown station"
          }
        }
      }
    }
  },
  "tags": [
    {
      "name": "Data",
      "description": "Environmental sensor readings and downloads"
    },
    {
      "name": "Stations",
      "description": "Station metadata and status"
    },
    {
      "name": "Content",
      "description": "Lab notes, publications, team, and research programmes"
    },
    {
      "name": "Meta",
      "description": "API discovery, search, and platform statistics"
    },
    {
      "name": "Ingest",
      "description": "Authenticated data ingestion from field stations"
    }
  ],
  "components": {
    "securitySchemes": {
      "stationApiKey": {
        "type": "apiKey",
        "in": "header",
        "name": "Authorization",
        "description": "Station API key as \"Token <key>\". The matching station_id must appear in the request body."
      }
    },
    "schemas": {
      "Reading": {
        "type": "object",
        "properties": {
          "timestamp": {
            "type": "string",
            "format": "date-time"
          },
          "temperature_c": {
            "type": "number",
            "description": "Air temperature in \u00b0C (DHT22)"
          },
          "humidity_rh": {
            "type": "number",
            "description": "Relative humidity % (DHT22)"
          },
          "pressure_hpa": {
            "type": "number",
            "description": "Barometric pressure in hPa (BMP280)"
          },
          "altitude_m": {
            "type": "number",
            "description": "Station altitude above sea level (m)"
          },
          "pm1_0": {
            "type": "number",
            "nullable": true,
            "description": "PM1.0 ultra-fine particulate matter \u00b5g/m\u00b3"
          },
          "pm2_5": {
            "type": "number",
            "nullable": true,
            "description": "PM2.5 fine particulate matter \u00b5g/m\u00b3"
          },
          "pm10": {
            "type": "number",
            "nullable": true,
            "description": "PM10 coarse particulate matter \u00b5g/m\u00b3"
          },
          "co2_ppm": {
            "type": "number",
            "nullable": true,
            "description": "Carbon dioxide concentration (ppm)"
          },
          "co_ppm": {
            "type": "number",
            "nullable": true,
            "description": "Carbon monoxide concentration (ppm)"
          },
          "uv_index": {
            "type": "number",
            "nullable": true,
            "description": "UV index (dimensionless, WHO scale 0\u201311+)"
          },
          "quality_flag": {
            "type": "string",
            "enum": [
              "ok",
              "suspect",
              "invalid"
            ],
            "description": "Auto-assigned on ingest; manual override possible"
          },
          "firmware_version": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "Station": {
        "type": "object",
        "properties": {
          "station_id": {
            "type": "string",
            "example": "KTM-001"
          },
          "name": {
            "type": "string"
          },
          "location_name": {
            "type": "string"
          },
          "altitude_m": {
            "type": "number"
          },
          "latitude": {
            "type": "number",
            "nullable": true
          },
          "longitude": {
            "type": "number",
            "nullable": true
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "offline",
              "planned"
            ]
          },
          "latest_reading": {
            "type": "object",
            "properties": {
              "timestamp": {
                "type": "string",
                "format": "date-time"
              },
              "temperature_c": {
                "type": "number",
                "description": "Air temperature in \u00b0C (DHT22)"
              },
              "humidity_rh": {
                "type": "number",
                "description": "Relative humidity % (DHT22)"
              },
              "pressure_hpa": {
                "type": "number",
                "description": "Barometric pressure in hPa (BMP280)"
              },
              "altitude_m": {
                "type": "number",
                "description": "Station altitude above sea level (m)"
              },
              "pm1_0": {
                "type": "number",
                "nullable": true,
                "description": "PM1.0 ultra-fine particulate matter \u00b5g/m\u00b3"
              },
              "pm2_5": {
                "type": "number",
                "nullable": true,
                "description": "PM2.5 fine particulate matter \u00b5g/m\u00b3"
              },
              "pm10": {
                "type": "number",
                "nullable": true,
                "description": "PM10 coarse particulate matter \u00b5g/m\u00b3"
              },
              "co2_ppm": {
                "type": "number",
                "nullable": true,
                "description": "Carbon dioxide concentration (ppm)"
              },
              "co_ppm": {
                "type": "number",
                "nullable": true,
                "description": "Carbon monoxide concentration (ppm)"
              },
              "uv_index": {
                "type": "number",
                "nullable": true,
                "description": "UV index (dimensionless, WHO scale 0\u201311+)"
              },
              "quality_flag": {
                "type": "string",
                "enum": [
                  "ok",
                  "suspect",
                  "invalid"
                ],
                "description": "Auto-assigned on ingest; manual override possible"
              },
              "firmware_version": {
                "type": "string",
                "nullable": true
              }
            },
            "nullable": true
          }
        }
      }
    }
  }
}