openapi: 3.0.2
tags:
  - name: Operational Route Planner
    description: Services for the operational route planner
info:
  title: Operational Route Planner API
  version: '1'
  description: |
    <p>The Operational Route Planner takes in as input tasks, work shifts and some additional data and finds the best plan for doing these tasks by providing routes of tasks for each work shift. The planner takes travel time, time windows, visit history and employee priority into account, and can be configured to prioritize the objectives based on the users' preference. The Operational Route Planner is for example used in Home Nursing Care to create daily routes for the health care employees.</p>
    <h3>Supported features</h3> <p>The ORP is highly flexible and supports multiple use cases. The most common ones are:</p> <ul>
        <li> The possibility to configure individual employees shift and rules for potential overtime, their means of travel and so on using the <i>employee work shift</i> and <i>shift overtime</i> elements</li>
        <li> The possibility to configure individual tasks and the potential relation between them using the <i>task</i>, <i>time dependent task pair</i> and <i>incompatible task list</i> elements</li>
        <li> The possibility to specify which employees can perform a task using the <i>task shifts compatibility matrix row</i> element</li>
        <li> The possibility to specify which employees are preferred to be assigned specific tasks using the <i>shift penalty matrix row</i></li>
        <li> The possibility to configure certain tasks to either be forced or preferred to the same employee during a single day using the <i>internal visit history group</i> element</li>
        <li> The possibility to support multiple transport modes for travel between tasks (such as walk, bicycle and car) by using the <i>travel time matrix</i> element</li>
        <li> The possibility to configure the route planner objectives to suit a team's specific preferences for routes using the <i>configuration</i> element</li>
    </ul>
    <h3>The time definition</h3> <p>The term time is used in this API. For instance, the start and end of a task are defined by a time. Time should always be defined as seconds. It is important that the time metric seconds is consistent in all places where time is defined. The user of the API has to define times based on a zero point. As an example the zero point could be set to be 0 and let it represent 01.01.2021 00:00. Let's say you would like to send in a task with a time window which is from 01.01.2021 07:00 to 01.01.2021 08:00. Then this time window should be referred to like this:</p> <ul>
        <li> <i>fromTime</i> = 25200, <i>toTime</i> =  28800 (seconds) </li>
    </ul> <p> This means that you also could use epoch time if you want. Keep in mind that seconds should be used as time unit when defining work shifts for employees, tasks and travel times.</p>

    <h3>Authentication</h3> <p>Authentication uses OAuth tokens from *Visma Connect*. Authorization is done on tenant level, so one OAuth token is needed per tenant. This can be tested by using the <i>Authorize</i> button on the right. To use the API you have to register a user on the *Visma Developer Portal* and request access to the API along with the required scopes. The scopes required are documented under each endpoint. If you don't have an application registered on Visma Connect you have to do it by:</p>
    1. Log into <a href="https://oauth.developers.visma.com">https://oauth.developers.stagaws.visma.com</a>  
    2. Click on <i>My applications</i>
    3. Create a new <i>Service Application</i> (machine-to-machine)
    4. Set a name, client id and description of the application
    5. Create a client secret under the section <i>Credentials</i>

    When you have an application registered in Visma Connect you are now able to apply for an integration against the Optimization Factory APIs:
    1. Navigate to <i>integrations</i> for the selected application
    2. Search for the API called <i>optimization-factory-api-prod</i>
    3. Apply for the scope you would like access to
    4. The Optimization Factory team will now receive a notification and approve the scope requested

    To use the Optimization Factory API you need an access token generated by Visma Connect's API. This could be done by either using the integrated Authorize-functionality in this website or you could generate it by following these steps:
    1. Send a POST request to <a href="https://connect.visma.com/connect/token">https://connect.identity.stagaws.visma.com/connect/token</a>
    2. Include the follwoing parameter in the body (the parameters must be in the payload (body) of the request and should be in form-urlencoded format. Not as querystring parameters)
        * client_id: <CLIENT_ID>
        * client_secret: <CLIENT_SECRET>
        * grant_type: client_credentials
    3. The token received should be used to request the Optimization Factory API by including the following header
        * Authorization: Bearer <ACCESS_TOKEN>

    For more information about *Visma Connect OAuth 2.0 Bearer Token Authentication* see <a href="https://confluence.visma.com/display/PROV/Visma+Connect+OAuth+2.0+Bearer+Token+Authentication">https://confluence.visma.com/display/PROV/Visma+Connect+OAuth+2.0+Bearer+Token+Authentication<a/>
    <h3>Status codes</h3> <ul>
        <li> 200 OK </li>
        <li> 202 Accepted - The request has been accepted for processing, but the processing has not been completed </li>
        <li> 400 Bad request/Data validation error </li>
        <li> 403 Forbidden </li>
        <li> 404 Not found </li>
        <li> 429 Too Many Requests </li>
        <li> 500 Internal Error - Unexpected condition was encountered and no more specific message is suitable </li>
    </ul>
    <h3>Handling 5XX errors</h3> <p>Sometimes 5XX errors might occur due to network issues such as delays or connection timeouts. Therefore we encourage our clients to always do one retry if the API returns a 5XX error.</p>
    <h3>Throttling levels</h3> <p> Rate limiting is performed on the API calls for an employee for each API consumer. Status regarding the rate limit is returned as headers. Once the rate limit is hit, all requests will return HTTP status code 429 for the remainder of the current period.</p>
    <h3>The user context</h3> <p>Each endpoint require the headers <i>customer_id</i> and <i>user_id</i>. The <i>customer_id</i> header should represent specific customer (company/municipality) that is using API and the <i>user_id</i> header should represent and identify unique user or region of that customer. The client implementing integration with the API is responsible for distinguishing between its customers and users by using these header fields. Header values allow us to track usage statistics and understand how different groups of end-users are using the service. That helps improve the optimization service offering and respond to any incidents faster.</p>
    <h3>Slack channel for communications</h3> <p>If you have any questions regarding the service. Please reach out to us in the slack channel #optimization-factory-community.</p>
