openapi: '3.0.3'
info:
  title: Ullr Public API
  version: '11.0'
  contact:
    name: API Support
    email: info@ullr.ski
    url: https://ullr.ski
  license:
    name: Ullr Terms of Service
    url: https://www.iubenda.com/terms-and-conditions/15075965
servers:
  - url: https://api.ullr.ski/public/v11

tags:
  - name: Areas (resorts)
    description: Information about areas (a.k.a. resorts)
  - name: Features
    description: Information about terrain park and trail features
  - name: Trails
    description: Information about trails
  - name: Lifts
    description: Information about lifts
  - name: Updates
    description: Updates and reports about areas
  - name: area_model
    x-displayName: Area Model (Resort)
    description: |
      <SchemaDefinition schemaRef="#/components/schemas/Area" />
  - name: feature_model
    x-displayName: Feature Model
    description: |
      <SchemaDefinition schemaRef="#/components/schemas/Feature" />
  - name: trail_model
    x-displayName: Trail Model
    description: |
      <SchemaDefinition schemaRef="#/components/schemas/Trail" />
  - name: lift_model
    x-displayName: Lift Model
    description: |
      <SchemaDefinition schemaRef="#/components/schemas/Lift" />
  - name: update_model
    x-displayName: Update Model
    description: |
      <SchemaDefinition schemaRef="#/components/schemas/Update" />
x-tagGroups:
  - name: API Endpoints
    tags:
      - Areas
      - Features
      - Trails
      - Lifts
      - Updates
  - name: Models
    tags:
      - area_model
      - feature_model
      - trail_model
      - lift_model
      - update_model

