openapi: "3.1.0"
info:
  title: Georavity API
  version: "2.1.0"
  description: |
    Sovereign geospatial intelligence platform. Routing, geocoding, isochrones,
    distance matrices, map matching, POI search, and elevation data — all
    through a single API.

    ## Authentication
    Every request requires an API key via one of:
    - Header: `X-API-Key: gr_xxx`
    - Header: `Authorization: Bearer gr_xxx`
    - Query: `?api_key=gr_xxx`

    Get your free key at [georavity.com/register](https://georavity.com/register).

    ## Permission Categories
    API keys can be scoped to specific capabilities:
    - `routing` — Route calculation, optimization, isochrones, matrix, map matching
    - `geocoding` — Forward/reverse geocoding, autocomplete
    - `places` — POI search, categories, coverage
    - `all` — Full access (default for new keys)
  contact:
    name: Georavity
    url: https://georavity.com
    email: hello@georavity.com
  license:
    name: Proprietary
    url: https://georavity.com/terms

servers:
  - url: https://api.georavity.com
    description: Production

security:
  - ApiKeyHeader: []
  - ApiKeyQuery: []
  - BearerAuth: []

components:
  securitySchemes:
    ApiKeyHeader:
      type: apiKey
      in: header
      name: X-API-Key
    ApiKeyQuery:
      type: apiKey
      in: query
      name: api_key
    BearerAuth:
      type: http
      scheme: bearer

  schemas:
    Location:
      type: object
      required: [lat, lon]
      properties:
        lat:
          type: number
          format: double
          example: 52.52
        lon:
          type: number
          format: double
          example: 13.405

    RouteRequest:
      type: object
      required: [locations, costing]
      properties:
        locations:
          type: array
          items:
            $ref: "#/components/schemas/Location"
          minItems: 2
        costing:
          type: string
          enum: [auto, bicycle, pedestrian, truck, bus, taxi, motor_scooter, motorcycle]
          example: auto
        directions_options:
          type: object
          properties:
            units:
              type: string
              enum: [km, mi]
              default: km
            language:
              type: string
              default: en-US
        alternates:
          type: integer
          minimum: 0
          maximum: 2
          description: Number of alternate routes (max 2)
        costing_options:
          type: object
          description: |
            Mode-specific options. Use the key matching your costing mode.
            Example: `{"truck": {"height": 4.1, "weight": 21.77}}`
          properties:
            auto:
              $ref: "#/components/schemas/AutoCostingOptions"
            truck:
              $ref: "#/components/schemas/TruckCostingOptions"
            bicycle:
              $ref: "#/components/schemas/BicycleCostingOptions"
            pedestrian:
              $ref: "#/components/schemas/PedestrianCostingOptions"
        date_time:
          type: object
          description: Time-dependent routing (traffic-aware when data is available)
          properties:
            type:
              type: integer
              enum: [0, 1, 2]
              description: "0=current, 1=depart_at, 2=arrive_by"
            value:
              type: string
              description: ISO 8601 datetime (e.g., 2026-03-12T08:00)

    IsochroneRequest:
      type: object
      required: [locations, costing, contours]
      properties:
        locations:
          type: array
          items:
            $ref: "#/components/schemas/Location"
          minItems: 1
          maxItems: 1
        costing:
          type: string
          enum: [auto, bicycle, pedestrian, truck, bus, taxi, motor_scooter, motorcycle]
        contours:
          type: array
          items:
            type: object
            properties:
              time:
                type: number
                description: Minutes (max 120)
              distance:
                type: number
                description: Kilometers (max 200)
              color:
                type: string
                description: Hex color for the contour polygon
          maxItems: 4
        polygons:
          type: boolean
          default: true
          description: Return polygons (true) or linestrings (false)
        denoise:
          type: number
          minimum: 0
          maximum: 1
          default: 1
          description: Smoothing factor (0=no smoothing, 1=max)
        generalize:
          type: number
          description: Generalization tolerance in meters
        costing_options:
          type: object
          description: Mode-specific options (same format as RouteRequest)
          properties:
            auto:
              $ref: "#/components/schemas/AutoCostingOptions"
            truck:
              $ref: "#/components/schemas/TruckCostingOptions"
            bicycle:
              $ref: "#/components/schemas/BicycleCostingOptions"
            pedestrian:
              $ref: "#/components/schemas/PedestrianCostingOptions"

    MatrixRequest:
      type: object
      required: [sources, targets, costing]
      properties:
        sources:
          type: array
          items:
            $ref: "#/components/schemas/Location"
        targets:
          type: array
          items:
            $ref: "#/components/schemas/Location"
        costing:
          type: string
          enum: [auto, bicycle, pedestrian, truck, bus, taxi, motor_scooter, motorcycle]
        costing_options:
          type: object
          description: Mode-specific options (same format as RouteRequest)
          properties:
            auto:
              $ref: "#/components/schemas/AutoCostingOptions"
            truck:
              $ref: "#/components/schemas/TruckCostingOptions"
            bicycle:
              $ref: "#/components/schemas/BicycleCostingOptions"
            pedestrian:
              $ref: "#/components/schemas/PedestrianCostingOptions"

    MapMatchRequest:
      type: object
      required: [shape, costing]
      properties:
        shape:
          type: array
          items:
            $ref: "#/components/schemas/Location"
          description: GPS trace points (max 16,000)
        costing:
          type: string
          enum: [auto, bicycle, pedestrian, truck]
        shape_match:
          type: string
          enum: [edge_walk, map_snap, walk_or_snap]
          default: walk_or_snap
          description: Matching algorithm
        gps_accuracy:
          type: number
          default: 5.0
          description: GPS accuracy in meters
        search_radius:
          type: number
          default: 50
          description: Search radius in meters

    TraceAttributesRequest:
      type: object
      required: [shape, costing]
      properties:
        shape:
          type: array
          items:
            $ref: "#/components/schemas/Location"
        costing:
          type: string
          enum: [auto, bicycle, pedestrian, truck]
        filters:
          type: object
          properties:
            attributes:
              type: array
              items:
                type: string
                enum: [edge.names, edge.length, edge.speed, edge.road_class, edge.surface, edge.max_upward_grade, edge.max_downward_grade, edge.speed_limit]
            action:
              type: string
              enum: [include, exclude]
              default: include

    LocateRequest:
      type: object
      required: [locations]
      properties:
        locations:
          type: array
          items:
            $ref: "#/components/schemas/Location"
        costing:
          type: string
          enum: [auto, bicycle, pedestrian, truck]
          default: auto
        verbose:
          type: boolean
          default: false

    HeightRequest:
      type: object
      properties:
        shape:
          type: array
          items:
            $ref: "#/components/schemas/Location"
          description: Coordinate points (use shape OR encoded_polyline)
        encoded_polyline:
          type: string
          description: Encoded polyline (Polyline6 precision)
        range:
          type: boolean
          default: false
          description: Return cumulative distance along the shape

    ExpansionRequest:
      type: object
      required: [locations, costing, action]
      properties:
        locations:
          type: array
          items:
            $ref: "#/components/schemas/Location"
          minItems: 1
        costing:
          type: string
          enum: [auto, bicycle, pedestrian, truck]
        action:
          type: string
          enum: [isochrone, routing]
          description: Expansion visualization mode
        skip_opposites:
          type: boolean
          default: false
        expansion_properties:
          type: array
          items:
            type: string
            enum: [costs, durations, distances, statuses, edge_ids]

    CentroidRequest:
      type: object
      required: [locations]
      properties:
        locations:
          type: array
          items:
            $ref: "#/components/schemas/Location"
          minItems: 2
          maxItems: 5

    # ═══ COSTING OPTIONS ═══

    AutoCostingOptions:
      type: object
      description: |
        Options for car routing. Also applies to `taxi` and `bus` modes.
      properties:
        use_highways:
          type: number
          minimum: 0
          maximum: 1
          default: 1.0
          description: Preference for highways (0=avoid, 1=prefer)
        use_tolls:
          type: number
          minimum: 0
          maximum: 1
          default: 0.5
          description: Willingness to use toll roads (0=avoid, 1=prefer)
        use_ferry:
          type: number
          minimum: 0
          maximum: 1
          default: 0.5
          description: Willingness to use ferries (0=avoid, 1=prefer)
        top_speed:
          type: integer
          minimum: 10
          maximum: 252
          default: 140
          description: Maximum speed in km/h (caps routing speed)
        use_living_streets:
          type: number
          minimum: 0
          maximum: 1
          default: 0
          description: Willingness to use living streets

    TruckCostingOptions:
      type: object
      description: |
        Options for commercial vehicle routing with physical restrictions.
        Critical for fleet logistics — ensures routes avoid low bridges,
        weight-restricted roads, and hazmat-prohibited zones.
      properties:
        height:
          type: number
          example: 4.11
          description: Vehicle height in meters
        width:
          type: number
          example: 2.6
          description: Vehicle width in meters
        length:
          type: number
          example: 21.64
          description: Vehicle length in meters
        weight:
          type: number
          example: 21.77
          description: Vehicle weight in metric tons
        axle_load:
          type: number
          example: 9.07
          description: Axle load in metric tons
        axle_count:
          type: integer
          default: 5
          description: Number of axles
        hazmat:
          type: boolean
          default: false
          description: Vehicle carries hazardous materials
        use_highways:
          type: number
          minimum: 0
          maximum: 1
          default: 1.0
        use_tolls:
          type: number
          minimum: 0
          maximum: 1
          default: 0.5
        top_speed:
          type: integer
          minimum: 10
          maximum: 120
          default: 90
          description: Maximum truck speed in km/h

    BicycleCostingOptions:
      type: object
      description: Options for bicycle routing.
      properties:
        bicycle_type:
          type: string
          enum: [Road, Hybrid, City, Cross, Mountain]
          default: Hybrid
          description: Type of bicycle affects speed and surface preferences
        cycling_speed:
          type: number
          minimum: 5
          maximum: 60
          default: 20
          description: Average cycling speed in km/h
        use_roads:
          type: number
          minimum: 0
          maximum: 1
          default: 0.5
          description: Willingness to ride on roads (0=avoid, 1=prefer)
        use_hills:
          type: number
          minimum: 0
          maximum: 1
          default: 0.5
          description: Willingness to climb hills (0=avoid, 1=prefer)
        avoid_bad_surfaces:
          type: number
          minimum: 0
          maximum: 1
          default: 0.25
          description: Avoidance of gravel, cobblestones, etc.

    PedestrianCostingOptions:
      type: object
      description: Options for walking routing.
      properties:
        walking_speed:
          type: number
          minimum: 0.5
          maximum: 25
          default: 5.1
          description: Walking speed in km/h
        walkway_factor:
          type: number
          minimum: 0
          maximum: 10
          default: 1.0
          description: Preference for dedicated walkways
        sidewalk_factor:
          type: number
          minimum: 0
          maximum: 10
          default: 1.0
          description: Preference for roads with sidewalks
        max_hiking_difficulty:
          type: integer
          minimum: 0
          maximum: 6
          default: 1
          description: SAC hiking scale (0=easy walk, 6=extreme alpine)

    ErrorResponse:
      type: object
      properties:
        error:
          type: string
        code:
          type: string
        details:
          type: string

    HealthResponse:
      type: object
      properties:
        status:
          type: string
          example: healthy
        service:
          type: string
          example: georavity-gateway
        version:
          type: string
          example: "2.1.0"
        uptime:
          type: string

    NearbyPOIResponse:
      type: object
      properties:
        results:
          type: array
          items:
            type: object
            properties:
              id:
                type: integer
              name:
                type: string
                example: "Starbucks"
              category:
                type: string
                example: "coffee_shop"
              subcategory:
                type: string
              brand_name:
                type: string
              phone:
                type: string
              website:
                type: string
              distance_m:
                type: number
                example: 142.5
              lat:
                type: number
              lon:
                type: number
              confidence:
                type: number
              data_source:
                type: string
                example: "overture"
        count:
          type: integer
        center:
          $ref: "#/components/schemas/Location"
        radius_m:
          type: integer

  parameters:
    GeocodingText:
      name: text
      in: query
      required: true
      schema:
        type: string
      example: "Berlin, Germany"
    ReversePointLat:
      name: point.lat
      in: query
      required: true
      schema:
        type: number
        format: double
      example: 52.52
    ReversePointLon:
      name: point.lon
      in: query
      required: true
      schema:
        type: number
        format: double
      example: 13.405

  headers:
    X-RateLimit-Limit:
      description: Maximum requests per window
      schema:
        type: integer
    X-RateLimit-Remaining:
      description: Remaining requests in current window
      schema:
        type: integer
    X-RateLimit-Reset:
      description: Seconds until window resets
      schema:
        type: integer
    X-Quota-Limit:
      description: Monthly quota limit
      schema:
        type: string
    X-Quota-Used:
      description: Requests used this month
      schema:
        type: string
    X-Quota-Remaining:
      description: Remaining monthly quota
      schema:
        type: string

paths:
  # ═══ ROUTING ═══
  /v1/route:
    post:
      tags: [Routing]
      summary: Calculate route
      description: Turn-by-turn routing between 2+ locations with maneuver instructions.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RouteRequest"
      responses:
        "200":
          description: Route calculated successfully
        "400":
          description: Invalid request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Missing or invalid API key
        "429":
          description: Rate limited or quota exceeded

  /v1/optimized_route:
    post:
      tags: [Routing]
      summary: Optimized route (TSP)
      description: |
        Traveling salesman — finds the optimal ordering of waypoints to minimize
        total travel time. Supports 2-20 locations depending on costing model.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RouteRequest"
      responses:
        "200":
          description: Optimized route calculated

  /v1/isochrone:
    post:
      tags: [Routing]
      summary: Isochrone areas
      description: |
        Calculate reachability areas from a point within time or distance
        contours. Returns GeoJSON polygons showing where you can reach
        within N minutes or N kilometers. Supports up to 4 contours.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/IsochroneRequest"
      responses:
        "200":
          description: GeoJSON isochrone polygons

  /v1/matrix:
    post:
      tags: [Routing]
      summary: Distance/time matrix
      description: |
        Many-to-many time and distance matrix between sources and targets.
        Returns a matrix of travel times and distances. Supports up to
        2,500 source-target pairs.

        Alias: `/v1/sources_to_targets` (same functionality).
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MatrixRequest"
      responses:
        "200":
          description: Matrix calculated

  /v1/sources_to_targets:
    post:
      tags: [Routing]
      summary: Distance/time matrix (alias)
      description: Alias for `/v1/matrix`. Both endpoints have identical behavior.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MatrixRequest"
      responses:
        "200":
          description: Matrix calculated

  /v1/trace_route:
    post:
      tags: [Routing]
      summary: Map matching
      description: |
        Snap a GPS trace to the road network and return the matched route
        with turn-by-turn instructions. Supports up to 16,000 shape points.
        Uses Hidden Markov Model for accurate matching.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MapMatchRequest"
      responses:
        "200":
          description: Matched route with maneuver instructions

  /v1/trace_attributes:
    post:
      tags: [Routing]
      summary: Trace attributes
      description: |
        Get road attributes (names, speed limits, surface type, grade) along
        a GPS trace. Useful for fleet analytics and road condition analysis.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TraceAttributesRequest"
      responses:
        "200":
          description: Road attributes along the trace

  /v1/locate:
    post:
      tags: [Routing]
      summary: Locate on network
      description: Find the nearest road segment to given coordinates. Returns edge information.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/LocateRequest"
      responses:
        "200":
          description: Nearest edge(s) found

  /v1/height:
    post:
      tags: [Routing]
      summary: Elevation profile
      description: |
        Get elevation data for coordinates or along an encoded polyline.
        Returns height in meters above sea level. Optionally includes
        cumulative distance for elevation profile charts.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/HeightRequest"
      responses:
        "200":
          description: Elevation data

  /v1/expansion:
    post:
      tags: [Routing]
      summary: Algorithm expansion
      description: |
        Visualize the routing algorithm's search expansion. Returns the edges
        explored during route calculation — useful for debugging and analysis.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ExpansionRequest"
      responses:
        "200":
          description: GeoJSON expansion data

  /v1/centroid:
    post:
      tags: [Routing]
      summary: Centroid
      description: |
        Calculate the optimal meeting point for 2-5 locations. Finds the
        geographic centroid weighted by road network travel time.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CentroidRequest"
      responses:
        "200":
          description: Centroid location

  # ═══ GEOCODING ═══
  /v1/geocode:
    get:
      tags: [Geocoding]
      summary: Forward geocode
      description: Convert a text address or place name to geographic coordinates.
      parameters:
        - $ref: "#/components/parameters/GeocodingText"
        - name: size
          in: query
          schema:
            type: integer
            default: 10
          description: Maximum number of results
      responses:
        "200":
          description: Geocoding results
        "401":
          description: Missing or invalid API key

  /v1/reverse:
    get:
      tags: [Geocoding]
      summary: Reverse geocode
      description: Convert coordinates to a human-readable address.
      parameters:
        - $ref: "#/components/parameters/ReversePointLat"
        - $ref: "#/components/parameters/ReversePointLon"
      responses:
        "200":
          description: Reverse geocoding result

  /v1/autocomplete:
    get:
      tags: [Geocoding]
      summary: Autocomplete
      description: Get search suggestions as the user types. Optimized for low-latency.
      parameters:
        - $ref: "#/components/parameters/GeocodingText"
      responses:
        "200":
          description: Autocomplete suggestions

  # ═══ PLACES (POI) ═══
  /v1/pois/nearby:
    get:
      tags: [Places]
      summary: Nearby POI search
      description: |
        Search for points of interest near a location. Powered by the
        Georavity Data Fusion Engine (Overture Maps + OpenStreetMap).
        Returns results sorted by distance.
      parameters:
        - name: lat
          in: query
          required: true
          schema:
            type: number
            format: double
            minimum: -90
            maximum: 90
          example: 52.52
        - name: lon
          in: query
          required: true
          schema:
            type: number
            format: double
            minimum: -180
            maximum: 180
          example: 13.405
        - name: radius
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 50000
            default: 1000
          description: Search radius in meters
        - name: category
          in: query
          schema:
            type: string
          description: Filter by Overture category (e.g., restaurant, hotel)
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 200
            default: 50
      responses:
        "200":
          description: Nearby POI results
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NearbyPOIResponse"
        "400":
          description: Missing or invalid parameters
        "401":
          description: Missing or invalid API key

  /v1/pois/categories:
    get:
      tags: [Places]
      summary: POI categories
      description: List available POI categories with counts. Optionally filter by country.
      parameters:
        - name: country
          in: query
          schema:
            type: string
          description: ISO 3166-1 alpha-2 country code
      responses:
        "200":
          description: Category statistics

  /v1/pois/coverage:
    get:
      tags: [Places]
      summary: POI coverage
      description: Country-level POI coverage summary showing data availability per region.
      responses:
        "200":
          description: Country-level coverage data

  # ═══ ADVANCED ═══
  /v1/export/gpx:
    post:
      tags: [Advanced]
      summary: Export route as GPX
      description: |
        Calculate a route and return it as a GPX 1.1 XML file.
        Includes waypoints, track segments, and maneuver instructions.
        Compatible with Garmin, Wahoo, Strava, and all GPS devices.

        **Google Maps does NOT offer this feature.**
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RouteRequest"
      responses:
        "200":
          description: GPX XML file
          content:
            application/gpx+xml:
              schema:
                type: string
                format: binary

  /v1/export/kml:
    post:
      tags: [Advanced]
      summary: Export route as KML
      description: |
        Calculate a route and return it as KML 2.2 XML file.
        Compatible with Google Earth and GIS applications.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RouteRequest"
      responses:
        "200":
          description: KML XML file
          content:
            application/vnd.google-earth.kml+xml:
              schema:
                type: string
                format: binary

  /v1/speed_limit:
    get:
      tags: [Advanced]
      summary: Speed limit lookup
      description: |
        Get the speed limit for the nearest road at a given location.
        Returns road name, class, surface type, and posted speed limit.

        Equivalent to Google Roads API — **which costs $20/1000 requests.**
      parameters:
        - name: lat
          in: query
          required: true
          schema:
            type: number
            format: double
          example: 52.52
        - name: lon
          in: query
          required: true
          schema:
            type: number
            format: double
          example: 13.405
      responses:
        "200":
          description: Speed limit data
          content:
            application/json:
              schema:
                type: object
                properties:
                  speed_limit_kmh:
                    type: integer
                    description: Posted speed limit (null if unknown)
                  default_speed_kmh:
                    type: integer
                    description: Engine default speed for this road class
                  road_name:
                    type: string
                  road_class:
                    type: string
                    enum: [motorway, trunk, primary, secondary, tertiary, residential, service_other]
                  surface:
                    type: string

  /v1/elevation/profile:
    post:
      tags: [Advanced]
      summary: Elevation profile
      description: |
        Get chart-ready elevation profile data with cumulative distance,
        min/max elevation, total ascent, and total descent.
        Accepts same body as `/v1/height`.

        Equivalent to Google Elevation API — **which costs $5/1000 requests.**
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/HeightRequest"
      responses:
        "200":
          description: Chart-ready elevation profile
          content:
            application/json:
              schema:
                type: object
                properties:
                  profile:
                    type: array
                    items:
                      type: object
                      properties:
                        distance_km:
                          type: number
                        elevation_m:
                          type: number
                  min_elevation_m:
                    type: number
                  max_elevation_m:
                    type: number
                  total_ascent_m:
                    type: number
                  total_descent_m:
                    type: number
                  total_distance_km:
                    type: number
                  point_count:
                    type: integer

  # ═══ SYSTEM ═══
  /health:
    get:
      tags: [System]
      summary: Health check
      security: []
      responses:
        "200":
          description: Gateway is healthy
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HealthResponse"

  /v1/status:
    get:
      tags: [System]
      summary: Engine status
      security: []
      responses:
        "200":
          description: Engine status with version and uptime
