Reference

Routing app API

Technical reference for the Nextmv routing app API

The Nextmv routing app is a pre-built, feature-rich routing application built on the Nextmv router engine. This reference includes the API specification and input and output schemas.

A demo of the Nextmv routing app is available via the console. Note, some defaults or options may differ from the API (see the input schema below). You can navigate to the demo page after logging in to your Nextmv account.

API Key

Your Nextmv API key can be copied from under your account profile after logging in to the Nextmv Cloud console.

Endpoints

Nextmv Cloud API provides you with endpoints to run a model, check the status of your run, get the results of a run, and configure runs and integrations.

Note, all requests must be authenticated with Bearer Authentication. Make sure your request has a header containing your Nextmv Cloud API key, as such:

  • Key: Authorization
  • Value: Bearer <YOUR-API-KEY>
Authorization: Bearer <YOUR-API-KEY>
Copy

Run

POSThttps://api.cloud.nextmv.io/v0/run

Create a new run.

Request a new run to the Nextmv Cloud API. You can use a `run_profile` to specify configurations and integrations that do not vary run to run. Use the `profiles` endpoints to create and manage run profiles.

GEThttps://api.cloud.nextmv.io/v0/run/{run_id}/status

Get a run status.

Poll the status of the run requested with the `/v0/run` endpoint, using the `run_id` obtained. If the status is `succeeded`, it means that the solution can be queried using the `/v0/run/{run_id}/result` endpoint.

GEThttps://api.cloud.nextmv.io/v0/run/{run_id}/result

Get the result of the run.

Get the result of the run requested with the `/v0/run` endpoint, using the `run_id` obtained. The solution can be found under the `state` key.

Configurations

POSThttps://api.cloud.nextmv.io/v1/routing/profiles/run

Create a new run profile.

Create a new run profile that can be used when executing runs using the `v0/run` endpoint. A run profile is used to configure options that do not vary run to run. Different run profiles may be created to fulfill distinct run configurations. A run profile uses one or more third-party integrations for enhanced functionality such as using traffic data.

GEThttps://api.cloud.nextmv.io/v1/routing/profiles/run

List all run profiles.

List the existing run profiles.

GEThttps://api.cloud.nextmv.io/v1/routing/profiles/run/{profile_id}

Get a specific run profile.

Fetch a specific run profile.

PUThttps://api.cloud.nextmv.io/v1/routing/profiles/run/{profile_id}

Modify a run profile.

Modify an existing run profile.

DELETEhttps://api.cloud.nextmv.io/v1/routing/profiles/run/{profile_id}

Delete a run profile.

Delete an existing run profile.

GEThttps://api.cloud.nextmv.io/v1/routing/profiles/integration

List all third-party run profile integrations.

List the existing third-party integrations that can be used with a run profile. Integrations enhance the functionality of the Nextmv Cloud API.

Onfleet integration

POSThttps://api.cloud.nextmv.io/v1/routing/profiles/integration/onfleet

Create a new Onfleet integration.

Create a new Onfleet integration. Onfleet is the source of the truth for tasks (stops) and workers (vehicles). Entities are fetched from the Onfleet API and containers (assignments) are created for the user to upload them to the Onfleet API. Use the `id` of the integration to manage it (using the `GET`, `PUT` and `DELETE` endpoints that follow).

GEThttps://api.cloud.nextmv.io/v1/routing/profiles/integration/onfleet/{integration_id}

Get an existing Onfleet integration.

Get an existing Onfleet integration using the `id` associated with it.

PUThttps://api.cloud.nextmv.io/v1/routing/profiles/integration/onfleet/{integration_id}

Modify an existing Onfleet integration.

Modify an existing Onfleet integration using the `id` associated with it.

DELETEhttps://api.cloud.nextmv.io/v1/routing/profiles/integration/onfleet/{integration_id}

Delete an existing Onfleet integration.

Delete an existing Onfleet integration using the `id` associated with it.

POSThttps://api.cloud.nextmv.io/v1/integrations/onfleet/run

Create a new run specific for Onfleet tasks and workers.

Request a new run to the Nextmv Cloud API, specific for Onfleet tasks and workers. Similar to the `v0/run` endpoint, you will obtain a `run_id` that you can use to query your status and results using the `v0/status` and `v0/result` endpoints, respectively. Make sure that you have an existing Onfleet integration configured and a run profile that uses that integration. The tasks and workers that are referenced must exist in the Onfleet API.

HERE integration

POSThttps://api.cloud.nextmv.io/v1/routing/profiles/integration/here

Create a new HERE integration.

Create a new HERE integration. HERE is a mapping provider offering street data to estimate the distance and duration of going from A to B. HERE has the possibility to incorporate real-time or historical traffic data in their estimations.

GEThttps://api.cloud.nextmv.io/v1/routing/profiles/integration/here/{integration_id}

Get an existing HERE integration.

Get an existing HERE integration using the `id` associated with it.

PUThttps://api.cloud.nextmv.io/v1/routing/profiles/integration/here/{integration_id}

Modify an existing HERE integration.

Modify an existing HERE integration using the `id` associated with it.

DELETEhttps://api.cloud.nextmv.io/v1/routing/profiles/integration/here/{integration_id}

Delete an existing HERE integration.

Delete an existing HERE integration using the `id` associated with it.

Streets integration

POSThttps://api.cloud.nextmv.io/v1/routing/profiles/integration/streets

Create a new Streets integration.

Create a new Streets integration. The integration uses Open Street Map (OSM) road network to estimate the distance of going from A to B. It relies on an OSRM (Open Source Routing Machine) server.

GEThttps://api.cloud.nextmv.io/v1/routing/profiles/integration/streets/{integration_id}

Get an existing Streets integration.

Get an existing Streets integration using the `id` associated with it.

PUThttps://api.cloud.nextmv.io/v1/routing/profiles/integration/streets/{integration_id}

Modify an existing Streets integration.

Modify an existing Streets integration using the `id` associated with it.

