openapi: 3.0.1
info:
  title: Tour Commander API
  description: Part of Bettermile Route API responsible for tour management
  contact:
    email: route-api@bettermile.com
  version: 1.0.0
  license:
    name: Proprietary
    url: https://bettermile.com/legal-notice
servers:
  - url: https://dev.routes.bettermile.com
    description: Dev API
security:
  - ApiKeyAuth: []
tags:
  - name: Tour
    description: Operations on the tour level
  - name: Sequence
    description: Operations on the sequence level
  - name: Waypoint
    description: Operations on the waypoint level
  - name: Job
    description: Operations on the job level
  - name: Custom Job
    description: Operations on custom jobs
  - name: Custom Waypoint
    description: Operations on custom waypoints
paths:
  /v1/tours:
    post:
      tags:
        - Tour
      summary: Request creation of a new tour
      operationId: createTour
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateTourRequest'
      responses:
        '200':
          description: Tour has been successfully created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/TourCreated'
        '400':
          $ref: '#/components/responses/BadRequestError'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '500':
          $ref: '#/components/responses/ServerFailedError'
    get:
      tags:
        - Tour
      summary: >-
        Get tours for a given date, optionally narrowed down by a given
        assignment value
      operationId: getTours
      parameters:
        - name: date
          in: query
          required: true
          schema:
            type: string
            format: date
        - name: assignment
          in: query
          required: false
          schema:
            type: string
      responses:
        '200':
          description: List of tours matching the provided criteria
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/TourList'
        '400':
          $ref: '#/components/responses/BadRequestError'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '500':
          $ref: '#/components/responses/ServerFailedError'
  /v1/tours/{tourId}:
    get:
      tags:
        - Tour
      summary: Get tour by ID
      operationId: getTourById
      parameters:
        - $ref: '#/components/parameters/TourId'
      responses:
        '200':
          description: Full tour information, excluding the current sequence
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/Tour'
        '400':
          $ref: '#/components/responses/BadRequestError'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '404':
          $ref: '#/components/responses/NotFoundError'
        '500':
          $ref: '#/components/responses/ServerFailedError'
  /v1/tours/{tourId}/destination:
    put:
      tags:
        - Tour
      summary: Set tour destination
      operationId: setTourDestination
      parameters:
        - $ref: '#/components/parameters/TourId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SetTourDestinationRequest'
      responses:
        '200':
          description: Tour destination has been set successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    example: {}
        '400':
          $ref: '#/components/responses/BadRequestError'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '404':
          $ref: '#/components/responses/NotFoundError'
        '500':
          $ref: '#/components/responses/ServerFailedError'
  /v1/tours/{tourId}/potential-recalc-gain:
    get:
      tags:
        - Tour
      summary: Get potential recalculation gain for tour
      description: >-
        Get potential recalculation gain for tour. Recommended for tours with
        “fixed” and “use provided” optimization only
      operationId: getPotentialRecalcGain
      parameters:
        - $ref: '#/components/parameters/TourId'
        - name: lat
          in: query
          required: true
          schema:
            type: number
            format: double
        - name: lon
          in: query
          required: true
          schema:
            type: number
            format: double
      responses:
        '200':
          description: >-
            Potential recalculation gain - how much time can be saved after tour
            recalculation.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/PotentialRecalcGainResponse'
        '400':
          $ref: '#/components/responses/BadRequestError'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '404':
          $ref: '#/components/responses/NotFoundError'
        '409':
          $ref: '#/components/responses/ConflictError'
        '429':
          $ref: '#/components/responses/TooManyRequestsError'
        '500':
          $ref: '#/components/responses/ServerFailedError'
  /v1/tours/{tourId}/sequences:
    post:
      tags:
        - Sequence
      summary: Request creation of a new sequence
      operationId: createSequence
      parameters:
        - $ref: '#/components/parameters/TourId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateSequenceRequest'
      responses:
        '200':
          description: Sequence creation has been successfully initiated
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/SequenceCreated'
        '400':
          $ref: '#/components/responses/BadRequestError'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '404':
          $ref: '#/components/responses/NotFoundError'
        '500':
          $ref: '#/components/responses/ServerFailedError'
    get:
      tags:
        - Sequence
      summary: Get sequences for a given tour, ordered from newest to oldest
      operationId: getSequences
      parameters:
        - $ref: '#/components/parameters/TourId'
      responses:
        '200':
          description: List of sequences, created for a given tour
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/SequenceList'
        '400':
          $ref: '#/components/responses/BadRequestError'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '404':
          $ref: '#/components/responses/NotFoundError'
        '500':
          $ref: '#/components/responses/ServerFailedError'
  /v1/tours/{tourId}/sequences/latest:
    get:
      tags:
        - Sequence
      summary: Get latest generated sequence for a given tour
      operationId: getLatestSequence
      parameters:
        - $ref: '#/components/parameters/TourId'
      responses:
        '200':
          description: >-
            Full sequence information, including open, closed and unassigned
            waypoints
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/Sequence'
        '400':
          $ref: '#/components/responses/BadRequestError'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '404':
          $ref: '#/components/responses/NotFoundError'
        '500':
          $ref: '#/components/responses/ServerFailedError'
  /v1/tours/{tourId}/sequences/{sequenceId}:
    get:
      tags:
        - Sequence
      summary: Get a sequence by ID
      operationId: getSequenceById
      parameters:
        - $ref: '#/components/parameters/TourId'
        - $ref: '#/components/parameters/SequenceId'
      responses:
        '200':
          description: >-
            Full sequence information, including open, closed and unassigned
            waypoints
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/Sequence'
        '400':
          $ref: '#/components/responses/BadRequestError'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '404':
          $ref: '#/components/responses/NotFoundError'
        '500':
          $ref: '#/components/responses/ServerFailedError'
  /v1/tours/{tourId}/waypoints/{waypointId}/time-frame:
    put:
      tags:
        - Waypoint
      summary: Pin waypoint timeframe
      operationId: pinWaypointTimeFrame
      description: >
        Pins the provided timeframe to the waypoint. If the provided timeframe
        is NULL, the current waypoint timeframe will be removed if present.
        Calling this endpoint, even with the NULL timeframe, prevents the
        automatic timeframe resolution logic from running for the affected
        waypoint.
      parameters:
        - $ref: '#/components/parameters/TourId'
        - $ref: '#/components/parameters/WaypointId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PinTimeFrameRequest'
      responses:
        '200':
          description: Timeframe has been successfully pinned
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    example: {}
        '400':
          $ref: '#/components/responses/BadRequestError'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '404':
          $ref: '#/components/responses/NotFoundError'
        '500':
          $ref: '#/components/responses/ServerFailedError'
  /v1/tours/{tourId}/waypoints/{waypointId}/stop-time:
    put:
      tags:
        - Waypoint
      summary: Pin waypoint stop time
      operationId: pinWaypointStopTime
      description: >
        Pins the provided stop time value to the waypoint. Calling this endpoint
        "freezes" the stop time of the waypoint at provided value.
      parameters:
        - $ref: '#/components/parameters/TourId'
        - $ref: '#/components/parameters/WaypointId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PinStopTimeRequest'
      responses:
        '200':
          description: Stop time has been successfully pinned
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    example: {}
        '400':
          $ref: '#/components/responses/BadRequestError'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '404':
          $ref: '#/components/responses/NotFoundError'
        '500':
          $ref: '#/components/responses/ServerFailedError'
    delete:
      tags:
        - Waypoint
      summary: Unpin waypoint stop time
      operationId: unpinWaypointStopTime
      description: >
        Removes the previously pinned stop time value from the waypoint. Calling
        this endpoint "unfreezes" the stop time of the waypoint, allowing for
        the stop time value to be to dynamically calculated.
      parameters:
        - $ref: '#/components/parameters/TourId'
        - $ref: '#/components/parameters/WaypointId'
      responses:
        '200':
          description: Stop time has been successfully unpinned
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    example: {}
        '400':
          $ref: '#/components/responses/BadRequestError'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '404':
          $ref: '#/components/responses/NotFoundError'
        '500':
          $ref: '#/components/responses/ServerFailedError'
  /v1/tours/{tourId}/jobs/statuses:
    post:
      tags:
        - Job
      summary: Apply changes to statuses of jobs
      description: >
        Updates the status of one of more jobs, so that these changes can be
        reflected upon the tour instantly. This is only an auxiliary way for
        providing Bettermile with job updates, while the main job updates are
        still to be transmitted over the connector.
      operationId: applyJobStatusChanges
      parameters:
        - $ref: '#/components/parameters/TourId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ApplyJobStatusChangesRequest'
      responses:
        '200':
          description: Changes to statuses of jobs have been successfully applied
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    example: {}
        '400':
          $ref: '#/components/responses/BadRequestError'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '404':
          $ref: '#/components/responses/NotFoundError'
        '500':
          $ref: '#/components/responses/ServerFailedError'
  /v1/tours/{tourId}/custom-jobs:
    post:
      tags:
        - Custom Job
      summary: Create a custom job
      description: Creates a custom job
      operationId: createCustomJob
      parameters:
        - $ref: '#/components/parameters/TourId'
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateCustomJobRequest'
      responses:
        '200':
          description: Successfully created a custom job
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateCustomJobResponse'
        '400':
          $ref: '#/components/responses/BadRequestError'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '404':
          $ref: '#/components/responses/NotFoundError'
        '500':
          $ref: '#/components/responses/ServerFailedError'
  /v1/tours/{tourId}/custom-jobs/{jobId}:
    delete:
      tags:
        - Custom Job
      summary: Deletes a custom job
      description: Deletes a custom job
      operationId: deleteCustomJob
      parameters:
        - $ref: '#/components/parameters/TourId'
        - $ref: '#/components/parameters/JobId'
      responses:
        '200':
          description: Successfully deleted the custom job
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    example: {}
        '400':
          $ref: '#/components/responses/BadRequestError'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '404':
          $ref: '#/components/responses/NotFoundError'
        '500':
          $ref: '#/components/responses/ServerFailedError'
  /v1/tours/{tourId}/custom-waypoints:
    post:
      tags:
        - Custom Waypoint
      summary: Create a custom waypoint
      description: Creates a custom waypoint by grouping provided jobs together
      operationId: createCustomWaypoint
      parameters:
        - $ref: '#/components/parameters/TourId'
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateCustomWaypointRequest'
      responses:
        '200':
          description: Successfully created a custom waypoint
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateCustomWaypointResponse'
        '400':
          $ref: '#/components/responses/BadRequestError'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '404':
          $ref: '#/components/responses/NotFoundError'
        '500':
          $ref: '#/components/responses/ServerFailedError'
  /v1/tours/{tourId}/custom-waypoints/{waypointId}:
    delete:
      tags:
        - Custom Waypoint
      summary: Deletes a custom waypoint
      description: >
        Deletes a custom waypoint, allowing the backend to regroup the jobs from
        this waypoint freely.
      operationId: deleteCustomWaypoint
      parameters:
        - $ref: '#/components/parameters/TourId'
        - $ref: '#/components/parameters/WaypointId'
      responses:
        '200':
          description: Successfully deleted the custom waypoint
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    example: {}
        '400':
          $ref: '#/components/responses/BadRequestError'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '404':
          $ref: '#/components/responses/NotFoundError'
        '500':
          $ref: '#/components/responses/ServerFailedError'
