openapi: 3.0.3
info:
  title: GribStream Weather API
  version: "1.1.0"
servers:
  # - url: https://gribstream.com
paths:
  /api/v2/{type}/timeseries:
    post:
      summary: Timeseries “best forecast” query. Returns, for every requested forecasted_time, the most-recent model run that predicted it, subject to optional time-travel and horizon filters.
      operationId: requestTimeseries
      parameters:
        - name: type
          in: path
          required: true
          description: Weather model code
          schema:
            type: string
            enum:
              [hrrr, rap, nbm, gfs, graphcast,
               gefsatmosmean, gefsatmos, gefschem, gefswaveprob,
               cfsipvf, cfsocnf, cfsflxf, cfspgbf, ifsoper, ifswave, ifsenfo, ifswaef, aifsoper, aifsenfo, refsprslev, rrfsprslev]
        - name: Accept
          in: header
          required: false
          description: Desired response format (defaults to text/csv)
          schema:
            type: string
            enum: ["text/csv", "application/json", "application/ndjson"]
            default: text/csv
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/HistoryRequest"
            examples:
              RangeExample:
                summary: Range query with coordinates
                value:
                  fromTime: "2025-05-01T00:00:00Z"
                  untilTime: "2025-05-02T00:00:00Z"
                  coordinates:
                    - lat: 40.758
                      lon: -73.985
                      name: TimesSquare
                  variables:
                    - name: TMP
                      level: "2 m above ground"
                      alias: temp
              GridExample:
                summary: Single-time grid query
                value:
                  timesList: ["2025-01-01T00:00:00Z"]
                  grid:
                    minLatitude: 25.0
                    maxLatitude: 27.0
                    minLongitude: -122.0
                    maxLongitude: -120.0
                    step: 0.5
                  variables:
                    - name: TMP
                      level: "2 m above ground"
                      alias: temp
              ExpressionsExample:
                summary: Derived columns and boolean filter
                value:
                  fromTime: "2024-09-10T00:00:00Z"
                  untilTime: "2024-09-10T10:00:00Z"
                  coordinates:
                    - lat: 40.7306
                      lon: -73.9352
                      name: NYC
                  variables:
                    - name: UGRD
                      level: "1000 mb"
                      alias: uwind
                    - name: VGRD
                      level: "1000 mb"
                      alias: vwind
                  expressions:
                    - expression: func.Hypot(uwind, vwind)
                      alias: wind_mag
                    - expression: int(270 - func.Atan2(vwind, uwind) * 180 / 3.14159) % 360
                      alias: wind_dir
                  filter:
                    expression: wind_mag > 4
      responses:
        "200":
          description: Successful response
          content:
            text/csv:
              schema:
                type: string
            application/json:
              schema:
                $ref: "#/components/schemas/ForecastArray"
            application/ndjson:
              schema:
                type: string
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
        "500":
          description: Server error

  /api/v2/{type}/history:
    post:
      summary: Legacy alias of /timeseries. Returns, for every requested forecasted_time, the most-recent model run that predicted it, subject to optional time-travel and horizon filters.
      deprecated: true
      operationId: requestHistoricalData
      parameters:
        - name: type
          in: path
          required: true
          description: Weather model code
          schema:
            type: string
            enum:
              [hrrr, rap, nbm, gfs, graphcast,
               gefsatmosmean, gefsatmos, gefschem, gefswaveprob,
               cfsipvf, cfsocnf, cfsflxf, cfspgbf, ifsoper, ifswave, ifsenfo, ifswaef, aifsoper, aifsenfo, refsprslev, rrfsprslev]
        - name: Accept
          in: header
          required: false
          description: Desired response format (defaults to text/csv)
          schema:
            type: string
            enum: ["text/csv", "application/json", "application/ndjson"]
            default: text/csv
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/HistoryRequest"
            examples:
              RangeExample:
                summary: Range query with coordinates
                value:
                  fromTime: "2025-05-01T00:00:00Z"
                  untilTime: "2025-05-02T00:00:00Z"
                  coordinates:
                    - lat: 40.758
                      lon: -73.985
                      name: TimesSquare
                  variables:
                    - name: TMP
                      level: "2 m above ground"
                      alias: temp
              GridExample:
                summary: Single-time grid query
                value:
                  timesList: ["2025-01-01T00:00:00Z"]
                  grid:
                    minLatitude: 25.0
                    maxLatitude: 27.0
                    minLongitude: -122.0
                    maxLongitude: -120.0
                    step: 0.5
                  variables:
                    - name: TMP
                      level: "2 m above ground"
                      alias: temp
              ExpressionsExample:
                summary: Derived columns and boolean filter
                value:
                  fromTime: "2024-09-10T00:00:00Z"
                  untilTime: "2024-09-10T10:00:00Z"
                  coordinates:
                    - lat: 40.7306
                      lon: -73.9352
                      name: NYC
                  variables:
                    - name: UGRD
                      level: "1000 mb"
                      alias: uwind
                    - name: VGRD
                      level: "1000 mb"
                      alias: vwind
                  expressions:
                    - expression: func.Hypot(uwind, vwind)
                      alias: wind_mag
                    - expression: int(270 - func.Atan2(vwind, uwind) * 180 / 3.14159) % 360
                      alias: wind_dir
                  filter:
                    expression: wind_mag > 4
      responses:
        "200":
          description: Successful response
          content:
            text/csv:
              schema:
                type: string
            application/json:
              schema:
                $ref: "#/components/schemas/ForecastArray"
            application/ndjson:
              schema:
                type: string
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
        "500":
          description: Server error

  /api/v2/{type}/runs:
    post:
      summary: Full forecast-run query. Returns *all* model runs for each time, filtered by run-time window and optional horizon.
      operationId: requestRuns
      parameters:
        - $ref: "#/components/parameters/ModelType"
        - $ref: "#/components/parameters/AcceptHeader"
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ForecastHistoryRequest"
            examples:
              ForecastRunsExample:
                summary: Windowed runs with horizon limits
                value:
                  forecastedFrom: "2024-09-10T00:00:00Z"
                  forecastedUntil: "2024-09-10T03:00:00Z"
                  minHorizon: 1
                  maxHorizon: 3
                  coordinates:
                    - lat: 47.60
                      lon: -122.33
                  variables:
                    - name: TMP
                      level: "2 m above ground"
                      alias: temp
      responses:
        "200":
          description: Successful response
          content:
            text/csv:
              schema:
                type: string
            application/json:
              schema:
                $ref: "#/components/schemas/ForecastArray"
            application/ndjson:
              schema:
                type: string
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
        "500":
          description: Server error

  /api/v2/{type}/forecasts:
    post:
      summary: Legacy alias of /runs. Returns *all* model runs for each time, filtered by run-time window and optional horizon.
      deprecated: true
      operationId: requestForecastHistory
      parameters:
        - $ref: "#/components/parameters/ModelType"
        - $ref: "#/components/parameters/AcceptHeader"
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ForecastHistoryRequest"
            examples:
              ForecastRunsExample:
                summary: Windowed runs with horizon limits
                value:
                  forecastedFrom: "2024-09-10T00:00:00Z"
                  forecastedUntil: "2024-09-10T03:00:00Z"
                  minHorizon: 1
                  maxHorizon: 3
                  coordinates:
                    - lat: 47.60
                      lon: -122.33
                  variables:
                    - name: TMP
                      level: "2 m above ground"
                      alias: temp
      responses:
        "200":
          description: Successful response
          content:
            text/csv:
              schema:
                type: string
            application/json:
              schema:
                $ref: "#/components/schemas/ForecastArray"
            application/ndjson:
              schema:
                type: string
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
        "500":
          description: Server error