DELETEhttps://api.cloud.nextmv.io/v1/routing/profiles/integration/streets/{integration_id}

Delete an existing Streets integration.

Delete an existing Streets integration using the `id` associated with it.

Input schema

The input schema is a JSON payload defining the vehicles, stops, and options for a given routing problem. Nextmv's tools are designed to operate directly on business data (in JSON) to produce decisions that are actionable by software systems. This makes decisions more interpretable and easier to test. It also makes integration with data warehouses and business intelligence platforms significantly easier. An input contains the following components:

PropertyRequiredField nameTypeDescription
VehiclesYesvehiclesarray of objectVehicles to route.
StopsYesstopsarray of objectStops that will be routed and assigned to the vehicles.
Run profileNorun_profileobjectProfile for configurations that do not vary run to run.
OptionsNooptionsobjectOptions to configure the solver.
DefaultsNodefaultsobjectDefault properties for vehicles and stops.
Stop groupsNostop_groupsarray of array of stringGroup of stops that must be part of the same route.
Alternate stopsNoalternate_stopsarray of objectAlternate locations that a vehicle can visit.
Duration groupsNoduration_groupsarray of objectGroup of stops that add an overall service time.

A sample input is provided below.

{
  "defaults": {
    "vehicles": {
      "start": { "lon": -96.65922, "lat": 33.12274 },
      "end": { "lon": -96.65922, "lat": 33.12274 },
      "shift_start": "2021-10-17T09:00:00-06:00",
      "shift_end": "2021-10-17T11:00:00-06:00",
      "speed": 10
    },
    "stops": {
      "stop_duration": 120,
      "unassigned_penalty": 200000
    }
  },
  "vehicles": [
    {
      "id": "vehicle-1",
      "capacity": 4
    },
    {
      "id": "vehicle-2",
      "capacity": 5
    }
  ],
  "stops": [
    {
      "id": "location-1",
      "position": { "lon": -96.75038, "lat": 33.2058 },
      "quantity": -1
    },
    {
      "id": "location-2",
      "position": { "lon": -96.54613, "lat": 33.2259 },
      "quantity": -1
    },
    {
      "id": "location-3",
      "position": { "lon": -96.61059, "lat": 33.23528 },
      "quantity": -1
    },
    {
      "id": "location-4",
      "position": { "lon": -96.61356, "lat": 33.20379 },
      "quantity": -1
    },
    {
      "id": "location-5",
      "position": { "lon": -96.64137, "lat": 33.178801 },
      "quantity": -1
    },
    {
      "id": "location-6",
      "position": { "lon": -96.83157, "lat": 33.02896 },
      "quantity": -1
    },
    {
      "id": "location-7",
      "position": { "lon": -96.62951, "lat": 33.0867 },
      "quantity": -1
    },
    {
      "id": "location-8",
      "position": { "lon": -96.86007, "lat": 33.08133 },
      "quantity": -1
    },
    {
      "id": "location-9",
      "position": { "lon": -96.87346, "lat": 33.092841 },
      "quantity": -1
    },
    {
      "id": "location-10",
      "position": { "lon": -96.79586, "lat": 33.10492 },
      "quantity": -1
    }
  ],
  "stop_groups": [
    ["location-1", "location-3"],
    ["location-2", "location-4", "location-7"]
  ],
  "options": {
    "solver": {
      "limits": {
        "duration": "5s"
      },
      "diagram": {
        "expansion": {
          "limit": 1
        }
      }
    }
  }
}
Copy

Vehicles

vehicles define all vehicles in a fleet. Available vehicle property fields and type requirements are detailed in the table below. Besides being a mandatory field, the id must be unique. Except for id, required fields can be set as defaults instead of being set for each vehicle.

Note, the field alternate_stops references stops by ID in the alternate_stops object, not the stops object.

PropertyRequiredField nameTypeExample
Name / identifierYesidstring"id": "vehicle-1"
Average speedYesspeedfloat [meters/second]"speed": 25
Shift startYesshift_starttimestamp"shift_start": "2021-10-17T09:00:00-06:00"
Shift endNoshift_endtimestamp"shift_end": "2021-10-17T11:00:00-06:00"
Starting positionNostartobject"start": { "lon":-96.659222, "lat": 33.122746 }
Ending positionNoendobject"end": { "lon": -96.659222, "lat": 33.122746 }
Carrying capacityNocapacityobject or int"capacity": { "weight": 500 } or "capacity": 500
Compatibility attributesNocompatibility_attributesarray of string"compatibility_attributes": ["refrigerated"]
Max stopsNomax_stopsint"max_stops": 1
Max distanceNomax_distanceint [meters]"max_distance": 50000
Max durationNomax_durationint [seconds]"max_duration": 3600
Stop duration multiplierNostop_duration_multiplierfloat"stop_duration_multiplier": 1.2
BacklogNobacklogarray of string"backlog": ["location-1"]
Alternate stopsNoalternate_stopsarray of string"alternate_stops": ["as-1"]
Initialization costNoinitialization_costint"initialization_cost": 34000

Here is an example of vehicles:

{
    "vehicles": [
        {
            "id": "vehicle-1",
            "capacity": 100,
            "speed": 20
        },
        {
            "id": "vehicle-2",
            "capacity": 50
        }
    ]
}
Copy

Stops

stops define all stops (or requests) that are candidates for assignment. Just like id for vehicles, id for stops must be unique. Available stop property fields and type requirements are detailed in the table below.

