{
    "paths": {
      "/heisenberg002-b/api/v1/sensors": {
        "get": {
          "responses": {
            "200": {
              "description": "OK",
              "content": {
                "application/json": {
                  "schema": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/Sensor"
                    }
                  }
                }
              }
            },
            "default": {
              "$ref": "#/components/responses/DEFAULT_ERROR"
            }
          },
          "summary": "List all sensors with `has_images` and available metrics.",
          "tags": [
            "sensors"
          ]
        }
      },
      "/heisenberg002-b/api/v1/sensors/{sensor}": {
        "get": {
          "responses": {
            "404": {
              "description": "Sensor not found"
            },
            "200": {
              "description": "OK",
              "content": {
                "application/json": {
                  "schema": {
                    "$ref": "#/components/schemas/Sensor"
                  }
                }
              }
            },
            "default": {
              "$ref": "#/components/responses/DEFAULT_ERROR"
            }
          },
          "summary": "Return a single sensor by id or key.",
          "tags": [
            "sensors"
          ]
        },
        "parameters": [
          {
            "in": "path",
            "name": "sensor",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          }
        ]
      },
      "/heisenberg002-b/api/v1/metrics": {
        "get": {
          "responses": {
            "200": {
              "description": "OK",
              "content": {
                "application/json": {
                  "schema": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/Metric"
                    }
                  }
                }
              }
            },
            "default": {
              "$ref": "#/components/responses/DEFAULT_ERROR"
            }
          },
          "summary": "List all metrics: measurement_types plus synthetic entries for each external_measurements_types prefix.",
          "tags": [
            "metrics"
          ]
        }
      },
      "/heisenberg002-b/api/v1/metrics/{metric}": {
        "get": {
          "responses": {
            "404": {
              "description": "Not Found"
            },
            "200": {
              "description": "OK",
              "content": {
                "application/json": {
                  "schema": {
                    "$ref": "#/components/schemas/Metric"
                  }
                }
              }
            },
            "default": {
              "$ref": "#/components/responses/DEFAULT_ERROR"
            }
          },
          "summary": "Return a single metric by id or key. Resolves measurement_types first, then external prefixes by key.",
          "tags": [
            "metrics"
          ]
        },
        "parameters": [
          {
            "in": "path",
            "name": "metric",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          }
        ]
      },
      "/heisenberg002-b/api/v1/data": {
        "get": {
          "parameters": [
            {
              "in": "query",
              "name": "type",
              "schema": {
                "type": "string",
                "default": "all",
                "enum": [
                  "measurement",
                  "image",
                  "text",
                  "all"
                ]
              },
              "required": false
            },
            {
              "in": "query",
              "name": "window",
              "schema": {
                "type": "string",
                "default": null,
                "nullable": true
              },
              "required": false
            },
            {
              "in": "query",
              "name": "from",
              "schema": {
                "type": "string",
                "default": null,
                "nullable": true
              },
              "required": false
            },
            {
              "in": "query",
              "name": "to",
              "schema": {
                "type": "string",
                "default": null,
                "nullable": true
              },
              "required": false
            },
            {
              "in": "query",
              "name": "sensor_id",
              "schema": {
                "type": "integer",
                "default": null,
                "nullable": true
              },
              "required": false
            },
            {
              "in": "query",
              "name": "sensor",
              "schema": {
                "type": "string",
                "default": null,
                "nullable": true
              },
              "required": false
            },
            {
              "in": "query",
              "name": "metric",
              "schema": {
                "type": "string",
                "default": null,
                "nullable": true
              },
              "required": false
            }
          ],
          "responses": {
            "422": {
              "$ref": "#/components/responses/UNPROCESSABLE_CONTENT"
            },
            "404": {
              "description": "Not Found"
            },
            "400": {
              "description": "Bad Request"
            },
            "200": {
              "description": "OK",
              "content": {
                "application/json": {
                  "schema": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/DataItem"
                    }
                  }
                }
              }
            },
            "default": {
              "$ref": "#/components/responses/DEFAULT_ERROR"
            }
          },
          "summary": "Unified measurements + images, sorted by timestamp. Defaults to last 24h.",
          "description": "`metric` can be a `measurement_types` key/id (filters measurements) or an\n`external_measurements_types` prefix key (filters files by raw filename prefix). When a\nmetric is supplied, `type=all` collapses to the relevant kind so the result stays scoped\nto that metric.",
          "tags": [
            "data"
          ]
        }
      },
      "/heisenberg002-b/api/v1/metrics/{metric}/data": {
        "get": {
          "parameters": [
            {
              "in": "query",
              "name": "type",
              "schema": {
                "type": "string",
                "default": "all",
                "enum": [
                  "measurement",
                  "image",
                  "text",
                  "all"
                ]
              },
              "required": false
            },
            {
              "in": "query",
              "name": "window",
              "schema": {
                "type": "string",
                "default": null,
                "nullable": true
              },
              "required": false
            },
            {
              "in": "query",
              "name": "from",
              "schema": {
                "type": "string",
                "default": null,
                "nullable": true
              },
              "required": false
            },
            {
              "in": "query",
              "name": "to",
              "schema": {
                "type": "string",
                "default": null,
                "nullable": true
              },
              "required": false
            },
            {
              "in": "query",
              "name": "sensor_id",
              "schema": {
                "type": "integer",
                "default": null,
                "nullable": true
              },
              "required": false
            },
            {
              "in": "query",
              "name": "sensor",
              "schema": {
                "type": "string",
                "default": null,
                "nullable": true
              },
              "required": false
            },
            {
              "in": "path",
              "name": "metric",
              "description": "Metric id (rowid in measurement_types) or key (slug from /metrics).",
              "schema": {
                "type": "string"
              },
              "required": true
            }
          ],
          "responses": {
            "422": {
              "$ref": "#/components/responses/UNPROCESSABLE_CONTENT"
            },
            "404": {
              "description": "Not Found"
            },
            "400": {
              "description": "Bad Request"
            },
            "200": {
              "description": "OK",
              "content": {
                "application/json": {
                  "schema": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/DataItem"
                    }
                  }
                }
              }
            },
            "default": {
              "$ref": "#/components/responses/DEFAULT_ERROR"
            }
          },
          "summary": "Alias of `/data?metric=<metric>`. All other query params (`type`, `window`, `from`, `to`,\n`sensor`, `sensor_id`) behave identically to `/data`.",
          "tags": [
            "data"
          ]
        },
        "parameters": [
          {
            "in": "path",
            "name": "metric",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          }
        ]
      },
      "/heisenberg002-b/api/v1/sensors/{sensor}/data": {
        "get": {
          "parameters": [
            {
              "in": "query",
              "name": "type",
              "schema": {
                "type": "string",
                "default": "all",
                "enum": [
                  "measurement",
                  "image",
                  "text",
                  "all"
                ]
              },
              "required": false
            },
            {
              "in": "query",
              "name": "window",
              "schema": {
                "type": "string",
                "default": null,
                "nullable": true
              },
              "required": false
            },
            {
              "in": "query",
              "name": "from",
              "schema": {
                "type": "string",
                "default": null,
                "nullable": true
              },
              "required": false
            },
            {
              "in": "query",
              "name": "to",
              "schema": {
                "type": "string",
                "default": null,
                "nullable": true
              },
              "required": false
            },
            {
              "in": "query",
              "name": "metric",
              "schema": {
                "type": "string",
                "default": null,
                "nullable": true
              },
              "required": false
            },
            {
              "in": "path",
              "name": "sensor",
              "description": "Sensor id or key (slug of sensors.type).",
              "schema": {
                "type": "string"
              },
              "required": true
            }
          ],
          "responses": {
            "422": {
              "$ref": "#/components/responses/UNPROCESSABLE_CONTENT"
            },
            "404": {
              "description": "Not Found"
            },
            "400": {
              "description": "Bad Request"
            },
            "200": {
              "description": "OK",
              "content": {
                "application/json": {
                  "schema": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/DataItem"
                    }
                  }
                }
              }
            },
            "default": {
              "$ref": "#/components/responses/DEFAULT_ERROR"
            }
          },
          "summary": "Alias of `/data?sensor=<sensor>`. All other query params (including `metric`, which can\nitself be a measurement key or a prefix) behave identically to `/data`.",
          "tags": [
            "data"
          ]
        },
        "parameters": [
          {
            "in": "path",
            "name": "sensor",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          }
        ]
      },
      "/heisenberg002-b/api/v1/sensors/{sensor}/metrics/{metric}": {
        "get": {
          "parameters": [
            {
              "in": "query",
              "name": "type",
              "schema": {
                "type": "string",
                "default": "all",
                "enum": [
                  "measurement",
                  "image",
                  "text",
                  "all"
                ]
              },
              "required": false
            },
            {
              "in": "query",
              "name": "window",
              "schema": {
                "type": "string",
                "default": null,
                "nullable": true
              },
              "required": false
            },
            {
              "in": "query",
              "name": "from",
              "schema": {
                "type": "string",
                "default": null,
                "nullable": true
              },
              "required": false
            },
            {
              "in": "query",
              "name": "to",
              "schema": {
                "type": "string",
                "default": null,
                "nullable": true
              },
              "required": false
            },
            {
              "in": "path",
              "name": "sensor",
              "description": "Sensor id or key (slug of sensors.type).",
              "schema": {
                "type": "string"
              },
              "required": true
            },
            {
              "in": "path",
              "name": "metric",
              "description": "Metric id (rowid) or key (slug of measurement_types.name).",
              "schema": {
                "type": "string"
              },
              "required": true
            }
          ],
          "responses": {
            "422": {
              "$ref": "#/components/responses/UNPROCESSABLE_CONTENT"
            },
            "404": {
              "description": "Not Found"
            },
            "400": {
              "description": "Bad Request"
            },
            "200": {
              "description": "OK",
              "content": {
                "application/json": {
                  "schema": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/DataItem"
                    }
                  }
                }
              }
            },
            "default": {
              "$ref": "#/components/responses/DEFAULT_ERROR"
            }
          },
          "summary": "Specific metric for a sensor (both by id or key). Measurement-type metrics only.",
          "tags": [
            "data"
          ]
        },
        "parameters": [
          {
            "in": "path",
            "name": "sensor",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          },
          {
            "in": "path",
            "name": "metric",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          }
        ]
      },
      "/heisenberg002-b/api/v1/files/{filename}": {
        "get": {
          "responses": {
            "404": {
              "description": "Not Found"
            },
            "400": {
              "description": "Bad Request"
            },
            "default": {
              "$ref": "#/components/responses/DEFAULT_ERROR"
            }
          },
          "summary": "Serve an on-disk file from the image directories. Filenames containing `/`, `\\`, `..` or NUL are rejected.",
          "tags": [
            "images"
          ]
        },
        "parameters": [
          {
            "in": "path",
            "name": "filename",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          }
        ]
      },
      "/heisenberg002-b/api/v1/files/live/{prefix}": {
        "get": {
          "responses": {
            "504": {
              "description": "Gateway Timeout"
            },
            "404": {
              "description": "Not Found"
            },
            "default": {
              "$ref": "#/components/responses/DEFAULT_ERROR"
            }
          },
          "summary": "Read the FIFO at /dev/shm/odin/<prefix> and return its content. 5s timeout.",
          "tags": [
            "images"
          ]
        },
        "parameters": [
          {
            "in": "path",
            "name": "prefix",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          }
        ]
      },
      "/heisenberg002-b/api/v1/configs": {
        "get": {
          "responses": {
            "200": {
              "description": "OK",
              "content": {
                "application/json": {
                  "schema": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/Config"
                    }
                  }
                }
              }
            },
            "default": {
              "$ref": "#/components/responses/DEFAULT_ERROR"
            }
          },
          "summary": "List all distinct configs.",
          "tags": [
            "configs"
          ]
        }
      },
      "/heisenberg002-b/api/v1/configs/latest": {
        "get": {
          "responses": {
            "404": {
              "description": "Not Found"
            },
            "200": {
              "description": "OK",
              "content": {
                "application/json": {
                  "schema": {
                    "$ref": "#/components/schemas/Config"
                  }
                }
              }
            },
            "default": {
              "$ref": "#/components/responses/DEFAULT_ERROR"
            }
          },
          "summary": "Config from the most recent measurement across all sensors.",
          "tags": [
            "configs"
          ]
        }
      },
      "/heisenberg002-b/api/v1/configs/{md5}": {
        "get": {
          "responses": {
            "404": {
              "description": "Not Found"
            },
            "200": {
              "description": "OK",
              "content": {
                "application/json": {
                  "schema": {
                    "$ref": "#/components/schemas/Config"
                  }
                }
              }
            },
            "default": {
              "$ref": "#/components/responses/DEFAULT_ERROR"
            }
          },
          "summary": "Config by its MD5 hash.",
          "tags": [
            "configs"
          ]
        },
        "parameters": [
          {
            "in": "path",
            "name": "md5",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          }
        ]
      },
      "/heisenberg002-b/api/v1/configs/{md5}/raw": {
        "get": {
          "responses": {
            "404": {
              "description": "Not Found"
            },
            "default": {
              "$ref": "#/components/responses/DEFAULT_ERROR"
            }
          },
          "summary": "Raw TOML source of the config.",
          "tags": [
            "configs"
          ]
        },
        "parameters": [
          {
            "in": "path",
            "name": "md5",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          }
        ]
      },
      "/heisenberg002-b/api/v1/sensors/{sensor}/config": {
        "get": {
          "parameters": [
            {
              "in": "query",
              "name": "at",
              "schema": {
                "type": "string",
                "default": null,
                "nullable": true
              },
              "required": false
            },
            {
              "in": "path",
              "name": "sensor",
              "description": "Sensor id or key (slug of sensors.type).",
              "schema": {
                "type": "string"
              },
              "required": true
            }
          ],
          "responses": {
            "422": {
              "$ref": "#/components/responses/UNPROCESSABLE_CONTENT"
            },
            "404": {
              "description": "Not Found"
            },
            "400": {
              "description": "Bad Request"
            },
            "200": {
              "description": "OK",
              "content": {
                "application/json": {
                  "schema": {
                    "$ref": "#/components/schemas/Config"
                  }
                }
              }
            },
            "default": {
              "$ref": "#/components/responses/DEFAULT_ERROR"
            }
          },
          "summary": "Config active for a sensor (id or key), optionally at a specific point in time.",
          "description": "The returned `config` is sliced down to the TOML section keyed by `sensors.module` (or\n`sensors.type` if `module` is null) \u2014 only the per-sensor settings, not the whole document.\nSensors that produce only files (e.g. allsky) have no `measurements` rows and so no\ndirect config link; for those, the deployment-wide latest config is used as the source\ndocument before the section is extracted.",
          "tags": [
            "configs"
          ]
        },
        "parameters": [
          {
            "in": "path",
            "name": "sensor",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          }
        ]
      },
      "/heisenberg002-b/api/v1/sensors/{sensor}/config/raw": {
        "get": {
          "parameters": [
            {
              "in": "query",
              "name": "at",
              "schema": {
                "type": "string",
                "default": null,
                "nullable": true
              },
              "required": false
            },
            {
              "in": "path",
              "name": "sensor",
              "description": "Sensor id or key (slug of sensors.type).",
              "schema": {
                "type": "string"
              },
              "required": true
            }
          ],
          "responses": {
            "422": {
              "$ref": "#/components/responses/UNPROCESSABLE_CONTENT"
            },
            "404": {
              "description": "Not Found"
            },
            "400": {
              "description": "Bad Request"
            },
            "default": {
              "$ref": "#/components/responses/DEFAULT_ERROR"
            }
          },
          "summary": "Raw TOML source restricted to the sensor's section.",
          "description": "Same source selection (per-sensor measurements, falling back to deployment-wide latest)\nand same section-naming rule as `/sensors/<sensor>/config`. Returns an empty body when\nthe source TOML has no section for that sensor.",
          "tags": [
            "configs"
          ]
        },
        "parameters": [
          {
            "in": "path",
            "name": "sensor",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          }
        ]
      }
    },
    "info": {
      "title": "miratlas REST API",
      "version": "v1"
    },
    "tags": [
      {
        "name": "sensors",
        "description": "Sensor metadata"
      },
      {
        "name": "metrics",
        "description": "Metric type catalog"
      },
      {
        "name": "data",
        "description": "Measurements and images"
      },
      {
        "name": "images",
        "description": "External file downloads"
      },
      {
        "name": "configs",
        "description": "Sensor configs (TOML)"
      }
    ],
    "openapi": "3.0.3",
    "components": {
      "schemas": {
        "Error": {
          "type": "object",
          "properties": {
            "code": {
              "type": "integer",
              "description": "Error code"
            },
            "status": {
              "type": "string",
              "description": "Error name"
            },
            "message": {
              "type": "string",
              "description": "Error message"
            },
            "errors": {
              "type": "object",
              "description": "Errors",
              "additionalProperties": {}
            }
          },
          "additionalProperties": false
        },
        "PaginationMetadata": {
          "type": "object",
          "properties": {
            "total": {
              "type": "integer",
              "description": "Total number of items."
            },
            "total_pages": {
              "type": "integer",
              "description": "Total number of pages."
            },
            "first_page": {
              "type": "integer",
              "description": "First available page number."
            },
            "last_page": {
              "type": "integer",
              "description": "Last available page number."
            },
            "page": {
              "type": "integer",
              "description": "Current page number."
            },
            "previous_page": {
              "type": "integer",
              "description": "Previous page number."
            },
            "next_page": {
              "type": "integer",
              "description": "Next page number."
            }
          },
          "additionalProperties": false
        },
        "Sensor": {
          "type": "object",
          "properties": {
            "id": {
              "type": "integer"
            },
            "key": {
              "type": "string"
            },
            "type": {
              "type": "string",
              "nullable": true
            },
            "module": {
              "type": "string",
              "nullable": true
            },
            "serial": {
              "type": "string",
              "nullable": true
            },
            "software_version": {
              "type": "string",
              "nullable": true
            },
            "has_images": {
              "type": "boolean"
            },
            "metrics": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          },
          "additionalProperties": false
        },
        "Metric": {
          "type": "object",
          "properties": {
            "id": {
              "type": "integer",
              "nullable": true
            },
            "key": {
              "type": "string"
            },
            "name": {
              "type": "string"
            },
            "unit": {
              "type": "string",
              "nullable": true
            },
            "lim_min": {
              "type": "number",
              "nullable": true
            },
            "lim_max": {
              "type": "number",
              "nullable": true
            },
            "source": {
              "type": "string",
              "description": "external_measurements_types.sensor_source for prefix-derived metrics, otherwise null",
              "nullable": true
            },
            "file_type": {
              "type": "string",
              "description": "external_measurements_types.file_type for prefix-derived metrics, otherwise null",
              "nullable": true
            }
          },
          "additionalProperties": false
        },
        "DataItem": {
          "type": "object",
          "properties": {
            "type": {
              "type": "string",
              "description": "measurement, image, or text"
            },
            "ts": {
              "type": "integer"
            },
            "sensor_id": {
              "type": "integer",
              "nullable": true
            },
            "sensor_name": {
              "type": "string",
              "nullable": true
            },
            "metric": {
              "type": "string",
              "nullable": true
            },
            "value": {
              "type": "number",
              "nullable": true
            },
            "filename": {
              "type": "string",
              "nullable": true
            },
            "url": {
              "type": "string",
              "nullable": true
            }
          },
          "additionalProperties": false
        },
        "Config": {
          "type": "object",
          "properties": {
            "id": {
              "type": "integer"
            },
            "version": {
              "type": "string",
              "nullable": true
            },
            "md5": {
              "type": "string",
              "nullable": true
            },
            "config": {
              "nullable": true
            }
          },
          "additionalProperties": false
        }
      },
      "responses": {
        "DEFAULT_ERROR": {
          "description": "Default error response",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Error"
              }
            }
          }
        },
        "UNPROCESSABLE_CONTENT": {
          "description": "Unprocessable Content",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Error"
              }
            }
          }
        }
      }
    }
  }