components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key
  parameters:
    TourId:
      in: path
      name: tourId
      required: true
      schema:
        type: string
        format: uuid
      description: Unique tour identifier (generated by Better Route backend)
    SequenceId:
      in: path
      name: sequenceId
      required: true
      schema:
        type: integer
        format: int64
      description: Unique sequence identifier (generated by Better Route backend)
    WaypointId:
      in: path
      name: waypointId
      required: true
      schema:
        type: string
        format: uuid
      description: Unique waypoint identifier (generated by Better Route backend)
    JobId:
      in: path
      name: jobId
      required: true
      schema:
        type: string
        format: uuid
      description: Unique job identifier (generated by Better Route backend)
  schemas:
    NullValue:
      description: Null value
      type: 'null'
      enum:
        - null
    JobType:
      description: Types of a job
      type: string
      enum:
        - DELIVERY
        - PICKUP
        - COLLECTION
        - CUSTOM
        - UNKNOWN
    JobState:
      description: Status of the job
      type: string
      enum:
        - PROCESSED
        - UNPROCESSED
        - UNKNOWN
    JobOutcome:
      description: The outcome of processing this job
      type: string
      enum:
        - SUCCESS
        - UNSUCCESSFUL
    WaypointStatus:
      description: State of the waypoint
      type: string
      enum:
        - UNPROCESSED
        - PROCESSED
        - CONFIRMED
    SequenceError:
      description: >-
        Describes a reason that prevents this waypoint from being placed into
        the sequence:
          - UNREACHABLE_TIMEFRAME: The waypoint cannot be visited within the given timeframe
          - OUTSIDE_ROAD_NETWORK: The waypoint cannot be accessed over road network
          - EXCEEDS_MAX_DISTANCE_TO_DEPOT: The waypoint is too far away from tour's depot
          - UNMAPPED_REASON: Some other undocumented reason
      type: string
      enum:
        - UNREACHABLE_TIMEFRAME
        - OUTSIDE_ROAD_NETWORK
        - EXCEEDS_MAX_DISTANCE_TO_DEPOT
        - UNMAPPED_REASON
    SequenceCreationMode:
      description: |-
        Mode which is used to create a sequence:
          - FIXED_OPTIMIZATION instructs that ETAs of waypoints are to be re-calculated,
                                while the order of the waypoints is mostly kept intact
                                (waypoint may only be moved in certain special cases,
                                like when the time frame changes for a waypoint)
          - FULL_OPTIMIZATION  instructs the backend to create an entirely new sequence,
                                without attempts to maintain the previous order of waypoints
          - RETURN_TO_PREVIOUS_WAYPOINT_ORDER can be used to return to a previously used order of waypoints,
                                in a situation when results of running FULL_OPTIMIZATION need to be "undone"
          - USE_PROVIDED instructs the backend to keep the order of the provided waypoint ids (time frames will be ignored)
      type: string
      enum:
        - FIXED_OPTIMIZATION
        - FULL_OPTIMIZATION
        - RETURN_TO_PREVIOUS_WAYPOINT_ORDER
        - USE_PROVIDED
    RttCategory:
      description: Category assigned by Real Time Tracking
      type: string
      enum:
        - GOOD
        - MEDIUM
        - BLOCKED
    WaypointType:
      description: |-
        Type of the waypoint:
         - GENERATED means that the waypoint was generated automatically
         - CUSTOM means that the waypoint was created via Custom Waypoint API
      type: string
      enum:
        - GENERATED
        - CUSTOM
    Coordinates:
      description: A pair of coordinates
      type: object
      properties:
        lat:
          type: number
          format: double
          description: Latitude
          nullable: false
        lon:
          type: number
          format: double
          description: Longitude
          nullable: false
    Address:
      description: An address
      type: object
      properties:
        street:
          description: Street
          type: string
          nullable: true
        streetNumber:
          description: Street number
          type: string
          nullable: true
        locality:
          description: City, town, village etc
          type: string
          nullable: true
        postalCode:
          description: Postal/zip code
          type: string
          nullable: true
        country:
          description: >
            Country as "alpha-2 code" of ISO 3166
            (https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes)
          type: string
          nullable: true
        streetCoordinates:
          anyOf:
            - $ref: '#/components/schemas/Coordinates'
            - $ref: '#/components/schemas/NullValue'
        rooftopCoordinates:
          anyOf:
            - $ref: '#/components/schemas/Coordinates'
            - $ref: '#/components/schemas/NullValue'
    TimeFrame:
      description: Time window with earliest and latest possible time
      properties:
        earliest:
          type: string
          example: '14:00:00'
        latest:
          type: string
          example: '16:00:00'
        timezone:
          type: string
          example: Europe/Berlin
    Job:
      description: A job, i.e. a single delivery, pickup, collection etc.
      type: object
      properties:
        jobId:
          description: >
            A unique identifier used within Bettermile. The API will only accept
            / use this id to identify jobs in requests.
          type: string
          format: uuid
        externalId:
          description: >
            The job id provided to Bettermile Data Gateway. Can't be used to
            reference this job when communicating with the API.
          example: E00013222111
          nullable: true
          type: string
        type:
          $ref: '#/components/schemas/JobType'
        originalAddress:
          description: |
            Only present, if also provided via the connector data.
          anyOf:
            - $ref: '#/components/schemas/Address'
            - $ref: '#/components/schemas/NullValue'
        redirectOriginalAddress:
          description: |
            Only present, if also provided via the connector data.
          anyOf:
            - $ref: '#/components/schemas/Address'
            - $ref: '#/components/schemas/NullValue'
          example: null
        navigationAddress:
          description: >
            Filled by Bettermile during tour creation. A job provided without a
            navigationAddress will be not included in any waypoint.
          anyOf:
            - $ref: '#/components/schemas/Address'
            - $ref: '#/components/schemas/NullValue'
        state:
          anyOf:
            - $ref: '#/components/schemas/JobState'
            - $ref: '#/components/schemas/NullValue'
          example: UNPROCESSED
        outcome:
          anyOf:
            - $ref: '#/components/schemas/JobOutcome'
            - $ref: '#/components/schemas/NullValue'
          example: null
        timeFrames:
          type: array
          items:
            $ref: '#/components/schemas/TimeFrame'
    JobStatusChange:
      description: It contains all the information required to change the status of the job
      type: object
      required:
        - jobId
        - state
        - timestamp
      properties:
        jobId:
          type: string
          format: uuid
          description: Job identifier generated by the backend
        state:
          $ref: '#/components/schemas/JobState'
        timestamp:
          description: >-
            Time at which this job status was changed (it should not be older
            than 1h)
          type: string
          format: date-time
        outcome:
          anyOf:
            - $ref: '#/components/schemas/JobOutcome'
            - $ref: '#/components/schemas/NullValue'
          example: SUCCESS
        description:
          type: string
          nullable: true
          description: Description for the status change
          maxLength: 200
        coords:
          anyOf:
            - $ref: '#/components/schemas/Coordinates'
            - $ref: '#/components/schemas/NullValue'
    ApiError:
      description: Error response
      type: object
      properties:
        status:
          type: integer
          description: HTTP error status
          example: 400
        message:
          type: string
          description: Human readable error message
          example: '''depot'' field can''t be null'
        errorCode:
          type: string
          description: >-
            Machine-friendly error message containing only alphanumeric
            characters and '.'
          example: tour.list.fetch.failed.bad.request
        details:
          type: array
          items:
            type: string
    TourList:
      description: List of tours.
      type: array
      items:
        $ref: '#/components/schemas/TourListItem'
    TourListItem:
      description: Basic information about an individual tour
      type: object
      properties:
        tourId:
          type: string
          format: uuid
          description: Tour identifier generated by the backend
        date:
          type: string
          format: date
          description: Date on which the tour is / will be driven
        assignment:
          type: string
          description: Vehicle / route / driver identifier.
        depot:
          type: string
          description: Depot identifier
        created:
          type: string
          format: date-time
    Tour:
      description: >-
        Full information about an individual tour, excluding the sequence
        information
      allOf:
        - $ref: '#/components/schemas/TourListItem'
        - type: object
          properties:
            jobs:
              description: >-
                All jobs for tour's assignment.

                Jobs not referenced in waypoints have no or insufficient address
                data.
              type: array
              nullable: false
              items:
                $ref: '#/components/schemas/Job'
            waypoints:
              description: Tour waypoints in no particular order
              type: array
              items:
                $ref: '#/components/schemas/Waypoint'
            rttCategory:
              $ref: '#/components/schemas/RttCategory'
            destination:
              $ref: '#/components/schemas/Destination'
            unassignedJobIdentifiers:
              description: |-
                Identifiers of jobs that couldn't be assigned to any waypoint
                (e.g. due to lacking coordinates).
              type: array
              nullable: false
              items:
                $ref: '#/components/schemas/JobIdentifier'
    SequenceList:
      description: List of sequences.
      type: array
      items:
        $ref: '#/components/schemas/SequenceListItem'
    SequenceListItem:
      description: Basic information about a sequence, excluding waypoints
      type: object
      properties:
        sequenceId:
          type: number
          format: int64
        mode:
          $ref: '#/components/schemas/SequenceCreationMode'
        coords:
          $ref: '#/components/schemas/Coordinates'
        startTime:
          type: string
          format: time
          nullable: true
        sequenceIdWithPreviousWaypointOrder:
          type: number
          format: int64
          nullable: true
        created:
          type: string
          format: date-time
        sequenceFidelity:
          type: number
          format: double
          nullable: true
          minimum: 0
          maximum: 1
    Destination:
      description: Destination for a tour / sequence
      type: object
      nullable: true
      properties:
        address:
          type: object
          nullable: true
          allOf:
            - $ref: '#/components/schemas/Address'
        eta:
          description: Arrival time to the destination
          type: string
          format: date-time
          nullable: true
    Sequence:
      description: >-
        Full information about a sequence, including open, closed and unassigned
        waypoints
      allOf:
        - $ref: '#/components/schemas/SequenceListItem'
        - type: object
          properties:
            destination:
              $ref: '#/components/schemas/Destination'
            openWaypoints:
              description: >-
                Open waypoints, ordered accordingly to their place in the
                sequence
              type: array
              items:
                $ref: '#/components/schemas/SequenceWaypoint'
            closedWaypoints:
              description: Waypoints that are already closed (i.e. all jobs are done)
              type: array
              items:
                $ref: '#/components/schemas/SequenceWaypoint'
            unassignedWaypoints:
              description: >-
                Waypoints that are not part of the sequence but are still open

                (e.g. waypoints that could not be added to the sequence due to
                unreachable timeframes)
              type: array
              items:
                $ref: '#/components/schemas/SequenceWaypoint'
    Waypoint:
      description: Waypoint information, but without the sequence data (like ETA)
      type: object
      properties:
        waypointId:
          description: A unique identifier of this waypoint
          type: string
          format: uuid
        jobIds:
          description: |-
            List of job references as UUIDs.
            Deprecated use jobIdentifiers instead
          deprecated: true
          type: array
          items:
            type: string
            format: uuid
        jobIdentifiers:
          description: List of job IDs (internal and external)
          type: array
          items:
            $ref: '#/components/schemas/JobIdentifier'
        address:
          $ref: '#/components/schemas/Address'
        status:
          $ref: '#/components/schemas/WaypointStatus'
        closedAtTimestamp:
          description: Time at which this waypoint was closed
          type: string
          format: date-time
          nullable: true
        stopTimeInSeconds:
          description: >-
            How much time is planned for stopping at the waypoint and delivering
            / picking up parcels
          type: number
          format: int32
        timeFrame:
          nullable: true
          type: object
          allOf:
            - $ref: '#/components/schemas/TimeFrame'
        type:
          nullable: false
          type: object
          allOf:
            - $ref: '#/components/schemas/WaypointType'
    SequenceWaypoint:
      description: Waypoint enriched with the sequence data
      type: object
      allOf:
        - $ref: '#/components/schemas/Waypoint'
        - type: object
          properties:
            eta:
              description: Estimated time of arrival at this waypoint
              type: string
              format: date-time
            sequenceError:
              anyOf:
                - $ref: '#/components/schemas/SequenceError'
                - $ref: '#/components/schemas/NullValue'
              example: null
    JobIdentifier:
      type: object
      properties:
        internalId:
          nullable: false
          description: Internal Job ID reference as UUID
          type: string
          format: uuid
        externalId:
          nullable: true
          description: External Job ID reference as string
          type: string
    CreateTourRequest:
      description: Input for tour creation
      type: object
      required:
        - date
        - assignment
        - depot
      properties:
        date:
          type: string
          format: date
          description: Date on which the tour is / will be driven. Must not be in the past
        assignment:
          type: string
          description: >-
            Vehicle / route / driver identifier. This value is used to look up
            jobs in Data Gateway
          minLength: 1
          maxLength: 128
        depot:
          type: string
          description: Depot identifier
          minLength: 1
          maxLength: 128
        depotIsDestination:
          type: boolean
          description: Whether to use depot address as the destination for the tour.
          default: false
    TourCreated:
      description: Output from tour creation
      type: object
      properties:
        tourId:
          type: string
          format: uuid
    SetTourDestinationRequest:
      description: Input for setting tour destination
      type: object
      required:
        - lat
        - lon
      properties:
        country:
          description: >
            Country as "alpha-2 code" of ISO 3166
            (https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes). If
            this value is not provided, the country of the depot that this tour
            belongs to will be used.
          type: string
          nullable: true
        lat:
          description: Latitude
          type: number
          format: double
          nullable: false
          minimum: -90
          maximum: 90
        lon:
          description: Longitude
          type: number
          format: double
          nullable: false
          minimum: -180
          maximum: 180
    CreateSequenceRequest:
      description: Input for sequence creation
      type: object
      required:
        - mode
        - coords
        - startTime
      properties:
        mode:
          $ref: '#/components/schemas/SequenceCreationMode'
        coords:
          $ref: '#/components/schemas/Coordinates'
        startTime:
          type: string
          format: time
          example: '08:00:00'
        waypointIds:
          description: >
            List of waypoints IDs representing expected order of waypoints in
            the generated sequence. When provided, sequence creation mode must
            be 'USE_PROVIDED'.
          type: array
          items:
            type: string
            format: uuid
        firstWaypointId:
          description: >
            ID of the waypoint which must become first in the generated
            sequence. When provided, sequence creation mode must be
            'FULL_OPTIMIZATION'.
          type: string
          format: uuid
    SequenceCreated:
      description: Output from sequence creation
      type: object
      properties:
        sequenceId:
          type: number
          format: int64
    PinTimeFrameRequest:
      description: Input for pinning a timeframe to the waypoint
      type: object
      properties:
        timeFrame:
          nullable: true
          type: object
          allOf:
            - $ref: '#/components/schemas/TimeFrame'
    PinStopTimeRequest:
      description: Input for pinning a stop time to the waypoint
      type: object
      properties:
        stopTime:
          description: Stop time in seconds
          nullable: false
          type: number
          format: int64
          minimum: 1
          maximum: 10800
    ApplyJobStatusChangesRequest:
      description: Input for applying changes to statuses of jobs
      type: object
      properties:
        statusChanges:
          description: >
            List of job status changes to be applied (only 1 status change per
            job is allowed)
          type: array
          items:
            $ref: '#/components/schemas/JobStatusChange'
          minItems: 1
          maxItems: 150
    CreateCustomJobRequest:
      description: Input for creating a custom job
      type: object
      properties:
        rawAddress:
          description: |
            Address in a format suitable for geocoding
          type: string
          minLength: 1
          maxLength: 500
        countryCode:
          description: |
            Two letter country code defined in ISO 3166-1
          type: string
          example: US
          minLength: 2
          maxLength: 2
        externalId:
          description: >
            Optional ID that can used to identify the job. If provided, this
            value must not collide with externalId on any other job.
          type: string
          nullable: true
          maxLength: 200
    CreateCustomJobResponse:
      description: Result of creating a custom job
      type: object
      properties:
        jobId:
          description: Internal ID assigned to this job by the backend
          type: string
          format: uuid
          nullable: false
        waypointId:
          description: >-
            Internal ID of (potentially new) waypoint to which the job has been
            assigned
          type: string
          format: uuid
          nullable: false
    PotentialRecalcGainResponse:
      description: Result of requesting for potential recalculation gain
      type: object
      properties:
        potentialGainInSeconds:
          description: potential time gaines in seconds
          type: number
          format: int32
          nullable: true
    CreateCustomWaypointRequest:
      description: Input for creating a custom waypoint
      type: object
      properties:
        jobIds:
          description: IDs of the jobs to be added under the custom waypoint
          oneOf:
            - $ref: '#/components/schemas/JobInternalIds'
            - $ref: '#/components/schemas/JobExternalIds'
        address:
          description: >
            Specifies address to be used for the custom waypoint. If not
            provided one of the job addresses is used.
          anyOf:
            - $ref: '#/components/schemas/Address'
            - $ref: '#/components/schemas/NullValue'
    CreateCustomWaypointResponse:
      description: Result of creating a custom waypoint
      type: object
      properties:
        waypointId:
          description: Internal ID of the created custom waypoint
          type: string
          format: uuid
          nullable: false
    JobInternalIds:
      type: object
      properties:
        internalIds:
          description: A list of internal Job IDs assigned by Bettermile
          nullable: false
          type: array
          items:
            type: string
            format: uuid
    JobExternalIds:
      type: object
      properties:
        externalIds:
          description: A list of external Job IDs assigned by the tenant
          nullable: false
          type: array
          items:
            type: string
  responses:
    BadRequestError:
      description: >
        Error response returned when the request is invalid. In most cases this
        implies that some data in the request is either missing or has incorrect
        format.
      content:
        application/json:
          schema:
            type: object
            properties:
              error:
                $ref: '#/components/schemas/ApiError'
          example:
            error:
              status: 400
              message: '''date'' field is required'
    UnauthorizedError:
      description: |
        Error response returned when the provided API key is missing or invalid
      content:
        application/json:
          schema:
            type: object
            properties:
              error:
                $ref: '#/components/schemas/ApiError'
          example:
            error:
              status: 401
              message: Request is missing 'x-api-key' header
    NotFoundError:
      description: >
        Error response returned in cases the requested endpoint or data can't be
        found
      content:
        application/json:
          schema:
            type: object
            properties:
              error:
                $ref: '#/components/schemas/ApiError'
          example:
            error:
              status: 404
              message: Tour not found
    ConflictError:
      description: >
        Error response returned in case operation will result in incorrect state
        of the resource
      content:
        application/json:
          schema:
            type: object
            properties:
              error:
                $ref: '#/components/schemas/ApiError'
          example:
            error:
              status: 409
              message: No destination set on tour
    TooManyRequestsError:
      description: |
        Error response returned in case operation encounter rate limi
      content:
        application/json:
          schema:
            type: object
            properties:
              error:
                $ref: '#/components/schemas/ApiError'
          example:
            error:
              status: 429
              message: Rate limit exceeded
    ServerFailedError:
      description: |
        Error response returned whenever the server fails to process the request
      content:
        application/json:
          schema:
            type: object
            properties:
              error:
                $ref: '#/components/schemas/ApiError'
          example:
            error:
              status: 500
              message: Server error