PropertyRequiredField nameTypeExample
Name / identifierYesidstring"id": "order-1-pickup"
Stop locationYespositionobject"position": { "lon": -96.827094, "lat": 33.004745 }
Stop durationNostop_durationint [seconds]"stop_duration": 120
Quantity picked up (-) or dropped off (+)Noquantityobject or int"quantity": { "weight": -50 } or "quantity": -50
Target timeNotarget_timetimestamp"target_time": "2021-10-17T09:45:00-06:00"
Hard windowNohard_windowarray of timestamp"hard_window": [ "2021-10-17T09:00:00-06:00", "2021-10-17T10:00:00-06:00" ]
Max wait timeNomax_waitint [seconds]"max_wait": 900
Penalty for being earlyNoearliness_penaltyfloat"earliness_penalty": 2
Penalty for being lateNolateness_penaltyfloat"lateness_penalty": 5
Penalty for not visitingNounassigned_penaltyint"unassigned_penalty": 200000
Precedence - visit beforeNoprecedesarray of string or string"precedes": ["order-1-drop", "order-2-drop"] or "precedes": "order-1-drop"
Precedence - visit afterNosucceedsarray of string or string"succeeds": ["order-1-drop", "order-2-drop"] or "succeeds": "order-1-drop"
Compatibility attributesNocompatibility_attributesarray of array of string"compatibility_attributes": ["refrigerated"]

Here is an example of stops:

{
    "stops": [
        {
            "id": "location-1",
            "position": { "lon": -96.71038, "lat": 33.20580 },
            "quantity": -27
        },
        {
            "id": "location-2",
            "position": { "lon": -96.65613, "lat": 33.22591 },
            "quantity": -30
        }
    ]
}
Copy

Run profile

run_profile is a single JSON object with an id attribute specifying a previously configured run profile. Settings for the profile are merged with the input for the run. Additional integration-specific fields may be added to the run profile. Please note that third-party integrations are configured as part of a run profile. For more information on available integrations, please contact support.

PropertyRequiredField nameTypeExample
Name / identifierYesidstring"id": "<MY-RUN-PROFILE-ID>"

Here is an example of a run_profile:

{
    "run_profile": { 
      "id": "<MY-RUN-PROFILE-ID>"
    }
}
Copy

Options

Solver options can also be added to the input file. Note, in the Nextmv Cloud console, options added to an input file will override options configured using the graphical user interface (e.g., a duration specified in an input file will override a duration set using the runtime slider). Available option fields and type requirements are detailed in the table below.