paths:
  /{sport}/areas/{areaId}:
    get: 
      operationId: getAreaById
      summary: Get area by ID
      description: Returns a single area by ID
      tags: 
        - Areas
      security:
        - ApiKeyAuth: []
      parameters:
        - name: sport
          in: path
          required: true
          schema:
            $ref: '#/components/schemas/UllrSport'
        - name: areaId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: A single area.
          content: 
            application/json:
              schema:
                $ref: '#/components/schemas/Area'
        '403':
          description: Unauthorized access.
          content:
            application/json:
              schema:
                type: string
                example: 'UNAUTHORIZED'

      
  /{sport}/areas/{areaId}/features:
    get:
      operationId: getTerrainParkFeatures
      summary: Get current features
      description: Returns features for a specific area and sport
      tags: 
        - Features
      security:
        - ApiKeyAuth: []
      parameters:
        - name: sport
          in: path
          required: true
          schema:
            $ref: '#/components/schemas/UllrSport'
        - name: areaId
          in: path
          required: true
          schema:
            type: string
        - name: status
          in: query
          required: false
          schema:
            $ref: '#/components/schemas/FeatureStatusEnum'
        - name: trailId
          in: query
          required: false
          schema:
            type: string
            format: uuid
            example: '123e4567-e89b-12d3-a456-426614174000'
      responses:
        '200':
          description: A list for all current features.
          content: 
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Feature'
        '403':
          description: Unauthorized access.
          content:
            application/json:
              schema:
                type: string
                example: 'UNAUTHORIZED'

  /{sport}/areas/{areaId}/features/{featureId}:
    get:
      operationId: getFeatureById
      summary: Get a single feature by ID
      description: Returns a single feature for a specific area, sport, and feature ID
      tags: 
        - Features
      security:
        - ApiKeyAuth: []
      parameters:
        - name: sport
          in: path
          required: true
          schema:
            $ref: '#/components/schemas/UllrSport'
        - name: areaId
          in: path
          required: true
          schema:
            type: string
        - name: featureId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            example: '4a38cb8b-cf4b-487f-8434-e89b531560d2'
      responses:
        '200':
          description: A single feature.
          content: 
            application/json:
              schema:
                $ref: '#/components/schemas/Feature'
        '403':
          description: Unauthorized access.
          content:
            application/json:
              schema:
                type: string
                example: 'UNAUTHORIZED'

  /{sport}/areas/{areaId}/features/history:
    get:
      operationId: getFeaturesHistory
      summary: Get historical features
      description: Returns features for a specific area and sport at a specified timestamp
      tags: 
        - Features
      security:
        - ApiKeyAuth: []
      parameters:
        - name: sport
          in: path
          required: true
          schema:
            $ref: '#/components/schemas/UllrSport'
        - name: areaId
          in: path
          required: true
          schema:
            type: string
        - name: timestamp
          in: query
          required: true
          schema:
            type: integer
            format: int
            example: 1723037333580  # Example timestamp in milliseconds
      responses:
        '200':
          description: A list for all features as they existed at the provided timestamp.
          content: 
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Feature'
        '403':
          description: Unauthorized access.
          content:
            application/json:
              schema:
                type: string
                example: 'UNAUTHORIZED'

  /{sport}/areas/{areaId}/trails:
    get: 
      operationId: getTrails
      summary: Get current trails
      description: Returns trails for a specific area
      tags: 
        - Trails
      security:
        - ApiKeyAuth: []
      parameters:
        - name: sport
          in: path
          required: true
          schema:
            $ref: '#/components/schemas/UllrSport'
        - name: areaId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: A list for all current trails.
          content: 
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Trail'
        '403':
          description: Unauthorized access.
          content:
            application/json:
              schema:
                type: string
                example: 'UNAUTHORIZED'

  /{sport}/areas/{areaId}/trails/{trailId}:
    get: 
      operationId: getTrailById
      summary: Get trail by ID
      description: Returns a single trail for a specific area by trail ID
      tags: 
        - Trails
      security:
        - ApiKeyAuth: []
      parameters:
        - name: sport
          in: path
          required: true
          schema:
            $ref: '#/components/schemas/UllrSport'
        - name: areaId
          in: path
          required: true
          schema:
            type: string
        - name: trailId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: A single trail.
          content: 
            application/json:
              schema:
                $ref: '#/components/schemas/Trail'
        '403':
          description: Unauthorized access.
          content:
            application/json:
              schema:
                type: string
                example: 'UNAUTHORIZED'

  /{sport}/areas/{areaId}/trails/history:
    get: 
      operationId: getTrailsHistory
      summary: Get trails history
      description: Returns trails for a specific area as they existed at the provided timestamp
      tags: 
        - Trails
      security:
        - ApiKeyAuth: []
      parameters:
        - name: sport
          in: path
          required: true
          schema:
            $ref: '#/components/schemas/UllrSport'
        - name: areaId
          in: path
          required: true
          schema:
            type: string
        - name: timestamp
          in: query
          required: true
          schema:
            type: integer
            format: int
            example: 1723037333580  # Example timestamp in milliseconds
      responses:
        '200':
          description: A list of trails as they existed at the provided timestamp.
          content: 
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Trail'
        '403':
          description: Unauthorized access.
          content:
            application/json:
              schema:
                type: string
                example: 'UNAUTHORIZED'
  
  /{sport}/areas/{areaId}/lifts:
    get:
      operationId: getLifts
      summary: Get current lifts
      description: Returns lifts for a specific area and sport
      tags:
        - Lifts
      security:
        - ApiKeyAuth: []
      parameters:
        - name: sport
          in: path
          required: true
          schema:
            $ref: '#/components/schemas/UllrSport'
        - name: areaId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: A list of lifts.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Lift'
        '403':
          description: Unauthorized access.
          content:
            application/json:
              schema:
                type: string
                example: 'UNAUTHORIZED'

  /{sport}/areas/{areaId}/lifts/{liftId}:
    get:
      operationId: getLiftById
      summary: Get lift by ID
      description: Returns a specific lift by its ID
      tags:
        - Lifts
      security:
        - ApiKeyAuth: []
      parameters:
        - name: sport
          in: path
          required: true
          schema:
            $ref: '#/components/schemas/UllrSport'
        - name: areaId
          in: path
          required: true
          schema:
            type: string
        - name: liftId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: A lift object.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Lift'
        '403':
          description: Unauthorized access.
          content:
            application/json:
              schema:
                type: string
                example: 'UNAUTHORIZED'

  /{sport}/areas/{areaId}/lifts/history:
    get:
      operationId: getLiftsHistory
      summary: Get lifts history
      description: Returns lifts for a specific area as they existed at the provided timestamp
      tags:
        - Lifts
      security:
        - ApiKeyAuth: []
      parameters:
        - name: sport
          in: path
          required: true
          schema:
            $ref: '#/components/schemas/UllrSport'
        - name: areaId
          in: path
          required: true
          schema:
            type: string
        - name: timestamp
          in: query
          required: true
          schema:
            type: integer
            format: int
            example: 1723037333580  # Example timestamp in milliseconds
      responses:
        '200':
          description: A list of lifts as they existed at the provided timestamp.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Lift'
        '403':
          description: Unauthorized access.
          content:
            application/json:
              schema:
                type: string
                example: 'UNAUTHORIZED'

  /{sport}/areas/{areaId}/updates:
    get:
      operationId: getUpdates
      summary: Get area updates
      description: Returns paginated updates (reports) for a specific area and sport
      tags:
        - Updates
      security:
        - ApiKeyAuth: []
      parameters:
        - name: sport
          in: path
          required: true
          schema:
            $ref: '#/components/schemas/UllrSport'
        - name: areaId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: A paginated list of updates.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Update'
                  hasMore:
                    type: boolean
                    description: Indicates if there are more results available
                  pageCursor:
                    type: string
                    description: Cursor for fetching the next page of results
        '403':
          description: Unauthorized access.
          content:
            application/json:
              schema:
                type: string
                example: 'UNAUTHORIZED'

