{
  "openapi": "3.1.0",
  "info": {
    "title": "GBM API",
    "version": "1.0.0",
    "description": "Machine-readable OpenAPI documentation for GBM_API_API. Use /openapi to retrieve JSON schema."
  },
  "servers": [
    {
      "url": "/",
      "description": "Root server relative URL"
    }
  ],
  "paths": {
    "/openapi": {
      "get": {
        "summary": "Get OpenAPI document",
        "description": "Returns the OpenAPI document in JSON.",
        "responses": {
          "200": {
            "description": "OpenAPI document",
            "content": {
              "application/json": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/dataseries": {
      "get": {
        "summary": "Fetch one dataseries",
        "description": "Returns a single dataseries for a specific country and date range. The response format is selected via the path extension: append '.csv' (e.g. /dataseries.csv) for CSV output; '.json' or no extension returns JSON (default).",
        "parameters": [
          {
            "name": "seriesid",
            "in": "query",
            "required": true,
            "schema": { "type": "integer" },
            "description": "ID of the dataseries to fetch."
          },
          {
            "name": "country",
            "in": "query",
            "required": true,
            "schema": { "type": "string" },
            "description": "Country code for the dataseries."
          },
          {
            "name": "startyear",
            "in": "query",
            "schema": { "type": "integer" },
            "description": "First year to return. If omitted, the dataseries start year for the country is used. Soft limit: an earlier year is adjusted to the first available year instead of causing an error."
          },
          {
            "name": "endyear",
            "in": "query",
            "schema": { "type": "integer" },
            "description": "Last year to return. If omitted, the dataseries end year for the country is used. Soft limit: a later year is adjusted to the last available year instead of causing an error."
          },
          {
            "name": "startmonth",
            "in": "query",
            "schema": { "type": "integer", "minimum": 1, "maximum": 12 },
            "description": "First month or period to return. Defaults to 1."
          },
          {
            "name": "endmonth",
            "in": "query",
            "schema": { "type": "integer", "minimum": 1, "maximum": 12 },
            "description": "Last month or period to return. Defaults to 12."
          }
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful dataseries response. Returns JSON by default, or CSV when the path ends with '.csv'.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/DataseriesResponse" }
              },
              "text/csv": {
                "schema": { "$ref": "#/components/schemas/DataseriesCsv" }
              }
            }
          },
          "400": { "description": "Bad request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "401": { "description": "Unauthorized", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "404": { "description": "Not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "429": { "description": "Too many requests", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "500": { "description": "Server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/dataseries/multiple": {
      "get": {
        "summary": "Fetch multiple dataseries",
        "description": "Returns multiple dataseries for combinations of series IDs and countries. The response format is selected via the path extension: append '.csv' (e.g. /dataseries/multiple.csv) for CSV output; '.json' or no extension returns JSON (default).",
        "parameters": [
          {
            "name": "seriesids",
            "in": "query",
            "required": true,
            "schema": { "type": "string" },
            "description": "Comma-separated series IDs."
          },
          {
            "name": "countries",
            "in": "query",
            "required": true,
            "schema": { "type": "string" },
            "description": "Comma-separated country codes."
          },
          {
            "name": "startyear",
            "in": "query",
            "schema": { "type": "integer" },
            "description": "First year to return, applied to every combination. If omitted, each combination's own start year is used. Soft limit: applied per combination, so an earlier year is adjusted to that combination's first available year instead of causing an error."
          },
          {
            "name": "endyear",
            "in": "query",
            "schema": { "type": "integer" },
            "description": "Last year to return, applied to every combination. If omitted, each combination's own end year is used. Soft limit: applied per combination, so a later year is adjusted to that combination's last available year instead of causing an error."
          },
          {
            "name": "startmonth",
            "in": "query",
            "schema": { "type": "integer", "minimum": 1, "maximum": 12 }
          },
          {
            "name": "endmonth",
            "in": "query",
            "schema": { "type": "integer", "minimum": 1, "maximum": 12 }
          }
        ],
        "security": [{ "bearerAuth": [] }],
        "responses": {
          "200": {
            "description": "Successful multiple dataseries response. Returns JSON by default, or CSV when the path ends with '.csv'.",
            "content": {
              "application/json": {
                "schema": { "type": "array", "items": { "$ref": "#/components/schemas/DataseriesResponse" } }
              },
              "text/csv": {
                "schema": { "$ref": "#/components/schemas/DataseriesCsv" }
              }
            }
          },
          "400": { "description": "Bad request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "401": { "description": "Unauthorized", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "413": { "description": "Payload too large", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "429": { "description": "Too many requests", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "500": { "description": "Server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/user/countries": {
      "get": {
        "summary": "List the countries booked for the API key",
        "description": "Returns the ISO-2 codes of all countries the given API key is allowed to fetch. Requesting data for a country outside this list results in 403.",
        "security": [{ "bearerAuth": [] }],
        "responses": {
          "200": {
            "description": "Booked country codes.",
            "content": {
              "application/json": {
                "schema": { "type": "array", "items": { "type": "string" } }
              }
            }
          },
          "400": { "description": "Bad request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "401": { "description": "Unauthorized", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "500": { "description": "Server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/dataseries/metrics": {
      "get": {
        "summary": "Fetch analysis metrics for one dataseries",
        "description": "Returns a set of analysis metrics (descriptive statistics, growth/trend indicators and volatility measures) computed for a single dataseries over the given country and date range. Works for exactly one dataseries, not for multiple. Metrics that cannot be computed meaningfully for the given data are returned as null.",
        "parameters": [
          {
            "name": "seriesid",
            "in": "query",
            "required": true,
            "schema": { "type": "integer" },
            "description": "ID of the dataseries to analyse."
          },
          {
            "name": "country",
            "in": "query",
            "required": true,
            "schema": { "type": "string" },
            "description": "Country code for the dataseries."
          },
          {
            "name": "startyear",
            "in": "query",
            "schema": { "type": "integer" },
            "description": "First year to include. If omitted, the dataseries start year for the country is used. Soft limit: an earlier year is adjusted to the first available year instead of causing an error."
          },
          {
            "name": "endyear",
            "in": "query",
            "schema": { "type": "integer" },
            "description": "Last year to include. If omitted, the dataseries end year for the country is used. Soft limit: a later year is adjusted to the last available year instead of causing an error."
          },
          {
            "name": "startmonth",
            "in": "query",
            "schema": { "type": "integer", "minimum": 1, "maximum": 12 },
            "description": "First month or period to include. Defaults to 1."
          },
          {
            "name": "endmonth",
            "in": "query",
            "schema": { "type": "integer", "minimum": 1, "maximum": 12 },
            "description": "Last month or period to include. Defaults to 12."
          }
        ],
        "security": [{ "bearerAuth": [] }],
        "responses": {
          "200": {
            "description": "Successful metrics response.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/MetricsResponse" }
              }
            }
          },
          "400": { "description": "Bad request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "401": { "description": "Unauthorized", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "404": { "description": "Not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "429": { "description": "Too many requests", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "500": { "description": "Server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/catalogs": {
      "get": {
        "summary": "List all catalogs",
        "responses": {
          "200": { "description": "Catalog list.", "content": { "application/json": { "schema": { "type": "array", "items": { "type": "string" } } } } }
        }
      }
    },
    "/series": {
      "get": {
        "summary": "List all series names",
        "responses": {
          "200": { "description": "Series list.", "content": { "application/json": { "schema": { "type": "array", "items": { "type": "string" } } } } }
        }
      }
    },
    "/frequencies": {
      "get": {
        "summary": "List all frequencies",
        "responses": {
          "200": { "description": "Frequencies list.", "content": { "application/json": { "schema": { "type": "array", "items": { "type": "string" } } } } }
        }
      }
    },
    "/units": {
      "get": {
        "summary": "List all units",
        "responses": {
          "200": { "description": "Units list.", "content": { "application/json": { "schema": { "type": "array", "items": { "type": "string" } } } } }
        }
      }
    },
    "/dataseries/catalog": {
      "get": {
        "summary": "Find dataseries by catalog filters",
        "parameters": [
          { "name": "catalog", "in": "query", "schema": { "type": "string" } },
          { "name": "series", "in": "query", "schema": { "type": "string" } },
          { "name": "frequency", "in": "query", "schema": { "type": "string" } },
          { "name": "unit", "in": "query", "schema": { "type": "string" } }
        ],
        "responses": {
          "200": { "description": "Filtered dataseries list.", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/CatalogItem" } } } } }
        }
      }
    },
    "/dataseries/catalog/{seriesid}": {
      "get": {
        "summary": "Get dataseries metadata by ID",
        "parameters": [
          { "name": "seriesid", "in": "path", "required": true, "schema": { "type": "integer" } }
        ],
        "responses": {
          "200": { "description": "Dataseries metadata.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CatalogItem" } } } },
          "400": { "description": "Bad request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "404": { "description": "Not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "API key"
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "message": { "type": "string" },
          "statusCode": { "type": "integer" }
        },
        "required": ["message"]
      },
      "DataPoint": {
        "type": "object",
        "properties": {
          "value": { "type": "number", "format": "float" },
          "date": { "type": "string", "description": "Date string in dd/MM/yyyy format." }
        },
        "required": ["value", "date"]
      },
      "DataseriesResponse": {
        "type": "object",
        "properties": {
          "seriesid": { "type": "integer" },
          "seriesname": { "type": "string" },
          "country": { "type": "string" },
          "startyear": { "type": "integer" },
          "endyear": { "type": "integer" },
          "startmonth": { "type": "integer" },
          "endmonth": { "type": "integer" },
          "frequency": { "type": "string" },
          "data": { "type": "array", "items": { "$ref": "#/components/schemas/DataPoint" } }
        },
        "required": ["seriesid", "seriesname", "country", "startyear", "endyear", "startmonth", "endmonth", "frequency", "data"]
      },
      "DataseriesCsv": {
        "type": "string",
        "description": "CSV representation, selected by appending '.csv' to the request path. The first line is the header 'seriesid,seriesname,country,year,period,date,value'; each following line is one datapoint. Numeric values use '.' as the decimal separator. For the multiple endpoint, the rows of all requested series are concatenated under a single header."
      },
      "Availability": {
        "type": "object",
        "properties": {
          "country": { "type": "string" },
          "start": { "type": "integer" },
          "end": { "type": "integer" },
          "latest_update": { "type": "string" }
        },
        "required": ["country", "start", "end"]
      },
      "CatalogItem": {
        "type": "object",
        "properties": {
          "id": { "type": "integer" },
          "catalog": { "type": "string" },
          "series": { "type": "string" },
          "frequency": { "type": "string" },
          "unit": { "type": "string" },
          "name": { "type": "string" },
          "available_countries": { "type": "array", "items": { "$ref": "#/components/schemas/Availability" } }
        },
        "required": ["id", "catalog", "series", "frequency", "unit", "name", "available_countries"]
      },
      "Extremum": {
        "type": "object",
        "properties": {
          "value": { "type": "number" },
          "date": { "type": "string", "description": "Date string in dd/MM/yyyy format." }
        },
        "required": ["value", "date"]
      },
      "Metrics": {
        "type": "object",
        "description": "Analysis metrics for a dataseries over the requested period. Fields that cannot be computed meaningfully are null.",
        "properties": {
          "count": { "type": "integer", "description": "Number of datapoints in the period." },
          "first": { "anyOf": [ { "$ref": "#/components/schemas/Extremum" }, { "type": "null" } ], "description": "First datapoint (chronologically)." },
          "last": { "anyOf": [ { "$ref": "#/components/schemas/Extremum" }, { "type": "null" } ], "description": "Last datapoint (chronologically)." },
          "min": { "anyOf": [ { "$ref": "#/components/schemas/Extremum" }, { "type": "null" } ], "description": "Datapoint with the smallest value." },
          "max": { "anyOf": [ { "$ref": "#/components/schemas/Extremum" }, { "type": "null" } ], "description": "Datapoint with the largest value." },
          "mean": { "type": ["number", "null"] },
          "median": { "type": ["number", "null"] },
          "sum": { "type": ["number", "null"], "description": "Sum of all values. null for non-quantity units (unit containing 'percentage' or 'index')." },
          "stddev": { "type": ["number", "null"], "description": "Sample standard deviation. null for fewer than 2 datapoints." },
          "absolute_change": { "type": ["number", "null"], "description": "last minus first value." },
          "total_change_pct": { "type": ["number", "null"], "description": "Percentage change from the first to the last value." },
          "cagr": { "type": ["number", "null"], "description": "Compound annual growth rate in percent. null unless all values are strictly positive." },
          "yoy_latest": { "type": ["number", "null"], "description": "Percentage change of the latest value versus the same period one year earlier. null if no matching prior-year point exists." },
          "trend_slope": { "type": ["number", "null"], "description": "Slope of the linear regression, annualized (change per year) so it is comparable across frequencies." },
          "trend_direction": { "type": ["string", "null"], "enum": ["increasing", "decreasing", "flat", null], "description": "Direction derived from trend_slope." },
          "coefficient_of_variation": { "type": ["number", "null"], "description": "Standard deviation divided by the absolute mean." },
          "volatility": { "type": ["number", "null"], "description": "Sample standard deviation of period-over-period changes. null for fewer than 3 datapoints." }
        },
        "required": ["count", "first", "last", "min", "max", "mean", "median", "sum", "stddev", "absolute_change", "total_change_pct", "cagr", "yoy_latest", "trend_slope", "trend_direction", "coefficient_of_variation", "volatility"]
      },
      "MetricsResponse": {
        "type": "object",
        "properties": {
          "seriesid": { "type": "integer" },
          "seriesname": { "type": "string" },
          "country": { "type": "string" },
          "startyear": { "type": "integer" },
          "endyear": { "type": "integer" },
          "startmonth": { "type": "integer" },
          "endmonth": { "type": "integer" },
          "frequency": { "type": "string" },
          "unit": { "type": "string" },
          "metrics": { "$ref": "#/components/schemas/Metrics" }
        },
        "required": ["seriesid", "seriesname", "country", "startyear", "endyear", "startmonth", "endmonth", "frequency", "unit", "metrics"]
      }
    }
  }
}