PropertyRequiredField nameTypeExampleDefault (limits)Description
Time limitNodurationstring"solver": { "limits": { "duration": "5s" } }API: 3s (600s limit); Console: 3s (30s limit)The length of time a solver must run.
Solution limitNosolutionsint"solver": { "limits": { "solutions": 1 } }model.MaxIntThe number of feasible solutions that must be found before stopping or reaching a time limit if specified.
Node limitNonodesint"solver": { "limits": { "nodes": 10 } }model.MaxIntThe number of nodes that must be generated before stopping or reaching a time limit if specified.
Diagram expansion limitNoexpansionint"solver": { "diagram": { "expansion": { "limit": 1 } }1The number of child nodes to generate on each pass.

Here is an example of custom options:

{
  "options": {
    "solver": {
      "limits": {
        "duration": "5s",
        "solutions": 1,
        "nodes": 10
      },
      "diagram":{
        "expansion": {
          "limit": 1
        }
      }
    }
  }
}
Copy

Defaults

defaults apply default properties to all vehicles, stops and alternate stops. Properties added to specific vehicles or stops override the default settings (see vehicles and stops). Here is an example of using defaults.

{
    "defaults": {
        "vehicles": {
            "start": { "lon": -96.65922, "lat": 33.12274 },
            "end": { "lon": -96.65922, "lat": 33.12274 },
            "shift_start": "2021-10-17T09:00:00-06:00",
            "speed": 10  
        },
        "stops": {
            "stop_duration": 120
        }
    }
}
Copy

Stop groups

stop_groups define a list of stops to be grouped together. Each group must consist of at least 2 stops, given by their IDs. Here is an example of using stop_groups:

{
    "stop_groups": [
      [
        "stop1", 
        "stop2"
      ],
      [
        "stop3", 
        "stop4", 
        "stop5"
      ]
  ]
}
Copy

Alternate stops

alternate_stops defines a list of stops. An alternate stop can be referenced by a vehicle by using the alternate_stops property. In addition to the stops already assigned to the vehicle, one alternate stop will be assigned from all referenced alternate stops. An alternate stop has the same fields as a normal stop. Here is an example of using alternate_stops:

{
    "alternate_stops": [
        {
          "id": "as-1",
          "position": { "lon": -96.71038, "lat": 33.20580 },
        },
        {
          "id": "as-2",
          "position": { "lon": -96.65613, "lat": 33.22591 },
        }
    ]
}
Copy

Duration groups

duration_groups define groups of stops that would take on an additional duration cost if any stop in the group is approached from a location outside of it. Any individual stop durations will be applied as usual. Two fields are required for each duration group: group and duration. A location must not be repeated within a group and must not be part of several groups. The table below defines the schema for a duration group.

PropertyRequiredField nameTypeExample
Stop groupYesgrouparray of string"group": ["location-1"]
DurationYesdurationint [seconds]"duration": 120

Here is an example of using duration_groups:

{
    "duration_groups": [
        {
            "group": ["location-4", "location-3", "location-2", "location-1"],
            "duration": 300
        },
        {
            "group": ["location-5", "location-6", "location-7"],
            "duration": 600
        }
    ]
}
Copy

Output schema

The output schema defines the solution to the routing problem, in addition to the options that were used and summary statistics, all in JSON format. The output schema contains the following components.

PropertyField nameTypeDescription
Hop versionhopstringThe version of the Nextmv solver (Hop) used.
OptionsoptionsobjectThe options defined in the input file and used by the solver.
SolutionstateobjectThe solution to the problem.
StatisticsstatisticsobjectSummary statistics of the search for the best solution.

Here, we focus on the state object containing a solution. The state contains the following components:

PropertyField nameTypeDescription
Vehicles' solutionvehiclesarray of objectVehicles with assigned routes.
Unassigned stopsunassignedarray of objectStops not assigned to any vehicle (if any).
Value summaryvalue_summaryobjectBreakdown of a vehicle's solution.

A sample output.json is provided below.

{
  "hop": {
    "version": "v0.12.2-dev.5"
  },
  "options": {
    "diagram": {
      "expansion": {
        "limit": 1
      },
      "restrictor": {
        "type": "github.com/nextmv-io/code/hop/solve/diagram/restrict.value"
      },
      "width": 10
    },
    "limits": {
      "duration": "5s"
    },
    "search": {
      "buffer": 100,
      "queuer": {
        "type": "github.com/nextmv-io/code/hop/solve/queue.depth"
      },
      "searcher": {
        "type": "github.com/nextmv-io/code/hop/solve/search.local"
      }
    },
    "sense": "min",
    "tags": {
      "assigner": {
        "type": "default"
      },
      "measure": [
        {
          "costs": [
            120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 0, 0, 0, 0
          ],
          "measure": {
            "measure": {
              "type": "haversine"
            },
            "scale": 0.1,
            "type": "scale"
          },
          "type": "location"
        },
        {
          "costs": [
            120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 0, 0, 0, 0
          ],
          "measure": {
            "measure": {
              "type": "haversine"
            },
            "scale": 0.1,
            "type": "scale"
          },
          "type": "location"
        }
      ],
      "router": {
        "type": "default"
      }
    }
  },
  "state": {
    "vehicles": [
      {
        "id": "vehicle-1",
        "value": 4397,
        "travel_distance": 39171.492,
        "travel_time": 4398.1393,
        "vehicle_initialization_costs": 0,
        "route": [
          {
            "id": "vehicle-1-start",
            "lon": -96.65922,
            "lat": 33.12274,
            "distance": 0,
            "eta": "2021-10-17T09:00:00-06:00",
            "ets": "2021-10-17T09:00:00-06:00"
          },
          {
            "id": "location-7",
            "lon": -96.62951,
            "lat": 33.0867,
            "distance": 4870.107,
            "eta": "2021-10-17T09:08:07-06:00",
            "ets": "2021-10-17T09:08:07-06:00"
          },
          {
            "id": "location-2",
            "lon": -96.54613,
            "lat": 33.2259,
            "distance": 22185.575,
            "eta": "2021-10-17T09:38:58-06:00",
            "ets": "2021-10-17T09:38:58-06:00"
          },
          {
            "id": "location-4",
            "lon": -96.61356,
            "lat": 33.20379,
            "distance": 28923.041,
            "eta": "2021-10-17T09:52:12-06:00",
            "ets": "2021-10-17T09:52:12-06:00"
          },
          {
            "id": "location-5",
            "lon": -96.64137,
            "lat": 33.178801,
            "distance": 32720.099000000002,
            "eta": "2021-10-17T10:00:32-06:00",
            "ets": "2021-10-17T10:00:32-06:00"
          },
          {
            "id": "vehicle-1-end",
            "lon": -96.65922,
            "lat": 33.12274,
            "distance": 39171.492,
            "eta": "2021-10-17T10:13:18-06:00",
            "ets": "2021-10-17T10:13:18-06:00"
          }
        ]
      },
      {
        "id": "vehicle-2",
        "value": 7092,
        "travel_distance": 64917.50300000001,
        "travel_time": 7091.7143,
        "vehicle_initialization_costs": 0,
        "route": [
          {
            "id": "vehicle-2-start",
            "lon": -96.65922,
            "lat": 33.12274,
            "distance": 0,
            "eta": "2021-10-17T09:00:00-06:00",
            "ets": "2021-10-17T09:00:00-06:00"
          },
          {
            "id": "location-10",
            "lon": -96.79586,
            "lat": 33.10492,
            "distance": 12879.361,
            "eta": "2021-10-17T09:21:27-06:00",
            "ets": "2021-10-17T09:21:27-06:00"
          },
          {
            "id": "location-8",
            "lon": -96.86007,
            "lat": 33.08133,
            "distance": 19410.864,
            "eta": "2021-10-17T09:34:21-06:00",
            "ets": "2021-10-17T09:34:21-06:00"
          },
          {
            "id": "location-9",
            "lon": -96.87346,
            "lat": 33.092841,
            "distance": 21198.172000000002,
            "eta": "2021-10-17T09:39:19-06:00",
            "ets": "2021-10-17T09:39:19-06:00"
          },
          {
            "id": "location-1",
            "lon": -96.75038,
            "lat": 33.2058,
            "distance": 38199.990000000005,
            "eta": "2021-10-17T10:09:40-06:00",
            "ets": "2021-10-17T10:09:40-06:00"
          },
          {
            "id": "location-3",
            "lon": -96.61059,
            "lat": 33.23528,
            "distance": 51610.36000000001,
            "eta": "2021-10-17T10:34:01-06:00",
            "ets": "2021-10-17T10:34:01-06:00"
          },
          {
            "id": "vehicle-2-end",
            "lon": -96.65922,
            "lat": 33.12274,
            "distance": 64917.50300000001,
            "eta": "2021-10-17T10:58:11-06:00",
            "ets": "2021-10-17T10:58:11-06:00"
          }
        ]
      }
    ],
    "unassigned": [
      {
        "id": "location-6",
        "lon": -96.83157,
        "lat": 33.02896
      }
    ],
    "value_summary": {
      "value": 211489,
      "total_travel_distance": 104088.99500000001,
      "total_travel_time": 11489.853599999999,
      "total_unassigned_penalty": 200000,
      "total_vehicle_initialization_costs": 0
    }
  },
  "statistics": {
    "bounds": {
      "lower": -9223372036854775808,
      "upper": 211489
    },
    "search": {
      "generated": 423,
      "filtered": 0,
      "expanded": 423,
      "reduced": 0,
      "restricted": 423,
      "deferred": 171,
      "explored": 416,
      "solutions": 7
    },
    "time": {
      "elapsed": "504.32388ms",
      "start": "2022-03-15T20:25:10.263857297Z"
    },
    "value": 211489
  }
}
Copy

Vehicles output

The vehicles key contains the route per vehicle and different fields with statistics for that vehicle. The components of a vehicle's solution are described in the following table.

PropertyField nameTypeExampleDescription
Name / identifieridstring"id": "vehicle-1"ID of the vehicle.
Valuevalueint"value": 5745Costs associated with this vehicle.
Total travel distancetravel_distancefloat [meters]"travel_distance": 51450.103837858944Total distance traveled by this vehicle.
Total travel timetravel_timefloat [seconds]"travel_time": 5745.074947478Total travel time for this vehicle.
Initialization costvehicle_initialization_costsint"vehicle_initialization_costs": 0The one-time costs that are applied for using this vehicle.
Polylinepolylinestring"polyline": "??gotiEjpomQ??fotiEkpomQ"A polyline from start to end encoded in Google's polyline format.
Routeroutearray of object"route": {"id":"location-1", "lon": -96.7503, "lat": 33.2058 "distance": 12542.225,"eta": "2022-02-22T09:20:54:06-00","ets": "2022-02-22T09:20:54:06-00"}, {...}, ...]The vehicle's route with stops and additional stop information.