components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header 
      name: api-key
      x-displayName: API Key
      description: |
        API keys are usually scoped to a single area (a.k.a. resort) and may have access to only a subset of the Public API endpoints. To get an API key, contact david@ullr.ski
        * Pass your API key in the `api-key` header. For example, if your API key is "abc-123", you would pass `api-key: abc-123` as a header.
  schemas:
    UllrSport:
      type: string
      enum:
        - SNOW
        - BIKE
    # Areas
    AreaSportSummary:
      type: object
      properties:
        featureCounts:
          type: object
          additionalProperties:
            type: integer
          description: A record of feature counts by type
        trailCounts:
          type: object
          additionalProperties:
            type: integer
          description: A record of trail counts by type
        averageRating:
          type: number
          format: float
          description: The average rating of the area
    Area:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier for the area
        name:
          type: string
          description: Name of the area
        latitude:
          type: number
          format: float
          description: Latitude of the area
        longitude:
          type: number
          format: float
          description: Longitude of the area
        sports:
          type: array
          items:
            $ref: '#/components/schemas/UllrSport'
          description: List of sports available at the area
        summaries:
          type: object
          properties:
            SNOW:
              $ref: '#/components/schemas/AreaSportSummary'
            BIKE:
              $ref: '#/components/schemas/AreaSportSummary'
            description: Summary information for each sport at the area
          description: Summary information for each sport at the area
        lastUpdatedTimestamp:
          type: integer
          format: int64
          description: Timestamp of the last update
        settings:
          type: object
          properties:
            defaultMapBearing:
              type: number
              format: float
              description: Default map bearing for the area
          description: Settings for the area
    # Features
    FeatureStatusEnum:
      type: integer
      enum:
        - -2
        - -1
        - 1
        - 2
      description: |
        -2: Archived (Feature has been removed from the hill and is only available for archival viewing)
        -1: Removed (Feature is no longer on the hill, but can still be edited and updated)
         1: Up & Open (Feature is on the hill and open for use)
         2: Up & Closed (Feature is on the hill, but closed for use)
    FeatureTypeEnumSnow:
      type: integer
      enum:
        - 0
        - 1
        - 2
        - 3
        - 4
      description: |
        0: Box
        1: Rail
        2: Jump
        3: Snow Feature (A snow feature that is not a jump. EX. half pipe, volcano, roller, etc.)
        4: Jib (A jib feature. This is usually a non-snow feature that can't be categorized as a box or rail. "Jib" can also act as a catch-all or "other" category because almost all park features could accurately be considered "jibs". EX. wall ride, bonk, etc.)
    FeatureTypeEnumBike:
      type: integer
      enum:
        - 0
        - 1
        - 2
        - 3
        - 4
        - 5
        - 6
        - 7
        - 8
        - 9
      description: |
        0: Rock garden
        1: Drop
        2: Jump
        3: Berm
        4: Rock
        5: Bridge
        6: Skinny
        7: Other
        8: Hip
        9: Roller
    FeatureTypeEnum:
      oneOf:
        - $ref: '#/components/schemas/FeatureTypeEnumSnow'
        - $ref: '#/components/schemas/FeatureTypeEnumBike'
      description: |
        This type can be either FeatureTypeEnumSnow or FeatureTypeEnumBike depending on the sport of the feature.
        - If the sport is "SNOW", the type will follow FeatureTypeEnumSnow.
        - If the sport is "BIKE", the type will follow FeatureTypeEnumBike.
    FeatureMediaUrl:
      type: object
      properties:
        type:
          type: string
          enum:
            - photo
          example: "photo"
        urls:
          type: object
          properties:
            320:
              type: string
              format: url
              example: "https://i.ullr.ski/feature-photos/320/example.jpg"
            640:
              type: string
              format: url
              example: "https://i.ullr.ski/feature-photos/640/example.jpg"
            1080:
              type: string
              format: url
              example: "https://i.ullr.ski/feature-photos/1080/example.jpg"
            original:
              type: string
              format: url
              example: "https://i.ullr.ski/feature-photos/original/example.jpg"
      required:
        - type
        - urls
    FeatureSizeEnum:
      type: string
      enum:
        - XS
        - S
        - M
        - L
        - XL
      description: |
        The "t-shirt" size of the feature. This is a general size category that can be used to describe the size of the feature.
    Feature:
      type: object
      properties:
        areaId:
          type: string
          example: "afb9c650-4682-11e7-8faa-910ccbe9204b"
        status:
          $ref: '#/components/schemas/FeatureStatusEnum'
          example: 1
        reportedTimestamp:
          type: integer
          example: 1711496330204
          description: Timestamp in milliseconds. The time at which the feature was last updated.
        sport:
          $ref: '#/components/schemas/UllrSport'
          example: "SNOW"
        upTimestamp:
          type: integer
          example: 1698235548851
          description: Timestamp in milliseconds. The time at which the feature was first reported as "up".
        userId:
          type: string
          example: "d2c46140-2ef5-11e7-9315-bb5c369b6ab5"
        longitude:
          type: number
          format: double
          example: -89.78030428385524
        mediaUrls:
          type: array
          items:
            $ref: '#/components/schemas/FeatureMediaUrl'
          nullable: true
        elevation:
          type: number
          format: double
          example: 258.7633056640625
          description: Elevation in meters
        id:
          type: string
          format: uuid
          example: "4a38cb8b-cf4b-487f-8434-e89b531560d2"
        latitude:
          type: number
          format: double
          example: 43.046245575809365
        downTimestamp:
          type: integer
          nullable: true
          example: null
          description: Timestamp in milliseconds. The most recent time at which the feature was removed. If null, the feature has not been removed.
        title:
          type: string
          example: "DFD Rail"
        type:
          $ref: '#/components/schemas/FeatureTypeEnum'
          example: 1
        terrainParkId:
          type: string
          format: uuid
          nullable: true
          example: "123e4567-e89b-12d3-a456-426614174000"
        terrainParkName:
          type: string
          nullable: true
          example: "Main Park"
        trailIds:
          type: array
          items:
            type: string
            format: uuid
          nullable: true
          example: ["d2c46140-2ef5-11e7-9315-bb5c369b6ab5", "4a38cb8b-cf4b-487f-8434-e89b531560d2"]
        size:
          $ref: '#/components/schemas/FeatureSizeEnum'
          example: "L"
        description:
          type: string
          nullable: true
          example: "A large DFD rail."
      required:
        - areaId
        - status
        - reportedTimestamp
        - sport
        - upTimestamp
        - userId
        - longitude
        - mediaUrls
        - elevation
        - id
        - latitude
        - title
        - type
    # Trails
    TrailStatusEnum:
      type: string
      enum:
        - OPEN
        - CLOSED
        - SCHEDULED
        - HOLD
        - NOT_REPORTED
        - ARCHIVED
      description: |
        OPEN: The trail is open for use.
        CLOSED: The trail is closed and not available for use.
        SCHEDULED: The trail is scheduled to open.
        HOLD: The trail is temporarily on hold.
        NOT_REPORTED: The status of the trail has not been reported.
        ARCHIVED: The trail has been archived and is no longer in use.
    TrailDifficultyEnumSnow:
      type: string
      enum:
        - GREEN_CIRCLE
        - BLUE_SQUARE
        - BLACK_DIAMOND
        - DOUBLE_BLACK_DIAMOND
        - FREESTYLE_TERRAIN
      description: |
        GREEN_CIRCLE: Green circle
        BLUE_SQUARE: Blue square
        BLACK_DIAMOND: Black diamond
        DOUBLE_BLACK_DIAMOND: Double black diamond
        FREESTYLE_TERRAIN: Freestyle terrain / terrain park.
    TrailDifficultyEnumBike:
      type: string
      enum:
        - ROAD
        - WHITE_CIRCLE
        - GREEN_CIRCLE
        - BLUE_SQUARE
        - BLACK_DIAMOND
        - DOUBLE_BLACK_DIAMOND
        - PRO
        - GREEN_CIRCLE_FREERIDE
        - BLUE_SQUARE_FREERIDE
        - BLACK_DIAMOND_FREERIDE
        - DOUBLE_BLACK_DIAMOND_FREERIDE
        - PRO_FREERIDE
      description: |
        ROAD: Road
        WHITE_CIRCLE: White circle
        GREEN_CIRCLE: Green circle
        BLUE_SQUARE: Blue square
        BLACK_DIAMOND: Black diamond
        DOUBLE_BLACK_DIAMOND: Double black diamond
        PRO: Pro
        GREEN_CIRCLE_FREERIDE: Green circle (freeride variant)
        BLUE_SQUARE_FREERIDE: Blue square (freeride variant)
        BLACK_DIAMOND_FREERIDE: Black diamond (freeride variant)
        DOUBLE_BLACK_DIAMOND_FREERIDE: Double black diamond (freeride variant)
        PRO_FREERIDE: Pro (freeride variant)
    TrailDifficultyEnum:
      oneOf: 
        -  $ref: "#/components/schemas/TrailDifficultyEnumSnow"
        - $ref: "#/components/schemas/TrailDifficultyEnumBike"
      description: |
        This type can be either TrailDifficultyEnumSnow or TrailDifficultyEnumBike depending on the `sport` of the trail.
        - If the sport is "SNOW", the type will follow TrailDifficultyEnumSnow.
        - If the sport is "BIKE", the type will follow TrailDifficultyEnumBike.
    TrailSurfaceConditionEnum:
      type: string
      enum:
        - GROOMED
        - NOT_GROOMED
        - NOT_REPORTED
      description: |
        GROOMED: The trail has been groomed.
        NOT_GROOMED: The trail has not been groomed.
        NOT_REPORTED: The surface condition of the trail has not been reported.
    Trail:
      type: object
      properties:
        id:
          type: string
          format: uuid
          example: "123e4567-e89b-12d3-a456-426614174000"
        areaId:
          type: string
          format: uuid
          example: "bf9612ad-6d84-45c5-a616-b99629661346"
        sport:
          $ref: '#/components/schemas/UllrSport'
          example: "SNOW"
        name:
          type: string
          example: "Main Trail"
        status:
          $ref: '#/components/schemas/TrailStatusEnum'
          example: "OPEN"
          description: The current status of the trail.
        reportedTimestamp:
          type: integer
          format: int64
          description: Timestamp in milliseconds. The time at which the trail was last updated.
          example: 1674048954000
        geojson:
          type: string
          nullable: true
          description: |
            A GeoJSON string representing the trail. This can be used to display the trail on a map.
          example: "{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"properties\":{},\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-89.78328824043274,43.043464613106295],[-89.78233873844147,43.04362535382605],[-89.78221535682678,43.04559733374386]]}}]}"
        difficulty:
          $ref: '#/components/schemas/TrailDifficultyEnum'
          nullable: true
          example: "Intermediate"
          description: The difficulty level of the trail.
        description:
          type: string
          nullable: true
          description: A description of the trail. Supports Markdown.
          example: "Terrain park trail. \n\n📹 [Preseason edit](https://www.youtube.com/watch?v=rDTykfG2l_o)"
        surfaceCondition:
          $ref: '#/components/schemas/TrailSurfaceConditionEnum'
          nullable: true
          example: "GROOMED"
          description: The surface condition of the trail.
      required:
        - id
        - areaId
        - sport
        - name
        - status
        - createdTimestamp
        - reportedTimestamp
    # Lifts
    LiftProperty:
      type: object
      properties:
        key:
          type: string
          description: The key of the property.
          example: "capacity"
        dataType:
          type: string
          enum:
            - string
            - number
            - boolean
            - select
          description: The data type of the property.
          example: "number"
        value:
          oneOf:
            - type: string
            - type: number
            - type: boolean
            - type: 'null'
          description: The value of the property.
          example: 1200
      required:
        - key
        - dataType
        - value

    Lift:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: The unique identifier for the lift.
          example: "123e4567-e89b-12d3-a456-426614174000"
        areaId:
          type: string
          format: uuid
          description: The unique identifier for the area.
          example: "bf9612ad-6d84-45c5-a616-b99629661346"
        sports:
          type: array
          items:
            $ref: '#/components/schemas/UllrSport'
          description: The sports that the lift supports.
          example: ["SNOW", "BIKE"]
        className:
          type: string
          description: The human-readable class name of the lift or lift component being returned. ex. "Lift", "Lift tower"
          example: "Lift"
        createdAt:
          type: integer
          format: int64
          description: Timestamp in milliseconds. The time at which the lift was created.
          example: 1674048954000
        updatedAt:
          type: integer
          format: int64
          description: Timestamp in milliseconds. The time at which the most recent update to the lift was made.
          example: 1674048954000
        timestamp:
          type: integer
          format: int64
          description: Timestamp in milliseconds. The time of the last lift update.
          example: 1674048954000
        geometry:
          type: string
          description: GeoJSON string representing the geometry of the lift.
          example: "{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"properties\":{},\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-89.78328824043274,43.043464613106295],[-89.78233873844147,43.04362535382605],[-89.78221535682678,43.04559733374386]]}}]}"
        parentId:
          type: string
          format: uuid
          description: Lits and lift components may be organized in a tree-like hierarchy. The unique identifier for the parent item.
          example: "123e4567-e89b-12d3-a456-426614174000"
          nullable: true
        properties:
          type: array
          items:
            $ref: '#/components/schemas/LiftProperty'
          nullable: true
          description: A list of properties associated with the lift. Usually will have properties for "name", "status", "capacity", and others.
      required:
        - id
        - areaId
        - name
        - status
        - createdAt
        - updatedAt
    # Updates
    Update:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: The unique identifier for the update.
          example: "123e4567-e89b-12d3-a456-426614174000"
        areaId:
          type: string
          format: uuid
          description: The unique identifier for the area.
          example: "bf9612ad-6d84-45c5-a616-b99629661346"
        sport:
          $ref: '#/components/schemas/UllrSport'
          example: "SNOW"
        userName:
          type: string
          description: The name of the user who submitted the update.
          example: "John Doe"
        featureId:
          type: string
          format: uuid
          nullable: true
          description: The unique identifier for the feature this update is about (if applicable).
          example: "4a38cb8b-cf4b-487f-8434-e89b531560d2"
        date:
          type: integer
          format: int64
          description: Timestamp in milliseconds. The time at which the update was created.
          example: 1674048954000
        mediaUrls:
          type: array
          items:
            type: string
            format: url
          description: Array of media URLs (legacy 640px image URLs).
          example: ["https://i.ullr.ski/report-photos/640/example.jpg"]
        reportText:
          type: string
          description: The text content of the update.
          example: "Conditions are excellent today with fresh powder!"
        stars:
          type: integer
          description: Star rating (1-5) given with this update.
          example: 5
        isOfficial:
          type: boolean
          description: Indicates if this is an official update from the resort.
          example: true
      required:
        - id
        - areaId
        - sport
        - userName
        - date
        - mediaUrls
        - reportText
        - stars
        - isOfficial