servers:
  - url: 'https://api.optimization.visma.com/orp/'
security:
  - visma_connect: []
paths:
  /start:
    post:
      responses:
        '202':
          description: Solver started
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StartResponse'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StartResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ResponseError'
        '500':
          description: Internal Server Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ResponseError'
      summary: Starts the solver
      tags:
        - Operational Route Planner
      operationId: startSolver
      parameters:
        - name: user_id
          in: header
          description: Id of the user requesting the optimization job
          required: true
          schema:
            type: string
      requestBody:
        description: Starts the operational route planner solver. The body should represent the necessary data to solve the route optimization problem. The solver starts the process in finding the allocation of tasks to employees and the order of the tasks. The result can be fetched via the /result endpoint.
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Request'
  '/result/{job_id}':
    get:
      summary: Returns a solution
      tags:
        - Operational Route Planner
      operationId: getResult
      parameters:
        - name: job_id
          in: path
          description: Id of the job to return result for.
          required: true
          schema:
            type: string
        - name: user_id
          in: header
          description: Id of the user requesting the result.
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Generated routes
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Response'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/paths/~1stop~1%7Bjob_id%7D/put/responses/400/content/application~1json/schema/items'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/paths/~1stop~1%7Bjob_id%7D/put/responses/400/content/application~1json/schema/items'
        '404':
          description: Not found
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/paths/~1stop~1%7Bjob_id%7D/put/responses/400/content/application~1json/schema/items'
        '429':
          description: Too many requests
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/paths/~1stop~1%7Bjob_id%7D/put/responses/400/content/application~1json/schema/items'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/paths/~1stop~1%7Bjob_id%7D/put/responses/400/content/application~1json/schema/items'
        default:
          description: This is used for having a valid open api spec. The response for start solver is specified in solver service.
  '/status/{job_id}':
    get:
      summary: Returns the status for a job
      operationId: getStatus
      parameters:
        - name: job_id
          in: path
          description: ID of job to return status for
          required: true
          schema:
            type: string
        - name: user_id
          in: header
          description: ID of the user requesting the status
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Job status
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/JobStatus'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/paths/~1stop~1%7Bjob_id%7D/put/responses/400/content/application~1json/schema/items'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/paths/~1stop~1%7Bjob_id%7D/put/responses/400/content/application~1json/schema/items'
        '404':
          description: Not found
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/paths/~1stop~1%7Bjob_id%7D/put/responses/400/content/application~1json/schema/items'
        '429':
          description: Too many requests
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/paths/~1stop~1%7Bjob_id%7D/put/responses/400/content/application~1json/schema/items'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/paths/~1stop~1%7Bjob_id%7D/put/responses/400/content/application~1json/schema/items'
      tags:
        - Operational Route Planner
  '/stop/{job_id}':
    put:
      summary: Stop the solver job
      operationId: stop
      parameters:
        - name: job_id
          in: path
          description: ID of job to stop
          required: true
          schema:
            type: string
        - name: user_id
          in: header
          description: ID of the user requesting the stop service
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Stop response
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    solver_stopped:
                      type: boolean
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    error_messages:
                      type: string
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/paths/~1stop~1%7Bjob_id%7D/put/responses/400/content/application~1json/schema/items'
        '404':
          description: Not found
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/paths/~1stop~1%7Bjob_id%7D/put/responses/400/content/application~1json/schema/items'
        '429':
          description: Too many requests
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/paths/~1stop~1%7Bjob_id%7D/put/responses/400/content/application~1json/schema/items'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/paths/~1stop~1%7Bjob_id%7D/put/responses/400/content/application~1json/schema/items'
      tags:
        - Operational Route Planner
  /healthcheck:
    get:
      summary: Healthcheck
      tags:
        - Solver Service Availability
      operationId: healthcheck
      responses:
        '200':
          description: Service Available
        '400':
          description: Bad request
        '403':
          description: Forbidden
        '404':
          description: Result not found
        '500':
          description: Internal Server Error