The components for a vehicle's route are described in the following table.

PropertyField nameTypeExampleDescription
Name / identifieridstring"id": "location-1"ID of the stop.
Longitudelonfloat"lon": -96.659222Longitude of the stop.
Latitudelatfloat"lat": 33.122746Latitude of the stop.
Accumulated distancedistancefloat [meters]"distance": 0Total route distance up to this stop.
Polylinepolylinestring"polyline": "styrFv~xiM??"A polyline from this stop to the next stop, encoded in Google's polyline format.
Estimated time of arrivaletatimestamp"eta": "2021-10-17T09:00:00-06:00"Estimated time at which the vehicle will arrive at this stop.
Estimated time of departureetdtimestamp"etd": "2021-10-17T09:00:00-06:00"Estimated time at which the vehicle will depart from this stop.
Estimated time of serviceetstimestamp"ets": "2021-10-17T09:00:00-06:00"When using time windows there are potential wait times when arriving before the window opens. This is the estimated time at which service at the stop starts.

Here is an example of the solution for a vehicle.

{
  "vehicles": [
    {
      "id": "vehicle-1",
      "polyline": "??gotiEjpomQ??fotiEkpomQ",
      "travel_distance": 48659.494999999995,
      "travel_time": 5346.0749,
      "value": 5346,
      "vehicle_initialization_costs": 0,
      "route": [
        {
          "id": "vehicle-1-start",
          "lon": -96.659222,
          "lat": 33.122746,
          "distance": 0,
          "eta": "2021-10-17T09:00:00-06:00",
          "etd": "2021-10-17T09:00:00-06:00",
          "ets": "2021-10-17T09:00:00-06:00",
          "polyline": "styrFv~xiM??"
        },
        {
          "id": "location-1",
          "lon": -96.75038245222623,
          "lat": 33.20580830033956,
          "distance": 12542.225,
          "eta": "2021-10-17T09:20:54-06:00",
          "etd": "2021-10-17T09:20:54-06:00",
          "ets": "2021-10-17T09:20:54-06:00"
        }
      ]
    }
  ]
}
Copy

Unassigned output

The unassigned key holds an array of unassigned stops (objects). Each unassigned stop has the following fields:

PropertyField nameTypeExample
Name / identifieridstring"id": "location-1"
Longitudelonfloat"lon": -96.659222
Latitudelatfloat"lat": 33.122746

Here is an example of how unassigned stops look like in the output.

{
  "unassigned": [
    {
      "id": "location-3",
      "lon": -96.61059803136642,
      "lat": 33.23528740544529
    }
  ]
}
Copy

Summary output

The value_summary key holds summary statistics of the overall solution, like the total distance traveled and the value of the solution. The components of the value summary are described below.

PropertyField nameTypeExample
Valuevalueint"value": 211264
Total travel distancetotal_travel_distancefloat [meters]"total_travel_distance": 101843.988
Total travel timetotal_travel_timefloat [seconds]"total_travel_time": 11265.113
Total earliness penaltytotal_earliness_penaltyfloat [seconds]"total_earliness_penalty": 0
Total lateness penaltytotal_lateness_penaltyfloat [seconds]"total_lateness_penalty": 0
Total unassigned penaltytotal_unassigned_penaltyint [seconds]"total_unassigned_penalty": 200000
Total vehicle initialization coststotal_vehicle_initialization_costsint [seconds]"total_vehicle_initialization_costs": 0

Here is an example of how the value_summary looks like in the output.

{
  "value_summary": {
    "value": 211264,
    "total_travel_distance": 101843.988,
    "total_travel_time": 11265.113,
    "total_earliness_penalty": 0,
    "total_lateness_penalty": 0,
    "total_unassigned_penalty": 200000,
    "total_vehicle_initialization_costs": 0
  }
}
Copy

Statistics

This section details the schema in the statistics object for each solution returned by the solver.

PropertyField nameTypeDescription
Solution boundsboundsobjectBounds for the value of the solution.
Solution state explorationsearchobjectSummary of the solver's search mechanism.
Solution time statisticstimeobjectTime it took for the solver to find the solution.
Solution valuevalueintTotal value of the solution.

The bounds key follows the schema described below.

PropertyField nameTypeDescription
Lower boundlowerintLower bound of the solution.
Upper boundupperintUpper bound of the solution.

In cases where the Nextmv solver is maximizing or minimizing an objective, the upper and lower bounds are continuously updated after generating a new state.

  • If the bounds are the same in value, Nextmv solver has mathematically proven optimality of its best solution.
  • When bounds are not the same value, Nextmv solver terminated early while searching the state space proving optimality. In that case, Nextmv solver will return the best solution found to date.

The search object shows the solver's search mechanism, which is a state exploration. It projects states forward from the root state to determine what decisions you should make. Every transition in that search produces a new state which is then explored. Each of these categories contain both feasible and infeasible states.