components:
  parameters:
    ModelType:
      name: type
      in: path
      required: true
      description: Weather model code
      schema:
        type: string
        enum:
          [hrrr, rap, nbm, gfs, graphcast,
           gefsatmosmean, gefsatmos, gefschem, gefswaveprob,
           cfsipvf, cfsocnf, cfsflxf, cfspgbf, ifsoper, ifswave, ifsenfo, ifswaef, aifsoper, aifsenfo, refsprslev, rrfsprslev]
    AcceptHeader:
      name: Accept
      in: header
      required: false
      description: Desired response format (defaults to text/csv)
      schema:
        type: string
        enum: ["text/csv", "application/json", "application/ndjson"]
        default: text/csv

  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: API-Token
      description: "API token in Authorization header, e.g. **Authorization: Bearer YOUR_TOKEN**"

  schemas:
    # primitive reusable pieces
    Coordinate:
      type: object
      required: [lat, lon]
      properties:
        lat:
          type: number
          format: float
          description: Latitude
        lon:
          type: number
          format: float
          description: Longitude (-180 … 180)
        name:
          type: string
          description: Optional human label or identifier

    Grid:
      type: object
      required:
        [minLatitude, maxLatitude, minLongitude, maxLongitude, step]
      properties:
        minLatitude:
          type: number
          format: float
        maxLatitude:
          type: number
          format: float
        minLongitude:
          type: number
          format: float
        maxLongitude:
          type: number
          format: float
        step:
          type: number
          format: float
          description: Grid spacing in degrees

    Variable:
      type: object
      required: [name, level]
      properties:
        name:
          type: string
          description: GRIB short name (e.g. TMP, UGRD, VGRD)
        level:
          type: string
          description: Level description exactly as listed in the model metadata
        info:
          type: string
          description: Optional extra level qualifier (blank if none)
        alias:
          type: string
          description: Optional short column name used in responses

    Expression:
      type: object
      required: [expression]
      properties:
        expression:
          type: string
          description: |
            Expr-language formula  
            *(See full docs: <https://gribstream.com/expressions>)*
        alias:
          type: string
          description: Column name for the expression result (omit for boolean filters)

    Filter:
      type: object
      required: [expression]
      properties:
        expression:
          type: string
          description: Expr-language boolean returning **true** to keep the row

    # request payloads
    HistoryRequest:
      type: object
      description: |
        Historical “best forecast” query.
        Provide either **coordinates** or **grid** (at least one of them) plus **variables**.
      properties:
        # time selection
        fromTime:
          type: string
          format: date-time
        untilTime:
          type: string
          format: date-time
        timesList:
          type: array
          items:
            type: string
            format: date-time
        # time-travel + horizon
        asOf:
          type: string
          format: date-time
          description: Exclude model runs made after this time
        minHorizon:
          type: integer
          description: Minimum hours ahead (inclusive)
        maxHorizon:
          type: integer
          description: Maximum hours ahead (inclusive)
        # locations
        coordinates:
          type: array
          items:
            $ref: "#/components/schemas/Coordinate"
        grid:
          $ref: "#/components/schemas/Grid"
        # data selection
        variables:
          type: array
          items:
            $ref: "#/components/schemas/Variable"
        # derived/calculated
        expressions:
          type: array
          items:
            $ref: "#/components/schemas/Expression"
        filter:
          $ref: "#/components/schemas/Filter"
      required: [variables]

    ForecastHistoryRequest:
      type: object
      description: |
        Full forecast history query — same structure as HistoryRequest but
        time window is expressed in *forecast run time*.
      properties:
        forecastedFrom:
          type: string
          format: date-time
        forecastedUntil:
          type: string
          format: date-time
        timesList:
          type: array
          items:
            type: string
            format: date-time
        minHorizon:
          type: integer
        maxHorizon:
          type: integer
        coordinates:
          type: array
          items:
            $ref: "#/components/schemas/Coordinate"
        grid:
          $ref: "#/components/schemas/Grid"
        variables:
          type: array
          items:
            $ref: "#/components/schemas/Variable"
        expressions:
          type: array
          items:
            $ref: "#/components/schemas/Expression"
        filter:
          $ref: "#/components/schemas/Filter"
      required: [variables]

    # generic success payload for JSON (array of rows)
    ForecastArray:
      type: array
      items:
        type: object
        required: [forecasted_at, forecasted_time, lat, lon]
        properties:
          forecasted_at:
            type: string
            format: date-time
          forecasted_time:
            type: string
            format: date-time
          lat:
            type: number
          lon:
            type: number
          name:
            type: string
        additionalProperties:
          type: number
          description: Weather or derived value columns keyed by alias / variable spec