components:
  schemas:
    RequestConfiguration:
      type: object
      title: Configuration
      description: The solver can be configured with double weights for objectives and boolean values for constraints. The sum of the weights (in double format) cannot be greater than 1.0.
      required:
        - solverRunTimeSeconds
      properties:
        solverRunTimeSeconds:
          description: 'Gives the amount of seconds you would like the solver to run for. It works as a maximum value for how long the optimization runs. The run time should reflect the complexity of your problem, i.e. if the problem is complex the run time should be greater to ensure that the quality of the solution is good. SolverRunTimeInSeconds is typically set to 3-10 minutes depending on how complex the problem is. The maximum run time allowed is 14 minutes.'
          type: integer
          minimum: 1
          maximum: 840
        travelTimeWeight:
          description: Importance of minimizing travel time.
          type: number
          format: double
          minimum: 0
          maximum: 1
          default: 0
        travelDistanceWeight:
          description: Importance of minimizing travel distance.
          type: number
          format: double
          minimum: 0
          maximum: 1
          default: 0
        timeWindowWeight:
          description: Importance of scheduling tasks within time windows.
          type: number
          format: double
          minimum: 0
          maximum: 1
          default: 0
        shiftPenaltyWeights:
          description: Importance of shift penalty for the different shift penalty matrices. The position in the list represents the weight for the shift matrix at the same position in the shift penalty matrices list. This list has to have the same size as shiftPenaltyMatrices.
          type: array
          items:
            type: number
            format: double
            minimum: 0
            maximum: 1
            default: 0
        internalSolutionVisitHistoryWeight:
          description: Importance of internal solution visit history.
          type: number
          format: double
          minimum: 0
          maximum: 1
          default: 0
        overallOvertimeWeight:
          description: The general importance of overtime.
          type: number
          format: double
          minimum: 0
          maximum: 1
          default: 0
        useWorkBalance:
          description: Whether the solver should attempt to ensure work balance among employees.
          type: boolean
          default: false
        workBalanceWeight:
          description: The general importance of work balance. Giving this weight a high value will result in the solver prioritizing finding routes where the amount of work is distributed as evenly as possible among the employees.
          type: number
          format: double
          minimum: 0
          maximum: 1
          default: 0
        useTooLateArrivalConstraint:
          description: 'Whether the solver should constrain tasks to not be performed after some maximum delay. Unless tooLateArrivalConstraintTimeLimit is set the default value will be used. This means that if the value is set to true no tasks will be assigned later than the maximum allowed late arrival. If they cannot be assigned within the allowed arrival period, they will be unallocated.'
          type: boolean
          default: false
        tooLateArrivalConstraintTimeLimit:
          description: 'Time limit for maximum task delay, given in seconds, maximum 3 hours.'
          type: integer
          minimum: 0
          maximum: 10800
        useRideSharing:
          description: |
            Indicates if solver should try employing ride-sharing to obtain better solutions. 
            Ride-sharing is defined as two or more persons driving in the same vehicle.
            If this value is set to true, ride-sharing will be used, otherwise solver will not used it.
            In case this value is not present, false value will be assumed.
          type: boolean
          default: false
    RequestEmployeeWorkShift:
      type: object
      title: Employee Work Shift
      required:
        - shiftId
        - employeeId
        - timeWindow
        - transport
      properties:
        shiftId:
          description: Id of employee shift. Must be unique.
          type: integer
        employeeId:
          description: 'Id of employee. Does not need to be unique among shifts, as one employee can have multiple shifts.'
          type: integer
        timeWindow:
          description: Time window representing the start and end time of the shift.
          $ref: '#/components/schemas/RequestTimeWindow'
        transport:
          description: Type of transport mode employee uses during shift. Must match a transport mode in the travel time matrix.
          type: string
        capacity:
          description: 'Capacity of shift related to weight of tasks. This can for example be used to represent tasks that are particularly heavy and the maximum number of such tasks that should be performed by the employee shift. If this value is not set, the shift will have no capacity limit.'
          type: integer
        startLocationId:
          description: 'Id of location where shift starts. This locationId must be present in the travel time matrix. If not included, that will mean an open start to the shift''s route, meaning the route starts at the first task of the route.'
          type: integer
        endLocationId:
          description: 'Id of location where shift ends. This locationId must be present in the travel time matrix. If not included, that will mean an open end to the shift''s route, meaning the route finishes at the last task of the route.'
          type: integer
        minimumAmountOfWork:
          description: Minimum amount of work that should be assigned to the employee represented in seconds. The solver will prioritize giving the shift enough tasks so that it has a total task duration that is above this threshold. Tasks with isBreak = true will not count towards this minimum amount of work.
          type: integer
        globalEmployeeId:
          description: 'A unique Id assigned to each employee across all requests within the API. This identifier distinguishes individual employees globally, ensuring a unique reference irrespective of the request they are associated with.'
          type: string
    RequestIncompatibleTaskListRow:
      type: object
      title: Incompatible Task List Row
      required:
        - taskId
        - incompatibleTaskIds
      description: Incompatibility is mutual; specifying in one direction is sufficient.
      properties:
        taskId:
          type: integer
        incompatibleTaskIds:
          description: Ids of tasks that are incompatible with the task.
          type: array
          items:
            type: integer
    RequestInternalVisitHistoryGroup:
      type: object
      title: Internal Visit History Group
      required:
        - tasks
      properties:
        tasks:
          description: Tasks in the group.
          type: array
          items:
            description: Ids of the tasks in the group.
            type: integer
        useAsHardRequirement:
          description: 'If true, the tasks in this group must be performed by the same employee and they cannot be divided over multiple employees. This can have a negative impact on the general quality of the solution. If false, the solver will still attempt to ensure that the tasks are performed by the same employee, but it will be weighed against the other objectives.'
          type: boolean
          default: false
    RequestRideSharingConfiguration:
      type: object
      title: Ride-Sharing Configuration
      required:
        - rideSharingTransportModes
        - rideSharingEmployeeWorkShifts
      properties:
        rideSharingTransportModes:
          description: |
            List of all transport modes that can be used in ride-sharing. 
            A mapping from transport mode to ride-sharing role has to be defined, so that employee work shifts with the same transport mode participate in ride-sharing with the same role.
            For both ride-sharing roles "driver" and "passenger" at least one transport mode should be defined.
            When a transport mode is not included in this list, all employee work shifts with this transport mode will not participate in ride-sharing.
          type: array
          items:
            $ref: '#/components/schemas/RequestRideSharingTransportMode'
        rideSharingEmployeeWorkShifts:
          description: |
            A list of all employee work shifts that are allowed to participate in ride-sharing.
            Also includes optional individual ride-sharing settings for drivers and employee work shift compatibilities.
          type: array
          items:
            $ref: '#/components/schemas/RequestRideSharingEmployeeWorkShift'
    RequestRideSharingDriverProperties:
      type: object
      title: Ride-Sharing Driver Properties
      description: Only required for transport modes with the "Driver" ride-sharing role. Default ride-sharing settings for all drivers with the corresponding transport mode. This object is ignored if it is used for a transport mode with the "Passenger" ride-sharing role.
      properties:
        maxPassengerCount:
          description: |
            Indicates the available seats in the vehicle that can be used for ride-sharing purposes.
            This count doesn't include driver, therefore minimum available seats should be at least 1.
          type: integer
          format: int32
          minimum: 1
          default: 1
        delay:
          description: |
            Represents time used for picking up and dropping off the passenger.
            If not present, solver will assume that picking up and dropping off is instantaneous. 
            Delay should be configured using seconds as time unit.
          type: integer
          format: int32
          minimum: 0
          default: 0
    RequestRideSharingEmployeeWorkShift:
      type: object
      title: Ride-Sharing Employee Work Shifts
      required:
        - shiftId
      properties:
        shiftId:
          type: integer
        driverProperties:
          description: |
            Optional override of maxPassengerCount and delay for the individual employee work shift.
            Should only be used for employee work shifts with a transport mode that maps to the "driver" ride-sharing role.
          $ref: '#/components/schemas/RequestRideSharingDriverProperties'
        employeeWorkShiftCompatibilities:
          description: |
            A list with entries that indicate the ride-sharing compatibility level between the current employee work shift and another employee work shift participating in ride-sharing.
            The compatibility is assumed to go both ways, so it is not necessary to define the compatibility for the other employee work shift back to the current employee work shift.
            If the reverse connection (from the other employee work shift back to the current employee work shift) is defined with a different compatibility, the smaller value will be taken.
            When the compatibility between two employee work shifts is not defined, it is assumed that they are fully compatible.
          type: array
          items:
            $ref: '#/components/schemas/RequestRideSharingShiftCompatibility'
    RequestRideSharingRole:
      title: Ride-Sharing Role
      type: string
      description: The role that should be applied in the ride-sharing setting to this transport mode.
      enum:
        - driver
        - passenger
    RequestRideSharingShiftCompatibility:
      type: object
      title: Ride-Sharing Employee Work Shift Compatibilities
      required:
        - shiftId
      properties:
        shiftId:
          type: integer
        compatibilityWeight:
          description: |
            The weight represents ride-sharing compatibility preference between different employee work shifts. 
            Setting this value to 0 will result in the solver using a hard constraint that forbids these shifts from ride-sharing together in the solution.
            Any non-zero value will indicate preference level, and the solver will select ride-sharing pairs with higher
            compatibility preference if in any other respect solutions are equal. Any non-zero value will be used 
            as a soft-constraint and will not enforce that the solution should contain this ride-sharing pair.
          type: number
          format: double
          maximum: 1
          minimum: 0
          default: 1
    RequestRideSharingTransportMode:
      type: object
      title: Ride-Sharing Transport Modes
      required:
        - transport
        - role
      properties:
        transport:
          description: |
            A transport mode. Must match one of the transport modes used for employee work shifts.
          type: string
        role:
          description: |
            The role that should be applied in the ride-sharing setting to this transport mode.
          $ref: '#/components/schemas/RequestRideSharingRole'
        driverProperties:
          description: |
            Only required for transport modes with the "driver" ride-sharing role:
            Default ride-sharing settings for all drivers with the corresponding transport mode.
            This object is ignored if it is used for a transport mode with the "passenger" ride-sharing role.
          $ref: '#/components/schemas/RequestRideSharingDriverProperties'
    RequestShiftOvertime:
      type: object
      title: Shift Overtime
      required:
        - shiftId
        - shiftOvertimeWeight
        - shiftMaxOvertime
      properties:
        shiftId:
          type: integer
        shiftOvertimeWeight:
          description: Penalty for a shift to be assigned overtime. 1 indicates the highest possible penalty and that this shift is preferred not to have overtime. A penalty of 0 indicates the opposite and that the shift should not be penalized for having overtime.
          type: number
          format: double
          maximum: 1
          minimum: 0
        shiftMaximumOvertime:
          description: Maximum amount of overtime for the shift. Given in seconds.
          type: integer
          minimum: 0
    RequestShiftPenalty:
      type: object
      title: Shift Penalty
      required:
        - shiftId
        - shiftPenalty
      properties:
        shiftId:
          type: integer
        shiftPenalty:
          description: Penalty of a shift to a task. 1 indicates the highest possible penalty and it that is not preferred to assign this task in this shift.
          type: number
          format: double
          maximum: 1
          minimum: 0
          default: 0
    RequestShiftPenaltyMatrix:
      type: object
      title: Shift Penalty Matrix
      required:
        - shiftPenaltyMatrix
      properties:
        shiftPenaltyMatrix:
          type: array
          items:
            $ref: '#/components/schemas/RequestShiftPenaltyMatrixRow'
    RequestShiftPenaltyMatrixRow:
      type: object
      title: Shift Penalty Matrix Row
      required:
        - taskId
        - shiftPenaltyList
      properties:
        taskId:
          description: Id of the task.
          type: integer
        shiftPenaltyList:
          description: List of penalties for a task to be conducted during a shift.
          type: array
          items:
            $ref: '#/components/schemas/RequestShiftPenalty'
    RequestTask:
      type: object
      title: Task
      required:
        - taskId
        - timeWindow
        - duration
        - strictTimeWindow
        - weight
      properties:
        taskId:
          description: Id of the task. Must be unique.
          type: integer
          minimum: 0
        duration:
          description: The estimated duration of the task in seconds.
          type: integer
          default: 0
        strictTimeWindow:
          description: Defines whether the time window should be followed strictly. If true the task must be scheduled within the time window.
          type: boolean
        timeWindow:
          description: Defines the time window that the task should be scheduled within.
          $ref: '#/components/schemas/RequestTimeWindow'
        weight:
          description: Defines the weight of the task. Can be used to represent the heaviness of the task or required load to complete the task. Used in combination with capacity of shifts.
          type: integer
          minimum: 0
        locationId:
          description: 'Id of the location where the task should be performed. The locationIds must be a continuous number series from 0 and incremented by 1, to accommodate the structure of the travel time matrix. This means that if you have four locations, their ids should be 0, 1, 2 and 3. If requirePhysicalAppearance is true then this field is required.'
          type: integer
        requirePhysicalAppearance:
          description: Defines whether the task requires physical appearance. If false the task can be scheduled as a phone call or similar as a stop on the way to another task with a physical location.
          type: boolean
          default: true
        prioritize:
          description: 'Defines whether the task is prioritized. If true, the task will not be unallocated once it has been allocated to an employee. However, it is important to note that the task must still be feasible to allocate to an employee. If it is not feasible, the task might not be allocated even when prioritized.'
          type: boolean
          default: false
        isBreak:
          description: 'If true, this task will be excluded from work balance calculations. Setting an employees minimumAmountOfWork too high can lead to a deprioritization of break tasks as they do not count towards reaching this minimum amount of work.'
          type: boolean
          default: false
    RequestTaskShiftsCompatibilityRow:
      type: object
      title: Task to Shifts Compatibility Matrix Row
      required:
        - taskId
        - compatibleShiftIds
      properties:
        taskId:
          description: Id of the task.
          type: integer
        compatibleShiftIds:
          description: 'List of shiftIds of all shifts to which this task may be assigned. Note that each task needs to be compatible with at least one shift, so this list may not be empty.'
          type: array
          items:
            type: integer
    RequestTimeDependentTaskPair:
      type: object
      title: Time Dependent Task Pair
      required:
        - masterTaskId
        - dependentTaskId
        - intervalStart
        - intervalEnd
      properties:
        intervalStart:
          description: Defines the minimum difference between the master task start time and the dependent task start time.
          type: integer
          minimum: 0
        intervalEnd:
          description: Defines the maximum difference between the master task start time and the dependent task start time.
          type: integer
          minimum: 0
        masterTaskId:
          description: 'Id of the task that is considered as master in this time dependent task pair. This task determines when the dependent task should start. A master task cannot be dependent in another pair, but it can be the master task for multiple dependent tasks, defined by multiple pairs.'
          type: integer
        dependentTaskId:
          description: Id of the dependent task. This task will be scheduled so that it starts within the interval given after the master task. A dependent task cannot be dependent in multiple pairs.
          type: integer
    RequestTimeWindow:
      type: object
      title: Time Window
      required:
        - fromTime
        - toTime
      properties:
        fromTime:
          type: integer
          minimum: 0
          description: The start time of the time window. The start time of a task should be the earliest allowed start time for the task.
        toTime:
          type: integer
          minimum: 0
          description: 'The end of the time window. For a task this is the latest allowed end time of the task, which means that the duration of the task should be taken into account when setting the end of the time window, i.e. the time window should be at least as long as the task''s duration.'
    RequestTransportTravelDistanceMatrix:
      type: object
      title: Transport Travel Distance Matrix
      required:
        - transport
        - matrix
      properties:
        transport:
          description: 'Transport mode for travel distance, must match transport modes for employee work shifts.'
          type: string
        maxTravelDistance:
          description: 'Represents maximal travel distance that can be assigned to the employee using this transport mode.  If none given, no such limit will be applied.'
          type: integer
          format: int32
        matrix:
          description: 'Travel distance matrix for the given transport mode, given as a matrix consisting of integers. The matrix positions are defined by the locationIds, which must be a number series from 0 incremented by 1. For example, the travel distance from location 1 to location 2 is given by the second outer array and the third position in that array. The travel distance should be given as an integer greater than or equal to 0, and the distance unit is meters. However, if travel between two locations is not allowed, the travel distance between the locations should be given as -1.'
          type: array
          items:
            type: array
            items:
              type: integer
    RequestTransportTravelTimeMatrix:
      type: object
      title: Transport Travel Time Matrix
      required:
        - transport
        - matrix
      properties:
        transport:
          description: 'Transport mode for travel time, must match transport modes for employee work shifts.'
          type: string
        maxTravelTime:
          description: 'Represents maximal travel time that can be assigned to the employee using this transport mode.  If none given, no such limit will be applied.'
          type: integer
          format: int32
        matrix:
          description: 'Travel time matrix for the given transport mode, given as a matrix consisting of integers. The matrix positions are defined by the locationIds, which must be a number series from 0 incremented by 1. For example, the travel time from location 1 to location 2 is given by the second outer array and the third position in that array. The travel time should be given as an integer greater than or equal to 0, and the time unit is seconds. However, if travel between two locations is not allowed, the travel time between the locations should be given as -1.'
          type: array
          items:
            type: array
            items:
              type: integer
    Request:
      type: object
      title: Model Operational Route Planner
      description: This is the model payload for the operational route planner solver
      required:
        - employeeWorkShifts
        - tasks
        - travelTimeMatrix
        - solverConfiguration
        - taskShiftsCompatabilityMatrix
      properties:
        employeeWorkShifts:
          description: List of employee work shifts.
          type: array
          items:
            $ref: '#/components/schemas/RequestEmployeeWorkShift'
        tasks:
          description: List of tasks.
          type: array
          items:
            $ref: '#/components/schemas/RequestTask'
        timeDependentTaskPairs:
          description: 'List of time dependent task pairs. These pairs can be used to determine time dependencies between tasks, for example if task with id=2 needs to be done minimum 2 hours and maximum 3 hours after task with id=1. They can then be performed by the same or different employees. If the tasks need to be synchronized and done by different employees at the same time, the tasks can also be listed in incompatibleTaskList to ensure that the tasks are performed by different employees.'
          type: array
          items:
            $ref: '#/components/schemas/RequestTimeDependentTaskPair'
        incompatibleTaskList:
          description: 'List of tasks that cannot be performed by the same employee, i.e. should not be scheduled in the same route. incompatibleTaskList can also be used in combination with timeDependentTaskPairs to ensure two or more tasks are synchronized in time and performed by different employees.'
          type: array
          items:
            $ref: '#/components/schemas/RequestIncompatibleTaskListRow'
        taskShiftsCompatabilityMatrix:
          description: 'Matrix that defines the compatibility between shifts and tasks, i.e. it lists for each task to which shifts it can be assigned.'
          type: array
          items:
            $ref: '#/components/schemas/RequestTaskShiftsCompatibilityRow'
        internalVisitHistoryGroups:
          description: 'List of internal visit history groups. Tasks within the same internal visit history group are prioritized to be performed by the same employee, or as few distinct employees as possible.'
          type: array
          items:
            $ref: '#/components/schemas/RequestInternalVisitHistoryGroup'
        travelTimeMatrix:
          description: Travel time matrix that defines the travel time between all locations for (optionally) different transport modes.
          type: array
          items:
            $ref: '#/components/schemas/RequestTransportTravelTimeMatrix'
        travelDistanceMatrix:
          description: Travel distance matrix that defines the travel distance between all locations for (optionally) different transport modes.
          type: array
          items:
            $ref: '#/components/schemas/RequestTransportTravelDistanceMatrix'
        shiftOvertimeLists:
          description: 'List of shift overtime information. If no information is given a shift can have overtime and it is penalized with the maximum penalty possible. However, in that case there is no limit as to how much overtime can be assigned.'
          type: array
          items:
            $ref: '#/components/schemas/RequestShiftOvertime'
        solverConfiguration:
          $ref: '#/components/schemas/RequestConfiguration'
        shiftPenaltyMatrices:
          description: 'List of shift penalty matrices that defines the penalty of conducting a task during a shift. The taskShiftCompatibilityMatrix dominates penalty matrices. This list can contain multiple penalty matrices, each of which consists of numbers between 0 and 1 reflecting the penalty incurred when conducting a specific task during a specific shift. A penalty of 1 means that the route planner should highly try to avoid allocating the task to the shift. All of these matrices are summarized and normalized before optimization process. The shift penalty matrices could for instance be used to (1) create penalties for doing overqualified work (i.e. nurses should not do assistant work), (2) visit history reasons (i.e. employees that have been few times to a location should get a high penalty), (3) giving penalties to shifts that are conducted by employees working for a separate organization (i.e. when two organizations have merged the planning to utilize employees/resources across organizations). Default values for all the penalties is 0. When using this field, it is required to also submit shiftPenaltyWeights in the solverConfiguration object.'
          type: array
          items:
            $ref: '#/components/schemas/RequestShiftPenaltyMatrix'
        rideSharingConfiguration:
          description: Configurations needed for ride-sharing. Only required when ride-sharing is enabled in the solverConfiguration.
          $ref: '#/components/schemas/RequestRideSharingConfiguration'
    ResponseRideSharingConfig:
      type: object
      title: Ride-Sharing Task Configuration
      description: |
        Describes ride-sharing tasks in the shift route. This config can describe both the pick up and drop off 
        from the driver or the passenger perspective.
      properties:
        rideShareGroupId:
          description: 'Id that indicates which group of ride-sharing tasks this task is a part of. A group of ride-sharing tasks consist of four tasks, one drop-off and pick-up for both the passenger and the driver.'
          type: integer
        rideShareId:
          description: 'Id that indicates which ride-share event this task is a part of. A ride-share event starts with a pick-up task and ends with a drop-off task. If a driver picks up multiple passengers before dropping them off, these pick-up and drop-off tasks will be considered part of the same ride-share event and therefore they will have the same rideShareId. The rideShareId is unique for each shift participating in a ride-share event.'
          type: integer
        role:
          type: string
          enum:
            - driver
            - passenger
        action:
          type: string
          enum:
            - pick_up
            - drop_off
        relatedRideSharingTasks:
          type: array
          items:
            $ref: '#/components/schemas/ResponseRideSharingRelatedTask'
    ResponseRideSharingRelatedTask:
      type: object
      title: Ride-Sharing Related Task
      description: |
        References task related to the ride-sharing action, e.g., if the driver has a ride-sharing task in his route, that task
        will reference the related task in the passenger's route.
      properties:
        shiftId:
          description: ShiftId of ride-sharing task.
          type: integer
        taskId:
          description: TaskId of task belonging to the same ride-sharing action but not the same route.
          type: integer
    ResponseVisit:
      type: object
      description: A planned visit given by the route planner
      required:
        - taskId
        - fromTime
        - toTime
        - travelTimeFromPreviousLocation
      properties:
        taskId:
          description: TaskId received from the client that uniquely identifies the task. Negative taskIds will be used for tasks created by the solver.
          type: integer
        fromTime:
          type: integer
        toTime:
          type: integer
        travelTimeFromPreviousLocation:
          type: integer
        travelDistanceFromPreviousLocation:
          type: integer
        timeDependentStartTime:
          type: integer
        locationId:
          type: integer
          description: Location where the visit is performed.
        rideSharingConfig:
          $ref: '#/components/schemas/ResponseRideSharingConfig'
    RouteKeyNumbers:
      type: object
      description: Key numbers of a route for a given employee shift. Also includes the route itself.
      required:
        - shiftId
        - totalTravelTime
        - totalTimeBreakingTimeWindows
        - route
      properties:
        shiftId:
          type: integer
        totalTravelTime:
          type: number
          format: double
        totalTravelDistance:
          type: integer
          format: int32
        totalRideSharingCount:
          description: Number of ride-shares used in the solution. One pick-up and drop-off pair counts as one ride-share.
          type: integer
          format: int32
        totalTimeBreakingTimeWindows:
          type: number
          format: double
        totalWeight:
          type: integer
        endLocationArrivalTime:
          type: integer
        route:
          type: array
          items:
            $ref: '#/components/schemas/ResponseVisit'
    SolverStatus:
      type: object
      description: Solver specific status of optimization
      properties:
        hasLikelyConverged:
          description: Boolean representing whether it is likely that the algorithm has converged and that it is safe to stop the solver.
          type: boolean
        secondsLeftOfOptimization:
          description: Number of seconds left of optimization. If the solver is stopped this field will have the value of seconds left when solver is stopped.
          type: integer
    TotalKeyNumbers:
      type: object
      description: Total key numbers for the complete route planning problem
      required:
        - totalFitness
        - totalTravelTime
        - totalTimeBreakingTimeWindows
        - nonAllocatedTasks
      properties:
        totalFitness:
          description: Number representing the total solution quality. Only comparable to other solutions from the same problem payload and with the same configuration weights.
          type: number
          format: double
        totalTravelTime:
          description: 'The sum of all travel times for all shifts in the solution, given in seconds.'
          type: number
          format: double
        totalTravelDistance:
          description: 'The sum of all travel distances for all shifts in the solution, given in the same distance unit as provided in the problem payload.'
          type: integer
          format: int32
        totalRideSharingCount:
          description: Number of ride-shares used in the solution. One pick-up and drop-off pair counts as one ride-share.
          type: integer
          format: int32
        totalTimeBreakingTimeWindows:
          description: 'The total time used to perform tasks outside of their assigned time window, given in seconds.'
          type: number
          format: double
        nonAllocatedTasks:
          description: All taskIds of tasks not allocated by the solver.
          type: array
          items:
            type: integer
        nonAllocatedTasksTotalDuration:
          description: Total duration of the tasks not allocated by the solver.
          type: number
          format: double
        totalShiftPenaltyScore:
          description: The total penalty in the solution originating from shift penalties.
          type: number
          format: double
        internalVisitHistoryScore:
          description: Score representing how well the solver manages to allocate the tasks in internalVisitHistoryGroups to the same employee.
          type: number
          format: double
    Response:
      type: object
      description: The response from the operational route planner optimizer. Includes key numbers for the complete solution and the routes for each employee shift
      properties:
        totalKeyNumbers:
          description: null
          $ref: '#/components/schemas/TotalKeyNumbers'
        routeKeyNumbers:
          type: array
          items:
            $ref: '#/components/schemas/RouteKeyNumbers'
    StopResponse:
      type: object
      properties:
        solver_stopped:
          type: boolean
    StartResponse:
      type: object
      properties:
        job:
          type: object
          description: Information about the requested optimization job
          properties:
            job_id:
              type: string
            valid:
              type: boolean
            error_messages:
              type: string
    ResponseError:
      type: object
      properties:
        error_messages:
          type: string
    JobStatus:
      type: object
      required:
        - status
      properties:
        status:
          type: string
          description: Represents the status of the solver. It typically takes a couple of second for the solver to go from starting to running
          enum:
            - starting
            - running
            - finished
            - stopped
            - error
        resultsReady:
          type: boolean
          description: Represents wether results are available or not. If this field is not true you should not request GET/result
        solverStatus:
          $ref: '#/components/schemas/SolverStatus'
  securitySchemes:
    visma_connect:
      type: oauth2
      x-tenantEnabled: false
      flows:
        clientCredentials:
          tokenUrl: 'https://connect.visma.com/connect/token'
          scopes: {}