PropertyField nameTypeDescription
Generated statesgeneratedintAll of the states the solver created.
Filtered statesfilteredintAll states that have been removed from the search because they have been bounded out. The solver has determined that it cannot, from a certain set of solutions, produce a solution that will be better than the best solution found so far.
Expanded statesexpandedintAll states that are not 'Filtered'
Reduced statesreducedintStates that have been removed from the search because a 'Reducer' determined that no states directly dominate them.
Deferred statesdeferredintStates that have not been deferred and will be investigated immediately.
Restricted statesrestrictedintStates saved for later exploration. This is how the solver manages an explosion of the state space. It will explore the most fruitful options first, then defer the others.
Explored statesexploredintAll states that have been fully explored. Explored states cannot produce more child states.
Total solutionssolutionsintNumber of solutions that the solver has found thus far.

Lastly, the time object shows the time it took for the solver to find the solution shown.

PropertyField nameTypeDescription
Elapsed timeelapsedstringHuman-readable elapsed time to find the solution.
Elapsed secondselapsed_secondsfloatTotal seconds to find the solution.
Run start timestarttimestampTimestamp indicating when the search began.

Below is an example of output statistics for a solver result.

{
  "statistics": {
    "bounds": {
      "lower": -9223372036854776000,
      "upper": 211264
    },
    "search": {
      "generated": 10,
      "filtered": 0,
      "expanded": 10,
      "reduced": 0,
      "restricted": 10,
      "deferred": 10,
      "explored": 0,
      "solutions": 1
    },
    "time": {
      "elapsed": "1.152495ms",
      "elapsed_seconds": "0.001152495",
      "start": "2022-03-02T14:17:59.295985184Z"
    },
    "value": 211264
  }
}
Copy

Onfleet integration input schema

The input schema for the Onfleet integration is a JSON payload that has the following keys:

KeyDescriptionRequiredType
routingOptions to customize the routing configurations.Yesobject
onfleetSpecification of tasks and workers to route.Yesobject

A sample input is provided as a .json file.

{
  "routing": {
    "run_profile": {
      "id": "onfleet-test"
    },
    "options": {
      "solver": {
        "limits": {
          "duration": "30s"
        }
      }
    },
    "defaults": {
      "vehicles": {
        "start": {
          "lon": -73.9799724,
          "lat": 40.7593752
        },
        "shift_start": "2021-10-17T09:00:00-06:00",
        "shift_end": "2021-10-17T11:00:00-06:00",
        "speed": 10
      },
      "stops": {
        "stop_duration": 120,
        "unassigned_penalty": 200000
      }
    }
  },
  "onfleet": {
    "tasks": {
      "ids": ["zntJ8QG5LPBK9mMea6zWN0lB", "cisx30t05*kQQiKYQhgyTmVF"],
      "params": {
        "from": 1641963600000
      }
    },
    "workers": {
      "ids": ["aWXnPY3NjKSOdisBihX8QPsL"],
      "params": {}
    },
    "stop_groups": [["zntJ8QG5LPBK9mMea6zWN0lB", "cisx30t05*kQQiKYQhgyTmVF"]],
    "alternate_stops": ["zntJ8QG5LPBK9mMea6zWN0lB"]
  }
}
Copy

Routing options

The routing options is an object that has the following keys:

KeyDescriptionRequiredType
run_profileObject to select the run profile id you want to use.Yesobject
optionsSelect the solver options that you want to use.Noobject
defaultsSpecify the default override attributes for stops and vehicles.Noobject

A sample routing object is provided as part of the input schema below.

Onfleet specification

The Onfleet specification is an object that has the following keys:

KeyDescriptionRequiredType
tasksObject to specify the tasks you want to route.Yesobject
workersObject to specify the workers you want to route.Yesobject
stop_groupsArray to specify custom stop groups for the routes.Noarray of array of string
alternate_stopsArray of task IDs that are considered alternate stops.Noarray of string
duration_groupsGroup of stops that add an overall service timeNoarray of object
  • Use of alternate stops with Onfleet does involve additional steps to assure that exactly 1 alternate stop is assigned per vehicle's route. When using alternate_stops it is mandatory to create the alternate_stops as tasks per worker in Onfleet before calling the Nextmv API. E.g., if you have two locations that are alternate_stops and you have 5 workers, you need to create 2*5=10 tasks. You can then call the Nextmv API. Once you have the Nextmv results with the calculated routes, the unused alternate_stops need to be deleted from Onfleet. This can be done by looping over the routes and identifying which alternate_stops appear there. The alternate_stops that are not found in the routes should be removed from Onfleet and the containers can be updated.

The tasks object is composed of the following keys:

KeyDescriptionRequiredSample valueType
idsList of IDs to fetch from the Onfleet API and perform the routing.Yes[ "zntJ8QG5LPBK9mMea6zWN0lB", "cisx30t05*kQQiKYQhgyTmVF" ]array of string
paramsObject to specify the query parameters when fetching tasks.Yes{ "from": 1641963600000 }object
  • When using stop_groups, please include the list of task IDs in both the list of task ids and the list of IDs in a group inside stop_groups.
  • Please include the IDs of alternate stops in both the list of task ids and the list of alternate_stops. This is handled differently than in the original alternate stops feature, where alternate stops are only specified under the alternate_stops key but not under stops. Keep in mind that alternate_stops are specified only using task IDs, not a stop object.

The tasks params object specifies most query parameters used in the Onfleet list tasks endpoint. This endpoint is used in the backend to search for the given task ids. All the query parameters should be specified using snake_case.

The lastId query parameter is not provided, as the pagination is done automatically by the Nextmv Cloud API. Please note that there is a limit to the number of pages that are walked over. If the page limit is reached and there are tasks that haven't been found, an error will be returned in the /v0/run/run_id/status endpoint.

We recommend using the query parameters to fine-tune the search of the desired tasks. The params object is composed of the following keys:

