MRP API Reference
Manual Route Planner API (1)
Download OpenAPI specification:Download
The Manual Route Planner takes in the same input as the Operational Route Planner. In addition it also takes in parts of the output from the GET/result service in Operational Route Planner. The Manual Route Planner allows the user to decide the order of the routes. The key numbers as well as the estimated time of arrival are calculated by the service. The user of the service is also able to request finding the best insertion of a task for a set of employees.
The time definition
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:
- fromTime = 25200, toTime = 28800 (seconds)
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.
Authentication
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 Authorize 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:
1. Log into https://oauth.developers.stagaws.visma.com 2. Click on My applications 3. Create a new Service Application (machine-to-machine) 4. Set a name, client id and description of the application 5. Create a client secret under the section CredentialsWhen you have an application registered in Visma Connect you are now able to apply for an integration against the Optimization Factory APIs:
- Navigate to integrations for the selected application
- Search for the API called optimization-factory-api-prod
- Apply for the scope you would like access to
- 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:
- Send a POST request to https://connect.identity.stagaws.visma.com/connect/token
- 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_secret:
- grant_type: client_credentials
- client_id:
- The token received should be used to request the Optimization Factory API by including the following header
- Authorization: Bearer
- Authorization: Bearer
For more information about Visma Connect OAuth 2.0 Bearer Token Authentication see https://confluence.visma.com/display/PROV/Visma+Connect+OAuth+2.0+Bearer+Token+Authentication
Status codes
- 200 OK
- 202 Accepted - The request has been accepted for processing, but the processing has not been completed
- 400 Bad request/Data validation error
- 403 Forbidden
- 404 Not found
- 429 Too Many Requests
- 500 Internal Error - Unexpected condition was encountered and no more specific message is suitable
Handling 5XX errors
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.
Throttling levels
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.
The user context
Each endpoint require the headers customer_id and user_id. The customer_id header should represent specific customer (company/municipality) that is using API and the user_id 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.
Slack channel for communications
If you have any questions regarding the service. Please reach out to us in the slack channel #optimization-factory-community.
Starts the solver
Authorizations:
header Parameters
user_id required | string Id of the user requesting the optimization job |
Request Body schema: application/jsonrequired
The input request to run the manual route planner. 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 shifts and the order of the tasks.
required | Array of objects (Employee Work Shift) List of employee work shifts. |
required | Array of objects (Task) List of tasks. |
Array of objects (Time Dependent Task Pair) 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. | |
Array of objects (Incompatible Task List Row) 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. | |
required | Array of objects (Task to Shifts Compatibility Matrix Row) Matrix that defines the compatibility between shifts and tasks, i.e. it lists for each task to which shifts it can be assigned. NB: this field's name currently contains a typo which will be fixed in a later release. |
Array of objects (Internal Visit History Group) 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. | |
required | Array of objects (Transport Travel Time Matrix) Travel time matrix that defines the travel time between all locations for (optionally) different transport modes. |
Array of objects (Transport Travel Distance Matrix) Travel distance matrix that defines the travel distance between all locations for (optionally) different transport modes. | |
Array of objects (Shift Overtime) 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. | |
required | object (Configuration) 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. |
object (Configuration for the manual task change) Define what configurations the manual route planner will use during the optimization. | |
object (TotalKeyNumbers) Total key numbers for the complete route planning problem | |
required | Array of objects (RouteKeyNumbers) |
required | Array of objects (Manual Task Change) |
Array of objects (Shift Penalty Matrix) 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. | |
object (Ride-Sharing Configuration) |
Responses
Request samples
- Payload
{- "employeeWorkShifts": [
- {
- "shiftId": 0,
- "employeeId": 0,
- "timeWindow": {
- "fromTime": 0,
- "toTime": 0
}, - "transport": "string",
- "capacity": 0,
- "startLocationId": 0,
- "endLocationId": 0,
- "minimumAmountOfWork": 0,
- "globalEmployeeId": "string"
}
], - "tasks": [
- {
- "taskId": 0,
- "duration": 0,
- "strictTimeWindow": true,
- "timeWindow": {
- "fromTime": 0,
- "toTime": 0
}, - "weight": 0,
- "locationId": 0,
- "requirePhysicalAppearance": true,
- "prioritize": false,
- "isBreak": false
}
], - "timeDependentTaskPairs": [
- {
- "intervalStart": 0,
- "intervalEnd": 0,
- "masterTaskId": 0,
- "dependentTaskId": 0
}
], - "incompatibleTaskList": [
- {
- "taskId": 0,
- "incompatibleTaskIds": [
- 0
]
}
], - "taskShiftsCompatabilityMatrix": [
- {
- "taskId": 0,
- "compatibleShiftIds": [
- 0
]
}
], - "internalVisitHistoryGroups": [
- {
- "tasks": [
- 0
], - "useAsHardRequirement": false
}
], - "travelTimeMatrix": [
- {
- "transport": "string",
- "maxTravelTime": 0,
- "matrix": [
- [
- 0
]
]
}
], - "travelDistanceMatrix": [
- {
- "transport": "string",
- "maxTravelDistance": 0,
- "matrix": [
- [
- 0
]
]
}
], - "shiftOvertimeLists": [
- {
- "shiftId": 0,
- "shiftOvertimeWeight": 1,
- "shiftMaximumOvertime": 0
}
], - "solverConfiguration": {
- "solverRunTimeSeconds": 1,
- "travelTimeWeight": 0,
- "travelDistanceWeight": 0,
- "timeWindowWeight": 0,
- "shiftPenaltyWeights": [
- 0
], - "internalSolutionVisitHistoryWeight": 0,
- "overallOvertimeWeight": 0,
- "useWorkBalance": false,
- "workBalanceWeight": 0,
- "useTooLateArrivalConstraint": false,
- "tooLateArrivalConstraintTimeLimit": 10800,
- "useRideSharing": false
}, - "solverExtraConfiguration": {
- "orpJobId": "string",
- "employeeOvertimeNotAllowedList": [ ]
}, - "existingTotalKeyNumbers": {
- "totalFitness": 0.1,
- "totalTravelTime": 0.1,
- "totalTravelDistance": 0,
- "totalRideSharingCount": 0,
- "totalTimeBreakingTimeWindows": 0.1,
- "nonAllocatedTasks": [
- 0
], - "nonAllocatedTasksTotalDuration": 0.1,
- "totalShiftPenaltyScore": 0.1,
- "internalVisitHistoryScore": 0.1
}, - "existingRouteKeyNumbers": [
- {
- "shiftId": 0,
- "totalTravelTime": 0.1,
- "totalTravelDistance": 0,
- "totalRideSharingCount": 0,
- "totalTimeBreakingTimeWindows": 0.1,
- "totalWeight": 0,
- "endLocationArrivalTime": 0,
- "route": [
- {
- "taskId": 0,
- "fromTime": 0,
- "toTime": 0,
- "travelTimeFromPreviousLocation": 0,
- "travelDistanceFromPreviousLocation": 0,
- "timeDependentStartTime": 0,
- "locationId": 0,
- "rideSharingConfig": {
- "rideShareGroupId": 0,
- "rideShareId": 0,
- "role": "driver",
- "action": "pick_up",
- "relatedRideSharingTasks": [
- {
- "shiftId": 0,
- "taskId": 0
}
]
}
}
]
}
], - "manualTaskChanges": [
- {
- "taskId": 0,
- "shiftId": null,
- "newRouteOrder": [ ],
- "moveToUnallocated": false
}
], - "shiftPenaltyMatrices": [
- {
- "shiftPenaltyMatrix": [
- {
- "taskId": 0,
- "shiftPenaltyList": [
- {
- "shiftId": 0,
- "shiftPenalty": 0
}
]
}
]
}
], - "rideSharingConfiguration": {
- "rideSharingTransportModes": [
- {
- "transport": "string",
- "role": "driver",
- "driverProperties": {
- "maxPassengerCount": 1,
- "delay": 0
}
}
], - "rideSharingEmployeeWorkShifts": [
- {
- "shiftId": 0,
- "driverProperties": {
- "maxPassengerCount": 1,
- "delay": 0
}, - "employeeWorkShiftCompatibilities": [
- {
- "shiftId": 0,
- "compatibilityWeight": 1
}
]
}
]
}
}
Response samples
- 200
- 400
- 403
- 500
{- "job_id": "string",
- "valid": true,
- "error_messages": "string",
- "result": {
- "routesUpdated": true,
- "totalKeyNumbers": {
- "totalFitness": 0.1,
- "totalTravelTime": 0.1,
- "totalTravelDistance": 0,
- "totalRideSharingCount": 0,
- "totalTimeBreakingTimeWindows": 0.1,
- "nonAllocatedTasks": [
- 0
], - "nonAllocatedTasksTotalDuration": 0.1,
- "totalShiftPenaltyScore": 0.1,
- "internalVisitHistoryScore": 0.1
}, - "routeKeyNumbers": [
- {
- "shiftId": 0,
- "totalTravelTime": 0.1,
- "totalTravelDistance": 0,
- "totalRideSharingCount": 0,
- "totalTimeBreakingTimeWindows": 0.1,
- "totalWeight": 0,
- "endLocationArrivalTime": 0,
- "route": [
- {
- "taskId": 0,
- "fromTime": 0,
- "toTime": 0,
- "travelTimeFromPreviousLocation": 0,
- "travelDistanceFromPreviousLocation": 0,
- "timeDependentStartTime": 0,
- "locationId": 0,
- "rideSharingConfig": {
- "rideShareGroupId": 0,
- "rideShareId": 0,
- "role": "driver",
- "action": "pick_up",
- "relatedRideSharingTasks": [
- {
- "shiftId": 0,
- "taskId": 0
}
]
}
}
]
}
]
}
}