NAV Navbar
json

Introduction

The Solvers API offers you the power of the MoviSmart planning engine through direct integration with your services.

The MoviSmart planning engine solves a complex variant of the vehicle routing problem. You can provide the algorithm with a set of drivers (and respective vehicles) and tasks which need to be done. The drivers and tasks have several attributes, such as time windows, skills, capacities, etc.

In the remainder of this document you can find instructions on how to use the API key you were provided to call our different API endpoints, as well as the meaning of each and every parameter and their influence on the MoviSmart planning engine.

Functionality and Concepts

In this section we will give you an overview of the functionality provided by the API and the concepts you will need to better understand the optimization process.

The MoviSmart planning engine implements a complex variant of the Vehicle Routing Problem with Time Windows (VRPTW) problem. At its core, the problem involves matching a set of drivers or human resources to a set of task that need to be performed. These tasks can involve simple services, done at the location of the tasks (e.g. "install a router", "cleanup a room") or they can involve the carrying of a kind of item or entity (e.g. "transport a person from Lisbon to Coimbra" or "carry 500 kg of cargo from Porto to Faro". The latter kind of tasks are usually called Shipments, and involve a Pickup (of the specified item/entity) and a Delivery (of the same item/entity). The solution to the problem is a set of routes which detail which tasks each driver must do, in which order, and at what time.

Our variant of the problem is characterized by several different entities, which we will now briefly explain.

Location

Every geographical place in the algorithm is referred to as a Location. For example, every driver/human resource has a location from where they depart when their working day start.

Driver

A driver (sometimes referred to as "user" or "human resource") represents an entity capable of performing tasks. If you are an employer, the drivers are typically your employees, which go on the road with their vehicles. Drivers have several characteristics which influence how they are used in the context of the optimization problem.

Schedule

The driver has a specific availability dictating when they start and end their day's work. These are fixed time windows with no margin for delays or anticipations. A driver with a tight schedule will therefore probably be able to perform less tasks than a driver with more relaxed schedule.

Cost per hour

A driver can have a cost per hour, specified in €/hour. The optimization algorithm prefers to pick drivers with lower costs per hour. Therefore, it is expected that "cheap" drivers usually perform the longest tasks or a larger number of tasks.

Vehicle cost per km

Every driver has their own vehicle, and each vehicle has its own cost per km. While the cost per hour influences the cost of the driver with relation to the length/duration of its work, the cost per km influences the relation between the driver and the distance made in the context of a task. A driver can have a very low cost per hour but still be a poor choice for a task if that task requires them to drive a lot and drive the price up based on the cost per km.

Vehicle capacities and comfort settings

Another property of a driver's vehicle is the set of the vehicle's capacities.

Typical real-world problems involve dealing with sets and compartments of items, people or other entities. For example, depending on your business area, you may need to: (i) carry a number of passengers; (ii) carry a number of bags; (iii) carry a maximum weight, (iv) carry a limited number of produce, etc. In the language of the MoviSmart planning system, these are known as "capacities". A vehicle can have any number of capacities with fixed sizes, and each task can have capacity requirements.

In addition to having fixed sizes for their compartments/capacities (e.g. "This vehicle has room for 4 people" or "This room can carry up to 400kg"), our system allows for specification of comfort values. A comfort value allows you to set the preferred ocupation of a given compartment, which is typically used in the transport area when transporting passengers. Your vehicle may have 4 seats, but it is more comfortable or desirable to keep its usage below 2 seats. On the other hand, for business reasons, it may not be profitable for a vehicle with 8 seats to only fill 1 of them for a long period of time. Thus, you can specify a minimum comfort value and a maximum comfort value which pass this domain-specific knowledge to the algorithm. Note that these examples are not limited to seats, and can apply to any kind of capacity.

Start and end Locations

The algorithm must know where each driver starts and ends their day. The location you provide as a "start" location is where the driver is considered to be at the start of their schedule. So, for example, if you set your drivers in China, but create all your tasks in Portugal, it is very likely that no tasks will be performed, because there is no time to travel the distance between China and Portugal. The start location is mandatory, because every driver must start somewhere.

The "end" location, on the other hand, is an optional attribute which allows you to specify a mandatory place where your driver must end their route. If it is not specified, then the algorithm assumes that the driver's workday ends wherever they were in their last task or stop. If the end is indeed specified, then the algorithm will force it to happen within the driver's schedule. As such, if you set the start to Portugal, and all tasks are in Portugal, but you set the end to China, it is very likely that the driver will have no tasks, because it cannot go to China during the provided schedule.

Limit on the number of working hours

Our version of the VRPTW includes a way to limit the number of hours that a driver can work. This can be extremely useful if your drivers have flexible schedules, or if they do indeed have a limit on the number of hours they can work.

While in theory companies and drivers have an agreement on a fixed number of hours their workers can have (e.g. 8 hours in a day), in practice, and in the real world, this value is often considered in a "softer" approach. In other words, it is usually agreed upon that occasionally the driver can work 8h30m or even 9h. For example, it may happen that the driver is the only one who could perform a crucial task at a given time, and so they could perform it even at the expense of working a bit longer that day. Our algorithm supports this by supplying two kinds of limits:

Note that these limits can also be used as a way to balance a solution: if you force all drivers to work a maximum number of hours, you are more likely to achieve balance and appropriate distribution of work. Note, however, that it is usually recommended to try to use the Limit on the number of jobs first as a solution to unbalancing issues, and only later turn to the limit on the number of working hours.

Limit on the number of maximum driving hours

Apart from allowing to limit the number of hours that a driver can work, one can impose a maximum total number of driving hours. For instance, many companies desire to balance the number of driving hours in order to avoid risky excess of driving hours. Moreover, nearly all countries have laws to impose a maximum number of driving hours within a workday.

Limit on the number of jobs

Similarly to the Limit on the number of working hours, it may make sense, business-wise, to establish a limit on the number of tasks that a driver can perform. This restriction can also be used as a way of achieving an approximation of a balanced solution. If you specify that a user can perform at most 30 jobs, then, regardless of a job's value, associated km and/or time, it will only be done by the driver if they have not done 30 jobs already.

Skills

A driver may possess certain skills that other drivers do not. Similarly, certain tasks may require a set of skills. The application of the skill concept varies from business area to business area. For example, if you work in the IT services area, your drivers can have different skills such as "can repair CISCO router" or "can deal with high-profile clients". Effectively, skills work as a way to filter out the number and kinds of drivers that can perform a given task. You may want to have a task be performed by a set of drivers known as the "Northern Region Driver Team", so you would create a skill with that name and mark each of the drivers having it. You may also have certain operations which require drivers with a specific kind of drivers license, etc.

Task whitelist

It may make sense to directly inform the algorithm of the tasks that a driver can perform. As such, you can specify a task whitelist which explicitly tells the algorithm which tasks the driver can perform. If this parameter is not passed, the algorithm assumes that every task can be done by the driver (after taking into account any schedule, skill and capacity restriction).

Shifts

Some companies operate in a shifts logic, which in turn means that a vehicle returns to depot at the middle of the working day in order to switch driver. Our optimization system supports this, by forcing the first shift's driver to return to depot not later than a maximum hour. As this shift hour is an upper bound, the shift can occur earlier.

When planning with shifts, the driver's Schedule corresponds to the first shift start hour and the second shift end hour. For instance, a schedule from 8am until 8pm means that the first shift starts not earlier than 8am and the second shift ends not later than 8pm. The driver shift occurs within this period of time, fulfilling the maximum allowed shift time.

Task

Generally speaking, a Task represents some work that must be done, somewhere, at some given point in time, with a given length and with certain restrictions on who can do it and which kinds of resources (e.g. litres, kg, seats) are required to do it. In practice, a task represents the work that your drivers have to do, such as picking up people and taking them somewhere, or simply performing a service at some destination.

Like drivers, tasks have different properties.

Type

There are two main types of tasks: shipment and service. Shipments are composed of a pickup and a delivery. Many of the properties of a service exist in the pickup and in the delivery, such as the time-window, the service time an the location. As such, we will refer to a subtask as any of pickup, delivery or service.

As a rule of thumb to knowing which kind of type you should use, ask yourself the following questions:

Service

A service is the "simplest" kind of task, with a single location. There may be a set of required capacities. For example, in order for a driver to perform a routine maintenace, they may need to have a number of parts in their car.

Shipment

A shipment is "similar" to two services that are bound together. It is consisted by a pickup and a delivery. The pickup happens in some location, in some time window, and the delivery happens in another location, at another time window. Any cargo/capacitiy that is picked up at the pickup is kept in the vehicle until the delivery is performed. If either the pickup or the delivery cannot be performed, then neither is done, and the task is left unassigned. Evidently, the pickup and delivery will belong to the same route - it would make no sense for a driver to do the pickup and then have another driver "magically" do the delivery.

Time-window

Tasks can have associated time-windows. In the case of a task of the service type, it has a single time-window. In the case of the shipment, it has two time-windows: one for the pickup and one for the delivery.

The time window dictates the period during which a subtask can be started. Note that it does not specify the window during which it can be performed. For example, you may have a time-window between 08:00 and 08:15 for a task that takes 3 hours to complete. What this means is that it must be started in that 15-minute span, but once it is started, it will take 3 hours to be done.

Service time

Any subtask (service, pickup or delivery) has an associated service time. This is the duration of the subtask, i.e., the time it takes to complete once it has been started. Depending on your business area, this can typically represent the load time at some place, or the time that passengers take to enter the car and put their bags or, even, the time it takes to perform a repair at a given client.

Location

A subtask (service, pickup or delivery) takes place somewhere. You cannot have tasks without location(s). Note that a pickup and delivery can happen in the same place.

Capacities

Capacities are explained in a broader way here. A task can have a set of capacities that the driver must have room for in their vehicle. For example, if you run a taxi service, you could have a task of the type shipment, with a pickup of 3 people (your fares) and the delivery of the same people. The amount of cargo/capacities that are "picked up" is the same amount that is delivered. Note also that several pickups can happen in sequence. For example, if you run a package distribution system, you may pickup several packages along the way and drop them off as you go.

Skills

Skills are explained in a broader way here. A task can have a required set of skills that the driver must have. If the task is a shipment, the skills are the same for the pickup and the delivery. A typical example of a skill is that of a specific driver's license that is required to perform a job, or, for example, a skill such as "is suited for high-profile clients"

Payment

Our version of the VRPTW algorithm supports the (optional) concept of income or payment associated with the conclusion of a task. This allows you to express the monetary gain of a task. While you can provide the algorithm with nothing but the tasks, it might make sense to include their relative "worth" in the form of the "payment". If this value is not specified, the system may not prioritize tasks which have high-income value, for example, because they would take longer to perform or because they would require a lot of traveling.

Priority

You can provide a priority associated with your task. There are two kinds of priority: normal and high. This parameter is used for the algorithm to decide which tasks are more important to assign. For example, if the algorithm has to choose between leaving a normal task unassigned or a high task unassigned, it will prefer to leave the normal one unassigned. This evidently means that the more "high" priority tasks you provide, the less their priority matters - no tasks are prioritary if all tasks are prioritary.

Other optimization settings

There are some additional settings that can be provided to the MoviSmart algorithm that influence the results of the optimization.

Task atomicity (all-or-nothing tasks)

It may make sense to have tasks that work in an "all-or-nothing" fashion, which we call "atomic tasks". For example, you may decide that you have a set of services that must all be performed, and that if one of them is not performed, then neither should the others. This can be achieved by specifying all of these tasks in the here.

Speed factor

Our system has a builtin route optimizer which determines the distance and time between locations, based on the coordinates you provide. However, in practice, your drivers may be able to go faster or slower than the values we calculated. For example, they may go slightly above the legal value, or they may be forced to go below it because they drive mostly slower, heavy-load trucks. To deal with this, you can provide a global speed factor parameter that determines if we should consider the time between two points as actually smaller or larger than reported by our route optimizar.

This value is applied as an inverse multiplier with regards to time. If you want your drivers to go at twice the speed they would otherwise go, then you should pass a speed factor of 0.5 (because they now take half the time to traverse their route). If you want your drivers to go at half the speed, you would pass a speed factor of 2 (because they now take double the time to traverse their route).

Constraint balance and trade-offs

As can be seen in this documentation, the optimization algorithm has to deal with a delicate balance of many different constraints and weights. For example, it must balance the importance of travelling larger distances vs the importance of fulfilling high-rewarding jobs. Similarly, it must also balance the penalty to apply to violations of the target number of maximum working hours or the influence of the violation of comfort settings.

Althought you should not normally have to manually influence these values, you can do so, and we hope to improve upon this fine-tuning in future revisions of the API. At the moment, you can associate a certain weight to three constraints:

Number of iterations

Though you should generally not need to change the number of iterations that our algorithm runs for, you can specify this to try and limit the amount of time it takes to process, which might be particularly useful while you are developing your integration with the API. Please note that we may clamp your number of iterations, to prevent you from providing a value that is too large to process and that could otherwise disrupt the normal operation of the system.

API Endpoints Overview

To make the optimization process simple, the API only provides you with two API endpoints that will be summarized next.

Create Plan

This is the endpoint to start a new optimization process, sending to the API a Plan Object as input. To allow our users to better control the way they can access planning results, we support 3 different types of planning:

Please note that each time you invoke this algorithm (with valid data), the amount of available calls you have in the current billing period (a month) is decreased by one. This topic is covered in greater depth in the authentication section

Check Plan

This endpoint lets you check the status of the optimization process for a given plan and view the results when they are calculated.

Date Formats

There are several objects that require you to pass dates (e.g. start and end date of a task). The dates and times are to be passed as UTC Zulu strings (ie. "2018-06-06T13:43:41Z").

Authentication

The authentication in the API is performed using an API key. This means that you will need a valid API key in order to access all the endpoints. The API key is issued by us and you are responsible to keep it private.

Our users typically have a monthly request limit. That limit is only taken into account when starting new plan optimizations. You can always check plan status and results as long as you have a valid key independently if you exceeded the montly request limit or not.

Using the API key

Any request that you perform requires that you send the key in the query parameter key. For example:

POST /api/plan/?key=<YOUR_API_KEY>

Example error response:

{
    "detail": "API key does not exist."
}

When there is any error related to request authentication, you will be presented with an error message and the HTTP status code 401 (Unauthorized). Possible errors are:

Input Objects

In this section we present you the several data objects that compose the input of the optimization API. We present this in a top down way, where we will start by the entry point of a plan object and go through all deeper objects.

Plan

The Plan object contains all the data required by the API to perform the optimization.

Example Plan object:

{
    "api_version": 1,
    "tasks": [<Task>, <Task>, ...],
    "drivers": [<Driver>, <Driver>, ...],
    "atomic_task_ids": [[1,2], ...],
    "settings": <Settings>
}
Field Type Required Description
api_version Integer yes Version of the API. For now, the only possible version is 1.
tasks Task array yes List of tasks to be optimized
drivers Driver array yes List of drivers available to perform the tasks
atomic_task_ids Array of arrays of integers no List of dependency arrays (atomic tasks). Each dependency array must contain the ids of tasks that must be performed together.
settings Settings no Object containing settings used by the optimizer

Task

The Task object contains all the information related to a single task as previously described here.

Example Task object

{
    "id": 1,
    "name": "Example Task 1",
    "type": "shipment",
    "priority": "normal",
    "payment": 20.410547032375042,
    "capacities": [<Capacity>, <Capacity>, ...],
    "skills": [<Skill>, ...],
    "pickup_data": <SubTask>,
    "delivery_data": <SubTask>
}
Field Type Required Description
id Integer yes Identifier of the task
type String yes Type of the task, as seen before. One of: service, shipment
name String no Name of the task. Used to easily identify a task in human readable form.
priority String no Priority of the task. One of: normal, high
payment Float no Payment for performing the task
capacities Capacity array yes Capacities needed to perform the task, including the value needed for each capacity
skills Skill array no Skills needed to perform the task
driver_id Integer no Id of a driver assigned to the task (it can only be done by them)
pickup_data Sub Task instance if task type is shipment Data related to the pickup
delivery_data Sub Task instance if task type is shipment Data related to the delivery
service_data Sub Task instance if task type is service Data related to the service

Sub Task

Example Sub Task object

{
    "time_window": <TimeWindow>,
    "location": <Location>,
    "service_time": 420
}

Additional data related to a task.

Field Type Required Description
time_window Time Window yes Time period during which the task can be performed
location Location yes Location where the sub task must be performed
service_time Integer yes Duration of the subtask in seconds

Time Window

Example Time Window object

   {
        "start": "2018-05-16T12:00:00.000Z",
        "end": "2018-05-16T15:00:00.000Z"
    }
Field Type Required Description
start Datetime yes Start datetime of the time window
end Datetime yes End datetime of the time window

Driver

Example Driver object

{
    "id": 1,
    "name": "John",
    "cost_per_hour": 5,
    "start_location": <Location>,
    "end_location": null,
    "task_id_whitelist": [],
    "schedule": [<TimeWindow>],
    "shift_max_timestamp": "2018-05-16T12:00:00.000Z",
    "skills": [<Skill>, <Skill>, ...],
    "vehicle": <Vehicle>,
    "max_working_hours": {
        "target": 4,
        "max_allowed": 8
    },
    "max_number_of_jobs": 4
}
Field Type Required Description
id Integer yes Identifier of the driver
name String no Name of the driver
cost_per_hour Float yes Cost per hour of work of the driver
start_location Location yes Location where the driver will start
end_location Location no Location where the driver will end
schedule Time Window array yes List of time windows that represent the time periods when the driver is available to perform tasks
shift_max_timestamp Datetime no Defines a maximum timestamp for the user return to depot in order to give the vehicle to the second shift driver
vehicle Vehicle yes Vehicle used by the driver to perform tasks
skills Skill array yes List of skills that the driver has
max_working_hours object yes Object containing two float/number attributes: target and max_allowed. The target is the ideal number of hours the driver will spend performing tasks and the max_allowed is the maximum number of hours. For more information see here.
max_driving_hours Float/Number no Maximum total number of hours that the user can drive
max_number_of_jobs Integer no Maximum amount of tasks the driver can perform as documented here

Vehicle

Example Vehicle object

{
    "id": 1,
    "name": "John's car",
    "capacities": [<Vehicle Capacity>, <Vehicle Capacity>, ...],
    "cost_per_km": 3.5
}
Field Type Required Description
id Integer yes Identifier of the vehicle
name String no Name of the vehicle
capacities Vehicle Capacity array yes Capacities the vehicle has.
cost_per_km Float or null yes Cost per km of the vehicle. Can be null, but if one vehicle specifies the cost_per_km, all others must also do so.

Settings

Example Settings object

{
    "constraints": <Constraints>,
    "speed_factor": 2.3,
    "num_iterations": 300
}
Field Type Required Description
constraints Constraints no Relative algorithm trade-off weights
speed_factor Float no Described here
num_iterations Integer no Number of iterations the optimizer algorithm will perform as described here

Constraints

Example Constraints object

{
    "PaybackSoft": 1.0,
    "ComfortSoft": 2.0,
    "MaxWorkingHours": 1.0
}
Field Type Required Description
PaybackSoft Float no Described here
ComfortSoft Float no Described here
MaxWorkingHours Float no Described here

Location

Example Location object

{
    "id": 3,
    "name": "Fórum Coimbra",
    "latitude": 40.2116461,
    "longitude": -8.4454925
}
Field Type Required Description
id Integer yes Identifier of the location
name String no Name of the location
latitude Float yes Latitude of the location between -85.05112878 and 85.05112878
longitude Float yes Longitude of the location between -180 and 180

Skill

Example Skill object

{
    "id": 1,
    "name": "Heavy vehicle licence"
}
Field Type Required Description
id Integer yes Identifier of the skill
name String yes Name of the skill
required_in_consecutive_pickups Boolean false Indicates whether consecutive tasks require this skill

Capacity

Example Capacity object

{
    "id": 1,
    "name": "Seats",
    "value": 2,
}
Field Type Required Description
id Integer yes Identifier of the capacity
name String no Name of the capacity
value String yes Value of the capacity

Vehicle Capacity

Example Vehicle Capacity object

{
    "id": 1,
    "name": "Seats",
    "value": 1,
    "comfort_min": 2,
    "comfort_max": 4
}

Has the same attributes as the Capacity plus some external attributes to configure the comfort needs for a vehicle.

Field Type Required Description
id Integer yes Identifier of the capacity
name String no Name of the capacity
value String yes Value of the capacity
comfort_min integer no Described here
comfort_max integer no Described here

Complete input example

{
    "api_version": 1,
    "drivers": [
        {
            "id": 1,
            "name": "John",
            "cost_per_hour": 5,
            "start_location": {
                "id": 1,
                "name": "IPN",
                "latitude": 40.192167,
                "longitude": -8.4163527
            },
            "end_location": null,
            "task_id_whitelist": [],
            "schedule": [
                {
                    "start": "2018-05-16T09:00:00.000Z",
                    "end": "2018-05-16T18:00:00.000Z"
                }
            ],
            "skills": [
                {
                    "id": 1,
                    "name": "Heavy vehicle licence"
                },
                {
                    "id": 2,
                    "name": "Speak english"
                }
            ],
            "vehicle": {
                "id": 1,
                "name": "John's car",
                "capacities": [
                    {
                        "id": 1,
                        "name": "Seats",
                        "value": 1,
                        "comfort_min": 1
                    },
                    {
                        "id": 2,
                        "name": "Luggage places",
                        "value": 1
                    }
                ],
                "cost_per_km": null
            },
            "max_working_hours": {
                "target": 4,
                "max_allowed": 8
            },
            "max_number_of_jobs": 4
        }
    ],
    "tasks": [
        {
            "id": 1,
            "name": "Example Task 1",
            "type": "shipment",
            "priority": "normal",
            "payment": 20.410547032375042,
            "capacities": [
                {
                    "id": 1,
                    "name": "Seats",
                    "value": 1
                },
                {
                    "id": 2,
                    "name": "Luggage places",
                    "value": 1
                }
            ],
            "skills": [
                {
                    "id": 2,
                    "name": "Speak english"
                }
            ],
            "pickup_data": {
                "time_window": {
                    "start": "2018-05-16T09:00:00.000Z",
                    "end": "2018-05-16T12:00:00.000Z"
                },
                "location": {
                    "id": 3,
                    "name": "Fórum Coimbra",
                    "latitude": 40.2116461,
                    "longitude": -8.4454925
                },
                "service_time": 720
            },
            "delivery_data": {
                "time_window": {
                    "start": "2018-05-16T12:00:00.000Z",
                    "end": "2018-05-16T15:00:00.000Z"
                },
                "location": {
                    "id": 4,
                    "name": "Alma Shopping Coimbra",
                    "latitude": 40.2046885,
                    "longitude": -8.4097995
                },
                "service_time": 420
            }
        }
    ],
    "atomic_task_ids": [],
    "settings": {
        "constraints": {
        },
        "speed_factor": 2
    }
}

Output Objects

Plan Result

Example Plan Result object

{
    "drivers": [<Driver>, <Driver>, ...],
    "unassigned_tasks": [<Task>, <Task>, ...]
}
Field Type Description
drivers Driver List of drivers and its assigned tasks
unassigned_tasks Task Array of Task which were not assigned to any driver

Task

Example Task object

{
    "id": 1,
    "type": "shipment",
    "name": "Example Task 1",
    "subtype": "pickup",
    "start_timestamp": null,
    "end_timestamp": null,
    "location": <Location>,
    "cargo":
    "failed_reasons": [<Fail Reason>, ...]
}
Field Type Description
id Integer Identification of the task
name String Name of the task
type String Type of the task. One of (start, end, break, shipment, service)
subtype String Subtype of the task. If the task type is shipment the subtype can be pickup or delivery.
location Location Location where the task is performed
failed_reasons Fail Reason array Statistics from the algorithm which may help explain why a task was left unassigned
start_timestamp Datetime When the task starts to be performed
end_timestamp Datetime When the task is completed

Location

This contains exactly the same properties as the input Location object.

Example Location object

{
    "id": 3,
    "name": "Fórum Coimbra",
    "latitude": 40.2116461,
    "longitude": -8.4454925
}
Field Type Description
id Integer yes
name String no
latitude Float yes
longitude Float yes

Vehicle

Example Vehicle object

{
    "id": 3,
}
Field Type Description
id Integer Identification of the vehicle

Fail Reason

Example Fail Reason object

{
    "reason": "Task time window(s) do not sufficiently overlap driver free time windows",
    "count": 153,
    "percentage": 100,
    "cumulative_percentage": 100
}
Field Type Description
reason String Human readable description of the reason
count Integer How many times the task could not be assigned, during the optimization, because of this reason
percentage Integer The percentage of total failures that are due to this reason
cumulative_percentage Integer A cumulative percentage of the percent of total failures (when in an array)

Full output example

{
    "drivers": [
            {
                "id": 1,
                "name": "John",
                "vehicle": {
                    "id": 1
                },
                "tasks": [
                    {
                        "id": -1,
                        "type": "start",
                        "name": null,
                        "subtype": null,
                        "start_timestamp": null,
                        "end_timestamp": "2018-05-16T09:00:00.000Z",
                        "location": {
                            "id": 1,
                            "name": "IPN",
                            "latitude": 40.192167,
                            "longitude": -8.4163527
                        }
                    },
                    {
                        "id": 2,
                        "type": "shipment",
                        "name": "Example Task 2",
                        "subtype": "pickup",
                        "start_timestamp": "2018-05-16T10:00:00.000Z",
                        "end_timestamp": "2018-05-16T10:12:00.000Z",
                        "location": {
                            "id": 3,
                            "name": "Fórum Coimbra",
                            "latitude": 40.2116461,
                            "longitude": -8.4454925
                        },
                        "failed_reasons": [
                            {
                                "reason": "Vehicle cannot fulfill capacities (shipment)",
                                "count": 30,
                                "percentage": 40,
                                "cumulative_percentage": 40
                            },
                            {
                                "reason": "Task time window(s) do not sufficiently overlap driver free time windows",
                                "count": 45,
                                "percentage": 60,
                                "cumulative_percentage": 100
                            }
                        ]
                    },
                    {
                        "id": -1,
                        "type": "break",
                        "name": null,
                        "subtype": null,
                        "start_timestamp": "2018-05-16T13:00:00.000Z",
                        "end_timestamp": "2018-05-16T14:00:00.000Z",
                        "location": null
                    },
                    {
                        "id": 2,
                        "type": "shipment",
                        "name": "Example Task 2",
                        "subtype": "delivery",
                        "start_timestamp": "2018-05-16T14:00:00.000Z",
                        "end_timestamp": "2018-05-16T14:07:00.000Z",
                        "location": {
                            "id": 4,
                            "name": "Alma Shopping Coimbra",
                            "latitude": 40.2046885,
                            "longitude": -8.4097995
                        },
                        "failed_reasons": [
                            {
                                "reason": "Vehicle cannot fulfill capacities (shipment)",
                                "count": 30,
                                "percentage": 40,
                                "cumulative_percentage": 40
                            },
                            {
                                "reason": "Task time window(s) do not sufficiently overlap driver free time windows",
                                "count": 45,
                                "percentage": 60,
                                "cumulative_percentage": 100
                            }
                        ]
                    },
                    {
                        "id": -1,
                        "type": "end",
                        "name": null,
                        "subtype": null,
                        "start_timestamp": "2018-05-16T14:07:00.000Z",
                        "end_timestamp": "2018-05-16T14:14:50.000Z",
                        "location": null
                    }
                ]
            }
        ],
        "unassigned_tasks": [
            {
                "id": 1,
                "type": "shipment",
                "name": "Example Task 1",
                "subtype": "pickup",
                "start_timestamp": null,
                "end_timestamp": null,
                "location": {
                    "id": 3,
                    "name": "Fórum Coimbra",
                    "latitude": 40.2116461,
                    "longitude": -8.4454925
                },
                "failed_reasons": [
                    {
                        "reason": "Vehicle cannot fulfill capacities (shipment)",
                        "count": 108,
                        "percentage": 60,
                        "cumulative_percentage": 60
                    },
                    {
                        "reason": "Task time window(s) do not sufficiently overlap driver free time windows",
                        "count": 72,
                        "percentage": 40,
                        "cumulative_percentage": 100
                    }
                ]
            }
        ]
    }

API Endpoints

In this section we describe the two endpoints that our API provides. One for creating a new plan optimization and another to check its progress and result after being optimized. When invoking these endpoints, some errors may occur. We document them here.

Create plan

HTTP Request

POST /api/plans

Query Parameters

Parameter Required Description
key yes API key of the client performing the request
type yes Type of the plan: sync, polling or webhook
callback_url if type is webhook URL to post the result of the planning

Body

The body is an object of type Plan Object in JSON format.

Response

Example response to sync planning

{
    "id": "837da5d5a27542afbc911855cfcc582d",
    "started_at": "2018-06-04T10:45:01.529Z",
    "status": "In queue",
    "finished_at": "2018-06-04T10:55:02.622Z",
    "result": <Output Plan Object>
}

Example response to async planning

{
    "id": "837da5d5a27542afbc911855cfcc582d",
    "started_at": "2018-06-04T10:45:01.529Z",
    "status": "In queue",
    "finished_at": null,
    "result": null
}
Field Description
id Identification of the plan
started_at When the plan was created
status The status of the plan, as described here
finished_at An error occurred while planning.
result Result returned by the solvers algorithm. Typically a Plan Result object if the plan was successfully planed, or a object containing an error message if the plan was not successfully planed.

In case of a polling and webhook plan types, only id, started_at and status will be sent. finished_at and result will be available as soon as the plan is completed and by accessing the Check Plan endpoint.

In case of the webhook type, the response body, in full, after planning, is posted to the Callback URL.

Possible planing status

Status Description
In queue The plan was submitted and is queued to be solved
Planning The plan is being planned
Planning success The plan was successfully planned
Planning error An error occurred while planning.
Timeout The planning took too long to process and resulted in a timeout

Get plan details

HTTP Request

GET /api/plans/<plan_id>

Query Parameters

Parameter Description
plan_id Id of the plan previously returned by the Create Plan endpoint
key API key of the client performing the request
Field Description
id Identification of the plan
started_at When the plan was created
status The status of the plan, as described here
finished_at An error occurred while planning.
result Result returned by the solvers algorithm. Typically a Plan Result object if the plan was successfully planed, or an object containing an error message if the plan was not successfully planed.

API Errors

When making requests to the API endpoints (creating or checking a plan), the response can be an error (due to invalid input, planning errors, network errors, etc). In this section we describe the different types of errors that can occur.

When an error happens, we send a response in the format of:

{
    "status": "Error",
    "error": ...,
    "detail": ...,

}

Possible errors are presented in the next table. Some are tangent to all the endpoints, others are specific to a endpoint.

Error HTTP Status Observations
Authentication Error 401 Ensure you have a valid key and you not exceeded your monthly request limit
Invalid JSON object 400 The JSON you submited is not valid JSON
Invalid input 422 The JSON you submitted is valid, but does not implement the specification defined here
Invalid plan type 400 You did not specify one of the available plan types (sync, polling or webhook)
Callback url not provided 400 Plan type is webhook but you did not pass callback_url
Invalid callback url 400 callback_url is not a valid url

Validation Errors

Example of a response with validation errors

 {
    "status": "Error",
    "errors": [
        {
            "error": "'123' is not a 'date-time'",
            "field": "plan.tasks[0].pickup_data.time_window.start"
        },
        {
            "error": "140.2116461 is greater than the maximum of 85.05112878",
            "field": "plan.tasks[0].pickup_data.location.latitude"
        }
    ]
}

While processing your input plan, the API will validate the data to ensure it matches our Plan Object specification. In case it detects any violation of the specification it will return an object with status = Error and a list of error objects.

An error object is composed by the error message and the field it refers to:

{ "error": ERROR_MESSAGE "field": FIELD_NAME }

Planning Errors

Sometimes an unexpected planning error can occur. When that happens, the plan status will be Planning error and the plan result will contain an object similar to this:

{
  "error": "An unexpected pre-algorithm error has occured. Please contact the development team."
}