openapi: "3.0.3"
info:
  title: "Aquaplot API"
  version: "1.0"
  description: "Aquaplot lets you compute distances and routes for ships. The API allows you to retrieve distances between any two coordinates in water and to search a public locations database.\n\nBase URL: https://api.aquaplot.com/v1 (HTTPS only).\n\nAuthentication:\n- Preferred: API token via Authorization: Bearer <token> (recommended).\n\nSelf-created API keys are limited to 10 requests in any rolling 60-second window. When an API key supplies a user ID, that user is limited to 1,000 token weight per UTC calendar month. The request that crosses 1,000 is admitted; later requests in that month are rejected."
servers:
  - url: "https://api.aquaplot.com/v1"
tags:
  - name: "Locations"
    description: "Port and location catalog endpoints."
  - name: "Geocode"
    description: "Validation and geocode utilities."
  - name: "Routing"
    description: "Route and distance calculations."
security:
  - bearerAuth: []
paths:
  /locations/{name}:
    get:
      tags:
        - "Locations"
      summary: "Search for locations"
      description: "Searches for locations by name or UN/LOCODE. Any match that has the search query as substring will be returned. The search is case-insensitive. Search results are limited to max. 20 results."
      parameters:
        - name: "name"
          in: "path"
          required: true
          schema:
            type: "string"
            minLength: 2
            maxLength: 32
          description: "Substring of location name or UN/LOCODE."
      responses:
        200:
          description: "Search results."
          content:
            application/json:
              schema:
                type: "array"
                items:
                  $ref: "#/components/schemas/Location"
        401:
          $ref: "#/components/responses/Unauthorized"
        429:
          $ref: "#/components/responses/TooManyRequests"
        500:
          $ref: "#/components/responses/ServerError"
        503:
          $ref: "#/components/responses/ServiceUnavailable"
  /validate:
    post:
      tags:
        - "Geocode"
      summary: "Validation of multiple coordinates"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ValidationRequestBody"
      responses:
        200:
          description: "Validation results."
          content:
            application/json:
              schema:
                type: "array"
                items:
                  $ref: "#/components/schemas/ValidationResult"
        400:
          $ref: "#/components/responses/BadRequest"
        401:
          $ref: "#/components/responses/Unauthorized"
        429:
          $ref: "#/components/responses/TooManyRequests"
        500:
          $ref: "#/components/responses/ServerError"
        503:
          $ref: "#/components/responses/ServiceUnavailable"
  /validate/{lng}/{lat}:
    get:
      tags:
        - "Geocode"
      summary: "Validation of coordinates"
      parameters:
        - name: "lng"
          in: "path"
          required: true
          schema:
            type: "number"
          description: "Longitude in decimal degrees."
        - name: "lat"
          in: "path"
          required: true
          schema:
            type: "number"
          description: "Latitude in decimal degrees."
      responses:
        200:
          description: "Validation result."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ValidationResult"
        400:
          $ref: "#/components/responses/BadRequest"
        401:
          $ref: "#/components/responses/Unauthorized"
        429:
          $ref: "#/components/responses/TooManyRequests"
        500:
          $ref: "#/components/responses/ServerError"
        503:
          $ref: "#/components/responses/ServiceUnavailable"
  /route/from/{from_lng}/{from_lat}/to/{to_lng}/{to_lat}:
    get:
      tags:
        - "Routing"
      summary: "Calculating single route"
      parameters:
        - name: "from_lng"
          in: "path"
          required: true
          schema:
            type: "number"
          description: "Longitude of the departure point."
        - name: "from_lat"
          in: "path"
          required: true
          schema:
            type: "number"
          description: "Latitude of the departure point."
        - name: "to_lng"
          in: "path"
          required: true
          schema:
            type: "number"
          description: "Longitude of the destination point."
        - name: "to_lat"
          in: "path"
          required: true
          schema:
            type: "number"
          description: "Latitude of the destination point."
        - name: "suez"
          in: "query"
          required: false
          schema:
            type: "string"
            enum:
              - "true"
              - "false"
            default: "true"
          description: "Allow passage of the Suez canal."
        - name: "panama"
          in: "query"
          required: false
          schema:
            type: "string"
            enum:
              - "true"
              - "false"
            default: "true"
          description: "Allow passage of the Panama canal."
        - name: "kiel"
          in: "query"
          required: false
          schema:
            type: "string"
            enum:
              - "true"
              - "false"
            default: "true"
          description: "Allow passage of the Kiel canal."
        - name: "corinth"
          in: "query"
          required: false
          schema:
            type: "string"
            enum:
              - "true"
              - "false"
            default: "false"
          description: "Allow passage of the Corinth canal."
        - name: "gibraltar"
          in: "query"
          required: false
          schema:
            type: "string"
            enum:
              - "true"
              - "false"
            default: "true"
          description: "Allow passage of the Gibraltar strait."
        - name: "messina"
          in: "query"
          required: false
          schema:
            type: "string"
            enum:
              - "true"
              - "false"
            default: "true"
          description: "Allow passage of the Messina strait."
        - name: "singapore"
          in: "query"
          required: false
          schema:
            type: "string"
            enum:
              - "true"
              - "false"
            default: "true"
          description: "Allow passage of the Singapore strait."
        - name: "dover"
          in: "query"
          required: false
          schema:
            type: "string"
            enum:
              - "true"
              - "false"
            default: "true"
          description: "Allow passage of the Strait of Dover."
        - name: "magellan"
          in: "query"
          required: false
          schema:
            type: "string"
            enum:
              - "true"
              - "false"
            default: "true"
          description: "Allow passage of the Magellan strait."
        - name: "floridaStrait"
          in: "query"
          required: false
          schema:
            type: "string"
            enum:
              - "true"
              - "false"
            default: "true"
          description: "Allow passage of the Florida strait."
        - name: "bosphorus"
          in: "query"
          required: false
          schema:
            type: "string"
            enum:
              - "true"
              - "false"
            default: "true"
          description: "Allow passage of the Bosphorus."
        - name: "oresund"
          in: "query"
          required: false
          schema:
            type: "string"
            enum:
              - "true"
              - "false"
            default: "true"
          description: "Allow passage of the Øresund."
        - name: "sunda"
          in: "query"
          required: false
          schema:
            type: "string"
            enum:
              - "true"
              - "false"
            default: "true"
          description: "Allow passage of the Sunda Strait."
        - name: "torres"
          in: "query"
          required: false
          schema:
            type: "string"
            enum:
              - "true"
              - "false"
            default: "true"
          description: "Allow passage of the Torres Strait."
        - name: "malacca"
          in: "query"
          required: false
          schema:
            type: "string"
            enum:
              - "true"
              - "false"
            default: "true"
          description: "Allow passage of the Strait of Malacca."
        - name: "littleBelt"
          in: "query"
          required: false
          schema:
            type: "string"
            enum:
              - "true"
              - "false"
            default: "true"
          description: "Allow passage of the Little Belt."
        - name: "dardanelles"
          in: "query"
          required: false
          schema:
            type: "string"
            enum:
              - "true"
              - "false"
            default: "true"
          description: "Allow passage of the Dardanelles."
        - name: "northwest"
          in: "query"
          required: false
          schema:
            type: "string"
            enum:
              - "true"
              - "false"
            default: "false"
          description: "Allow passage of the Northwest Passage."
        - name: "northeast"
          in: "query"
          required: false
          schema:
            type: "string"
            enum:
              - "true"
              - "false"
            default: "false"
          description: "Allow passage of the Northeast Passage."
        - name: "eca"
          in: "query"
          required: false
          schema:
            type: "string"
            enum:
              - "ignore"
              - "minimize"
            default: "ignore"
          description: "How to treat ECA areas."
        - name: "hra"
          in: "query"
          required: false
          schema:
            type: "string"
            enum:
              - "ignore"
              - "minimize"
            default: "ignore"
          description: "How to treat HRA areas."
        - name: "jwc"
          in: "query"
          required: false
          schema:
            type: "string"
            enum:
              - "ignore"
              - "minimize"
            default: "ignore"
          description: "How to treat JWC areas."
        - name: "autovalidate"
          in: "query"
          required: false
          schema:
            type: "string"
            enum:
              - "true"
              - "false"
            default: "false"
          description: "Validate start and end points before routing."
      responses:
        200:
          description: "GeoJSON FeatureCollection with one feature."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GeoJsonFeatureCollection"
        400:
          $ref: "#/components/responses/BadRequest"
        401:
          $ref: "#/components/responses/Unauthorized"
        429:
          $ref: "#/components/responses/TooManyRequests"
        500:
          $ref: "#/components/responses/ServerError"
        503:
          $ref: "#/components/responses/ServiceUnavailable"
  /distance/from/{from_lng}/{from_lat}/to/{to_lng}/{to_lat}:
    get:
      tags:
        - "Routing"
      summary: "Calculating distance (alias)"
      description: "Alias of /route/from/{from_lng}/{from_lat}/to/{to_lng}/{to_lat}."
      parameters:
        - name: "from_lng"
          in: "path"
          required: true
          schema:
            type: "number"
          description: "Longitude of the departure point."
        - name: "from_lat"
          in: "path"
          required: true
          schema:
            type: "number"
          description: "Latitude of the departure point."
        - name: "to_lng"
          in: "path"
          required: true
          schema:
            type: "number"
          description: "Longitude of the destination point."
        - name: "to_lat"
          in: "path"
          required: true
          schema:
            type: "number"
          description: "Latitude of the destination point."
        - name: "suez"
          in: "query"
          required: false
          schema:
            type: "string"
            enum:
              - "true"
              - "false"
            default: "true"
          description: "Allow passage of the Suez canal."
        - name: "panama"
          in: "query"
          required: false
          schema:
            type: "string"
            enum:
              - "true"
              - "false"
            default: "true"
          description: "Allow passage of the Panama canal."
        - name: "kiel"
          in: "query"
          required: false
          schema:
            type: "string"
            enum:
              - "true"
              - "false"
            default: "true"
          description: "Allow passage of the Kiel canal."
        - name: "corinth"
          in: "query"
          required: false
          schema:
            type: "string"
            enum:
              - "true"
              - "false"
            default: "false"
          description: "Allow passage of the Corinth canal."
        - name: "gibraltar"
          in: "query"
          required: false
          schema:
            type: "string"
            enum:
              - "true"
              - "false"
            default: "true"
          description: "Allow passage of the Gibraltar strait."
        - name: "messina"
          in: "query"
          required: false
          schema:
            type: "string"
            enum:
              - "true"
              - "false"
            default: "true"
          description: "Allow passage of the Messina strait."
        - name: "singapore"
          in: "query"
          required: false
          schema:
            type: "string"
            enum:
              - "true"
              - "false"
            default: "true"
          description: "Allow passage of the Singapore strait."
        - name: "dover"
          in: "query"
          required: false
          schema:
            type: "string"
            enum:
              - "true"
              - "false"
            default: "true"
          description: "Allow passage of the Strait of Dover."
        - name: "magellan"
          in: "query"
          required: false
          schema:
            type: "string"
            enum:
              - "true"
              - "false"
            default: "true"
          description: "Allow passage of the Magellan strait."
        - name: "floridaStrait"
          in: "query"
          required: false
          schema:
            type: "string"
            enum:
              - "true"
              - "false"
            default: "true"
          description: "Allow passage of the Florida strait."
        - name: "bosphorus"
          in: "query"
          required: false
          schema:
            type: "string"
            enum:
              - "true"
              - "false"
            default: "true"
          description: "Allow passage of the Bosphorus."
        - name: "oresund"
          in: "query"
          required: false
          schema:
            type: "string"
            enum:
              - "true"
              - "false"
            default: "true"
          description: "Allow passage of the Øresund."
        - name: "sunda"
          in: "query"
          required: false
          schema:
            type: "string"
            enum:
              - "true"
              - "false"
            default: "true"
          description: "Allow passage of the Sunda Strait."
        - name: "torres"
          in: "query"
          required: false
          schema:
            type: "string"
            enum:
              - "true"
              - "false"
            default: "true"
          description: "Allow passage of the Torres Strait."
        - name: "malacca"
          in: "query"
          required: false
          schema:
            type: "string"
            enum:
              - "true"
              - "false"
            default: "true"
          description: "Allow passage of the Strait of Malacca."
        - name: "littleBelt"
          in: "query"
          required: false
          schema:
            type: "string"
            enum:
              - "true"
              - "false"
            default: "true"
          description: "Allow passage of the Little Belt."
        - name: "dardanelles"
          in: "query"
          required: false
          schema:
            type: "string"
            enum:
              - "true"
              - "false"
            default: "true"
          description: "Allow passage of the Dardanelles."
        - name: "northwest"
          in: "query"
          required: false
          schema:
            type: "string"
            enum:
              - "true"
              - "false"
            default: "false"
          description: "Allow passage of the Northwest Passage."
        - name: "northeast"
          in: "query"
          required: false
          schema:
            type: "string"
            enum:
              - "true"
              - "false"
            default: "false"
          description: "Allow passage of the Northeast Passage."
        - name: "eca"
          in: "query"
          required: false
          schema:
            type: "string"
            enum:
              - "ignore"
              - "minimize"
            default: "ignore"
          description: "How to treat ECA areas."
        - name: "hra"
          in: "query"
          required: false
          schema:
            type: "string"
            enum:
              - "ignore"
              - "minimize"
            default: "ignore"
          description: "How to treat HRA areas."
        - name: "jwc"
          in: "query"
          required: false
          schema:
            type: "string"
            enum:
              - "ignore"
              - "minimize"
            default: "ignore"
          description: "How to treat JWC areas."
        - name: "autovalidate"
          in: "query"
          required: false
          schema:
            type: "string"
            enum:
              - "true"
              - "false"
            default: "false"
          description: "Validate start and end points before routing."
      responses:
        200:
          description: "GeoJSON FeatureCollection with one feature."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GeoJsonFeatureCollection"
        400:
          $ref: "#/components/responses/BadRequest"
        401:
          $ref: "#/components/responses/Unauthorized"
        429:
          $ref: "#/components/responses/TooManyRequests"
        500:
          $ref: "#/components/responses/ServerError"
        503:
          $ref: "#/components/responses/ServiceUnavailable"
  /routes:
    post:
      tags:
        - "Routing"
      summary: "Calculating multiple routes"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RoutesRequestEnvelope"
      responses:
        200:
          description: "GeoJSON FeatureCollection with multiple features."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GeoJsonFeatureCollection"
        400:
          $ref: "#/components/responses/BadRequest"
        401:
          $ref: "#/components/responses/Unauthorized"
        429:
          $ref: "#/components/responses/TooManyRequests"
        500:
          $ref: "#/components/responses/ServerError"
        503:
          $ref: "#/components/responses/ServiceUnavailable"