KeyDescriptionRequiredSample valueType
fromThe starting time of the range. Tasks created or completed at or after this time will be included.Yes1641963600000int
toIf missing, defaults to the current time. The ending time of the range. Tasks created or completed before this time will be included.No1641963600000int
stateA comma-separated list of task states.No"0,1"string
workerThe ID of a worker who is currently assigned to the tasks or has previously completed the tasks.No"aWXnPY3NjKSOdisBihX8QPsL"string
complete_before_beforeThe timestamp before which the task completeBefore value must be.No1641963600000int
complete_after_afterThe timestamp after which the task completeAfter value must be.No1641963600000int
dependenciesA comma-separated list of dependency task ids. A task gets returned in the response if it has at least one of the dependencies provided.No"zntJ8QG5LPBK9mMea6zWN0lB,cisx30t05*kQQiKYQhgyTmVF"string

The workers object is composed of the following keys:

KeyDescriptionRequiredSample valueType
idsList of IDs to fetch from the Onfleet API and perform the routing.Yes[ "aWXnPY3NjKSOdisBihX8QPsL" ]array of string
paramsObject to specify the query parameters when fetching workers.No{ "states": "0,1" }object

The workers params object specifies most query parameters used in the Onfleet list workers endpoint. This endpoint is used in the backend to search for the given worker ids.

We recommend using the query parameters to fine-tune the search of the desired workers. The params object is composed of the following keys:

KeyDescriptionRequiredSample valueType
filterA comma-separated list of fields to return, if all are not desired. For example, name, location.No"name,location"string
teamsA comma-separated list of the team IDs that workers must be part of.No"aWXnPY3NjKSOdisBihX8QPsL"string
statesA comma-separated list of worker states, where 0 is off-duty, 1 is idle (on-duty, no active task) and 2 is active (on-duty, active task).No"0,1"string
phonesA comma-separated list of workers' phone numbers.No"9055555555"string

Onfleet integration output schema

The output schema is a JSON response that has the following keys:

KeyDescriptionType
workersList of worker objects fetched from the Onfleet API.array of object
tasksList of task objects fetched from the Onfleet API.array of object
containersList of containers specifying the final assignments. You can use this information to update the containers using the Onfleet API.array of object
solutionNexmtv Cloud output schema with solution.object
inputParsed Nexmtv Cloud input schema used to run the optimization. Showcases the transformations of tasks to stops and workers to vehiclesobject

A sample output response obtained after running the sample input is provided below.