components:
  securitySchemes:
    bearerAuth:
      type: "http"
      scheme: "bearer"
      bearerFormat: "API key"
      description: "Preferred token auth using Authorization: Bearer <token>."
  responses:
    LocationList:
      description: "List of locations."
      content:
        application/json:
          schema:
            type: "array"
            items:
              $ref: "#/components/schemas/Location"
    Unauthorized:
      description: "Unauthorized. Provide valid credentials."
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
    BadRequest:
      description: "Invalid request parameters."
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
    TooManyRequests:
      description: "API-key request or monthly token-weight limit exceeded."
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
          examples:
            requestRateLimit:
              value:
                reason: "api_key_rate_limited"
            monthlyTokenWeight:
              value:
                reason: "monthly_token_weight_exceeded"
    ServerError:
      description: "Internal server error."
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
    ServiceUnavailable:
      description: "API-key rate limiting is temporarily unavailable."
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
          example:
            reason: "rate_limit_unavailable"
  schemas:
    LatLng:
      type: "object"
      required:
        - "lat"
        - "lng"
      properties:
        lat:
          type: "number"
        lng:
          type: "number"
    Location:
      type: "object"
      required:
        - "name"
        - "latlng"
        - "type"
      properties:
        name:
          type: "string"
        latlng:
          $ref: "#/components/schemas/LatLng"
        country:
          type: "string"
          nullable: true
        locode:
          type: "string"
          nullable: true
        type:
          type: "string"
    ValidationRequestBody:
      type: "object"
      required:
        - "requests"
      properties:
        requests:
          type: "array"
          minItems: 1
          maxItems: 100
          items:
            $ref: "#/components/schemas/LatLng"
    ValidationResult:
      type: "object"
      properties:
        status:
          type: "string"
        is_valid:
          type: "boolean"
        suggestion:
          type: "array"
          minItems: 2
          maxItems: 2
          items:
            type: "number"
        normalized:
          type: "array"
          minItems: 2
          maxItems: 2
          items:
            type: "number"
        moved_by:
          type: "number"
        validatedLatlng:
          $ref: "#/components/schemas/LatLng"
    CoordinatePair:
      type: "array"
      minItems: 2
      maxItems: 2
      items:
        type: "number"
    NogoBlock:
      type: "object"
      properties:
        coords:
          type: "array"
          minItems: 3
          items:
            $ref: "#/components/schemas/LatLng"
        prop:
          type: "object"
          additionalProperties: true
    RouteOptions:
      type: "object"
      properties:
        suez:
          type: "string"
          enum:
            - "true"
            - "false"
        panama:
          type: "string"
          enum:
            - "true"
            - "false"
        kiel:
          type: "string"
          enum:
            - "true"
            - "false"
        corinth:
          type: "string"
          enum:
            - "true"
            - "false"
        gibraltar:
          type: "string"
          enum:
            - "true"
            - "false"
        messina:
          type: "string"
          enum:
            - "true"
            - "false"
        singapore:
          type: "string"
          enum:
            - "true"
            - "false"
        dover:
          type: "string"
          enum:
            - "true"
            - "false"
        magellan:
          type: "string"
          enum:
            - "true"
            - "false"
        floridaStrait:
          type: "string"
          enum:
            - "true"
            - "false"
        bosphorus:
          type: "string"
          enum:
            - "true"
            - "false"
        oresund:
          type: "string"
          enum:
            - "true"
            - "false"
        sunda:
          type: "string"
          enum:
            - "true"
            - "false"
        torres:
          type: "string"
          enum:
            - "true"
            - "false"
        malacca:
          type: "string"
          enum:
            - "true"
            - "false"
        littleBelt:
          type: "string"
          enum:
            - "true"
            - "false"
        dardanelles:
          type: "string"
          enum:
            - "true"
            - "false"
        northeast:
          type: "string"
          enum:
            - "true"
            - "false"
        northwest:
          type: "string"
          enum:
            - "true"
            - "false"
        eca:
          type: "string"
          enum:
            - "ignore"
            - "minimize"
        hra:
          type: "string"
          enum:
            - "ignore"
            - "minimize"
        jwc:
          type: "string"
          enum:
            - "ignore"
            - "minimize"
        autovalidate:
          type: "string"
          enum:
            - "true"
            - "false"
        nogoBlocks:
          type: "array"
          items:
            $ref: "#/components/schemas/NogoBlock"
    RouteRequest:
      type: "object"
      required:
        - "from"
        - "to"
      properties:
        id:
          type: "string"
        averageVesselSpeedOverGround:
          type: "number"
          minimum: 1
          maximum: 100
        from:
          $ref: "#/components/schemas/LatLng"
        to:
          $ref: "#/components/schemas/LatLng"
        options:
          $ref: "#/components/schemas/RouteOptions"
    RoutesRequestEnvelope:
      type: "object"
      required:
        - "requests"
      properties:
        requests:
          type: "array"
          minItems: 1
          maxItems: 100
          items:
            $ref: "#/components/schemas/RouteRequest"
    GeoJsonGeometry:
      type: "object"
      required:
        - "type"
        - "coordinates"
      properties:
        type:
          type: "string"
          enum:
            - "LineString"
        coordinates:
          type: "array"
          items:
            $ref: "#/components/schemas/CoordinatePair"
    GeoJsonFeature:
      type: "object"
      required:
        - "type"
        - "properties"
        - "geometry"
      properties:
        type:
          type: "string"
          enum:
            - "Feature"
        properties:
          type: "object"
          additionalProperties: true
        geometry:
          $ref: "#/components/schemas/GeoJsonGeometry"
    GeoJsonFeatureCollection:
      type: "object"
      required:
        - "type"
        - "features"
      properties:
        type:
          type: "string"
          enum:
            - "FeatureCollection"
        status:
          type: "string"
        features:
          type: "array"
          items:
            $ref: "#/components/schemas/GeoJsonFeature"
        errors:
          type: "array"
          items:
            type: "object"
            additionalProperties: true
    ErrorResponse:
      type: "object"
      properties:
        status:
          type: "string"
        reason:
          type: "string"