{
  "workers": [
    {
      "id": "aWXnPY3NjKSOdisBihX8QPsL",
      "timeCreated": 1642004684000,
      "timeLastModified": 1642004684618,
      "organization": "PAQ~oFrxgtHk95qKtlxfpyjB",
      "name": "Nicole",
      "phone": "+19055525459",
      "tasks": [],
      "onDuty": false,
      "capacity": 0,
      "userData": {},
      "accountStatus": "INVITED",
      "metadata": [],
      "teams": ["HP6ydtpsRv7IL2drNjt7Osan"]
    }
  ],
  "tasks": [
    {
      "id": "zntJ8QG5LPBK9mMea6zWN0lB",
      "timeCreated": 1642004288000,
      "timeLastModified": 1642104097673,
      "organization": "PAQ~oFrxgtHk95qKtlxfpyjB",
      "shortId": "0f2bc8b2",
      "trackingURL": "https://onf.lt/0f2bc8b22a",
      "merchant": "PAQ~oFrxgtHk95qKtlxfpyjB",
      "executor": "PAQ~oFrxgtHk95qKtlxfpyjB",
      "creator": "9JWaPBE~H*V*xiMmKVj4G4xp",
      "dependencies": [],
      "state": 0,
      "pickupTask": false,
      "notes": "",
      "completionDetails": {
        "events": [],
        "failureReason": "NONE",
        "firstLocation": [0, 0],
        "lastLocation": [0, 0]
      },
      "feedback": [],
      "metadata": [],
      "overrides": {},
      "container": {
        "organization": "PAQ~oFrxgtHk95qKtlxfpyjB",
        "type": "ORGANIZATION"
      },
      "quantity": 0,
      "serviceTime": 2,
      "trackingViewed": false,
      "destination": {
        "id": "iUr1xPH1DORARWNh9VgKzy9p",
        "timeCreated": 1642004288000,
        "timeLastModified": 1642004288196,
        "location": [-73.9799726, 40.7593755],
        "address": {
          "name": "Rockefeller Plaza",
          "apartment": "",
          "city": "New York",
          "state": "New York",
          "postalCode": "10112",
          "country": "United States"
        },
        "notes": "",
        "metadata": []
      },
      "recipients": [
        {
          "id": "prMwZu4Yxtd*FvLUvvVEGfxP",
          "organization": "PAQ~oFrxgtHk95qKtlxfpyjB",
          "timeCreated": 1641835991000,
          "timeLastModified": 1642103905493,
          "name": "Bruce Wayne",
          "phone": "+19055555555",
          "notes": "",
          "skipSmsNotifications": false,
          "metadata": []
        }
      ],
      "scanOnlyRequiredBarcodes": false,
      "appearance": {}
    },
    {
      "id": "cisx30t05*kQQiKYQhgyTmVF",
      "timeCreated": 1642006615000,
      "timeLastModified": 1642006615880,
      "organization": "PAQ~oFrxgtHk95qKtlxfpyjB",
      "shortId": "896f2c1b",
      "trackingURL": "https://onf.lt/896f2c1b91",
      "merchant": "PAQ~oFrxgtHk95qKtlxfpyjB",
      "executor": "PAQ~oFrxgtHk95qKtlxfpyjB",
      "creator": "9JWaPBE~H*V*xiMmKVj4G4xp",
      "dependencies": [],
      "state": 0,
      "pickupTask": false,
      "notes": "",
      "completionDetails": {
        "events": [],
        "failureReason": "NONE",
        "firstLocation": [0, 0],
        "lastLocation": [0, 0]
      },
      "feedback": [],
      "metadata": [
        {
          "name": "nextmv:1:unassigned_penalty",
          "type": "number",
          "visibility": ["api"],
          "value": 123123123
        }
      ],
      "overrides": {},
      "container": {
        "organization": "PAQ~oFrxgtHk95qKtlxfpyjB",
        "type": "ORGANIZATION"
      },
      "quantity": 0,
      "serviceTime": 0,
      "trackingViewed": false,
      "destination": {
        "id": "r1HQS9JB~eYj*0GZ2p*iEOkz",
        "timeCreated": 1642006615000,
        "timeLastModified": 1642006615729,
        "location": [-73.9799726, 40.7593755],
        "address": {
          "name": "Rockefeller Plaza",
          "apartment": "",
          "city": "New York",
          "state": "New York",
          "postalCode": "10112",
          "country": "United States"
        },
        "notes": "",
        "metadata": []
      },
      "recipients": [
        {
          "id": "prMwZu4Yxtd*FvLUvvVEGfxP",
          "organization": "PAQ~oFrxgtHk95qKtlxfpyjB",
          "timeCreated": 1641835991000,
          "timeLastModified": 1642103905493,
          "name": "Bruce Wayne",
          "phone": "+19055555555",
          "notes": "",
          "skipSmsNotifications": false,
          "metadata": []
        }
      ],
      "scanOnlyRequiredBarcodes": false,
      "appearance": {}
    }
  ],
  "containers": [
    {
      "type": "WORKER",
      "tasks": ["zntJ8QG5LPBK9mMea6zWN0lB", "cisx30t05*kQQiKYQhgyTmVF"],
      "worker": "aWXnPY3NjKSOdisBihX8QPsL"
    }
  ],
  "solution": {
    "hop": {
      "version": "v0.12.1"
    },
    "options": {
      "diagram": {
        "expansion": {
          "limit": 0
        },
        "restrictor": {
          "type": "github.com/nextmv-io/code/hop/solve/diagram/restrict.value"
        },
        "width": 10
      },
      "limits": {
        "duration": "30s"
      },
      "search": {
        "buffer": 100,
        "queuer": {
          "type": "github.com/nextmv-io/code/hop/solve/queue.depth"
        },
        "searcher": {
          "type": "github.com/nextmv-io/code/hop/solve/search.local"
        }
      },
      "sense": "min",
      "tags": {
        "assigner": {
          "type": "default"
        },
        "measure": [
          {
            "type": "streets"
          }
        ],
        "router": {
          "type": "default"
        }
      }
    },
    "state": {
      "vehicles": [
        {
          "id": "aWXnPY3NjKSOdisBihX8QPsL",
          "value": 2,
          "travel_distance": -0.1,
          "travel_time": 2,
          "route": [
            {
              "id": "aWXnPY3NjKSOdisBihX8QPsL-start",
              "lon": -73.9799724,
              "lat": 40.7593752,
              "distance": 0,
              "eta": "2021-10-17T09:00:00-06:00",
              "ets": "2021-10-17T09:00:00-06:00"
            },
            {
              "id": "zntJ8QG5LPBK9mMea6zWN0lB",
              "lon": -73.9799726,
              "lat": 40.7593755,
              "distance": -0.1,
              "eta": "2021-10-17T09:00:00-06:00",
              "ets": "2021-10-17T09:00:00-06:00"
            },
            {
              "id": "cisx30t05*kQQiKYQhgyTmVF",
              "lon": -73.9799726,
              "lat": 40.7593755,
              "distance": -0.1,
              "eta": "2021-10-17T09:00:02-06:00",
              "ets": "2021-10-17T09:00:02-06:00"
            }
          ]
        }
      ],
      "unassigned": [],
      "value_summary": {
        "value": 2,
        "total_travel_distance": -0.1,
        "total_travel_time": 2,
        "total_unassigned_penalty": 0
      }
    },
    "statistics": {
      "bounds": {
        "lower": -9223372036854776000,
        "upper": 2
      },
      "search": {
        "deferred": 0,
        "expanded": 2,
        "explored": 1,
        "filtered": 0,
        "generated": 2,
        "reduced": 0,
        "restricted": 2,
        "solutions": 1
      },
      "time": {
        "elapsed": "398.397µs",
        "start": "2022-01-13T20:17:29.428320628Z"
      },
      "value": 2
    }
  },
  "input": {
    "options": {
      "solver": {
        "limits": {
          "duration": "30s"
        },
        "measure": {
          "osrmHost": "http://Stagi-Stagi-1ACNIMH171YXY-46fc9af96e361694.elb.us-east-2.amazonaws.com",
          "type": "streets"
        }
      }
    },
    "defaults": {
      "vehicles": {
        "start": {
          "lon": -73.9799724,
          "lat": 40.7593752
        },
        "speed": 10,
        "shift_start": "2021-10-17T09:00:00-06:00",
        "shift_end": "2021-10-17T11:00:00-06:00"
      },
      "stops": {
        "unassigned_penalty": 200000,
        "stop_duration": 120
      }
    },
    "vehicles": [
      {
        "id": "aWXnPY3NjKSOdisBihX8QPsL",
        "capacity": {
          "onfleet": 0
        }
      }
    ],
    "stops": [
      {
        "id": "cisx30t05*kQQiKYQhgyTmVF",
        "position": {
          "lon": -73.9799726,
          "lat": 40.7593755
        },
        "unassigned_penalty": 123123123,
        "quantity": {
          "onfleet": 0
        },
        "stop_duration": 0
      }
    ],
    "stop_groups": [["zntJ8QG5LPBK9mMea6zWN0lB", "cisx30t05*kQQiKYQhgyTmVF"]],
    "alternate_stops": [
      {
        "id": "zntJ8QG5LPBK9mMea6zWN0lB",
        "position": {
          "lon": -73.9799726,
          "lat": 40.7593755
        },
        "quantity": {
          "onfleet": 0
        },
        "stop_duration": 120
      }
    ]
  }
}
Copy

Page last updated

Go to on-page nav menu