Skip to content

Optimize API

Unmap Optimize assigns shipments to vehicles and orders their stops. A shipment is a pickup, a delivery, or a paired pickup and delivery. The solver looks for a low-cost plan that respects your time windows, load capacities, driver breaks and route limits, and returns one route per vehicle.

The endpoint is live at POST https://api.unmap.dev/optimize. It accepts the Google Maps Platform Route Optimization OptimizeToursRequest format and returns an OptimizeToursResponse. REST integrations can use Unmap's host and API key, subject to the supported fields and behaviour differences. Unmap is not affiliated with or endorsed by Google.

Use Optimize for fleet assignment or planning with time windows, capacities or breaks. For the stop order of a single vehicle without those constraints, use /optimized-route.

To try a request without writing code, open the Optimize playground: paste a request or pick one of the worked examples, run it with your key, and see every route on a map with its cost, times and skipped shipments.

Your first request

This example assigns two parcels to one van in downtown Calgary. It uses straight-line travel at 10 metres per second, so it does not depend on the road network. Remove useGeodesicDistances and geodesicMetersPerSecond to plan with road travel times instead. Set UNMAP_API_KEY for curl, or key for TypeScript, to your API key. Set the model's global start and end times to the day you want to plan.

curl -X POST "https://api.unmap.dev/optimize" \
  -H "X-API-Key: $UNMAP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "useGeodesicDistances": true,
    "geodesicMetersPerSecond": 10,
    "model": {
      "globalStartTime": "2026-10-13T15:00:00Z",
      "globalEndTime": "2026-10-14T03:00:00Z",
      "shipments": [
        { "label": "parcel-1", "deliveries": [{ "arrivalLocation": { "latitude": 51.0478, "longitude": -114.0593 } }] },
        { "label": "parcel-2", "deliveries": [{ "arrivalLocation": { "latitude": 51.0375, "longitude": -114.0814 } }] }
      ],
      "vehicles": [
        { "label": "van-1", "startLocation": { "latitude": 51.0447, "longitude": -114.0719 }, "costPerKilometer": 1 }
      ]
    }
  }'

The response below comes from a local run of this request through the same pipeline and solver. Visit order and timings may vary between runs. The solver uses a time budget to search for a lower-cost plan.

// 200, with X-Unmap-Billable-Shipments: 2
{
  "routes": [
    {
      "vehicleIndex": 0,
      "vehicleLabel": "van-1",
      "vehicleStartTime": "2026-10-13T15:00:00Z",
      "vehicleEndTime": "2026-10-13T15:04:47Z",
      "visits": [
        { "shipmentIndex": 0, "isPickup": false, "visitRequestIndex": 0, "startTime": "2026-10-13T15:01:35Z", "detour": "0s", "shipmentLabel": "parcel-1" },
        { "shipmentIndex": 1, "isPickup": false, "visitRequestIndex": 0, "startTime": "2026-10-13T15:04:47Z", "detour": "183s", "shipmentLabel": "parcel-2" }
      ],
      "transitions": [
        { "travelDuration": "95s", "travelDistanceMeters": 946, "waitDuration": "0s", "breakDuration": "0s", "delayDuration": "0s", "totalDuration": "95s", "startTime": "2026-10-13T15:00:00Z" },
        { "travelDuration": "192s", "travelDistanceMeters": 1923, "waitDuration": "0s", "breakDuration": "0s", "delayDuration": "0s", "totalDuration": "192s", "startTime": "2026-10-13T15:01:35Z" },
        { "travelDuration": "0s", "travelDistanceMeters": 0, "waitDuration": "0s", "breakDuration": "0s", "delayDuration": "0s", "totalDuration": "0s", "startTime": "2026-10-13T15:04:47Z" }
      ],
      "metrics": {
        "performedShipmentCount": 2,
        "travelDuration": "287s",
        "waitDuration": "0s",
        "delayDuration": "0s",
        "breakDuration": "0s",
        "visitDuration": "0s",
        "totalDuration": "287s",
        "travelDistanceMeters": 2869
      },
      "routeCosts": { "model.vehicles.cost_per_kilometer": 2.869 },
      "routeTotalCost": 2.869
    }
  ],
  "metrics": {
    "aggregatedRouteMetrics": {
      "performedShipmentCount": 2,
      "travelDuration": "287s",
      "waitDuration": "0s",
      "delayDuration": "0s",
      "breakDuration": "0s",
      "visitDuration": "0s",
      "totalDuration": "287s",
      "travelDistanceMeters": 2869
    },
    "skippedMandatoryShipmentCount": 0,
    "usedVehicleCount": 1,
    "earliestVehicleStartTime": "2026-10-13T15:00:00Z",
    "latestVehicleEndTime": "2026-10-13T15:04:47Z",
    "costs": { "model.vehicles.cost_per_kilometer": 2.869 },
    "totalCost": 2.869
  }
}
  • routes: one per vehicle, in request order. visits is the order to drive, each with the time service starts. transitions has one more entry than visits: the legs before each visit and the leg to the vehicle's end.
  • The van has no endLocation, so its route ends at its last stop and the final transition has zero travel time and distance. Set endLocation to bring it back to the depot.
  • metrics.totalCost includes route costs and penalties for skipped optional shipments. Here it is costPerKilometer multiplied by the distance travelled. Costs use the units you choose and do not represent API charges. Response timestamps are RFC 3339 in UTC; durations are strings in seconds, such as "95s".
  • Unserved shipments appear in skippedShipments; reasons may be absent when no specific cause can be identified. A shipment without penaltyCost is mandatory unless ignore: true. If the solver cannot serve all mandatory shipments, the request fails with 400 FAILED_PRECONDITION.

Endpoints

POST /optimize
POST /v1/projects/{project}:optimizeTours
POST /v1/projects/{project}/locations/{location}:optimizeTours
POST /projects/{project}:optimizeTours
POST /projects/{project}/locations/{location}:optimizeTours

All five paths accept the same request and return the same response format. Use /optimize for new integrations. The other four support existing Google Route Optimization REST integrations. {project} and {location} are ignored, as is parent in the body, which produces a warning. The API key identifies the account that runs and pays for the request. Only the :optimizeTours method exists; any other method suffix is a 404 NOT_FOUND.

Send the key as Authorization: Bearer <key>, X-API-Key, X-Goog-Api-Key or ?key=. Keep the key out of the URL on server calls. See Authentication.

The body is JSON, at most 4 MiB (a larger one is a 413). Field names may be camelCase or snake_case; the response is camelCase. Every response the Optimize handler writes, a solve or one of its errors, carries Cache-Control: no-store; a refusal from the key check in front of it, such as a 401, carries the gateway's usual headers instead.

Travel times

A request takes its travel times from one of three places:

  • The road network (the default). Driving times and distances from the same Canada road graph as Routing, at free-flow speeds. considerRoadTraffic is accepted and ignored, with a warning when set to true. Stops must be near roads in Canada, and each vehicle must be able to reach its assigned stops. Local routing on an island can work even when the island has no road connection to the mainland. An unreachable mandatory shipment fails the request with 400 FAILED_PRECONDITION; an unreachable optional shipment is skipped. Road travel supports at most 200 distinct locations per request, or the plan's lower location limit. For larger requests, use straight-line travel or your own matrices. Two stops more than 1,000 km apart in a straight line are treated as unreachable from each other, and a request spread that widely can be refused as too large for one solve; split it by region.
  • Straight lines. useGeodesicDistances: true with geodesicMetersPerSecond (at least 1): great-circle distance at a fixed speed. It works anywhere, including outside Canada.
  • Your own matrices. model.durationDistanceMatrices with durationDistanceMatrixSrcTags and durationDistanceMatrixDstTags. Every stop and vehicle is then placed by tags instead of coordinates, and one matrix per vehicleStartTag gives different vehicles different travel.

Vehicles drive (travelMode DRIVING). Unmap adds one field to the vehicle, unmapProfile, which names the road profile; auto is the only value today.

With populatePolylines: true, Unmap attempts to add routePolyline.points to each used route. It is an encoded polyline at precision 5, drawn along Canadian roads. If drawing fails or runs out of time, the route remains valid and returns without a polyline, with a ROUTE_POLYLINE_UNAVAILABLE warning. For geodesic requests, a road polyline does not change the straight-line travel times or distances used to plan the route. Requests with your own matrices have no coordinates and return no polylines.

What it plans

Supported today:

  • Shipments with one pickup, one delivery, or both (a pair is served by one vehicle, pickup first), each visit with a location, a service duration and hard timeWindows.
  • Loads: loadDemands on the shipment against loadLimits (maxLoad) on the vehicle, by load type name.
  • Optional shipments: penaltyCost adds a cost when a shipment is skipped. The solver may skip it to reduce total cost, to satisfy constraints, or because it cannot find a plan that includes it within its time budget. ignore: true excludes a shipment from planning, with no penalty.
  • allowedVehicleIndices to restrict which vehicles may serve a shipment.
  • Vehicles with start and end locations (or open ends), one start and one end time window, fixedCost, costPerHour, costPerTraveledHour and costPerKilometer.
  • Hard routeDurationLimit, travelDurationLimit and routeDistanceLimit.
  • Soft time windows on visits and on a vehicle's start and end (softStartTime, softEndTime and their costs per hour), soft route and travel duration limits, a soft route distance limit and a soft maximum load, each reported under its own key in metrics.costs.
  • Driver breaks: breakRule.breakRequests, each with an earliest and latest start and a minimum duration.
  • Labels on the request, shipments, visits and vehicles, echoed in the response.
  • searchMode: RETURN_FAST stops improving soon after a first plan, CONSUME_ALL_AVAILABLE_TIME uses the whole timeout.

Enum fields accept the name or its integer, as Google's clients send them ("travelMode": 1).

Not yet: quadratic soft duration limits, more than one pickup or delivery alternative per shipment, more than one start or end window per vehicle, shipment types and precedence rules, injected solutions, and walking. Each refuses with FIELD_NOT_SUPPORTED_YET or VALUE_NOT_SUPPORTED_YET rather than being silently dropped, because a plan that ignored one of your constraints would be wrong without telling you. Like quadratic soft duration limits, routeDistanceLimit.costPerKilometerBelowSoftMax is refused, but for good and with INVALID_VALUE: GMPRO does not support it either. A Google place ID is refused too: geocode the address with /geocode/search and send coordinates. Field support lists each field and its status.

Validate without solving

Send "solvingMode": "VALIDATE_ONLY" to check a request. Nothing is fetched or solved, and it is never billed. A valid request answers 200 with {}. An invalid one answers 200 with every problem found, up to maxValidationErrors (default 100):

// 200
{
  "validationErrors": [
    {
      "code": 11,
      "displayName": "MISSING_REQUIRED_FIELD",
      "fields": [{ "name": "geodesicMetersPerSecond" }],
      "errorMessage": "geodesicMetersPerSecond is required when useGeodesicDistances is true."
    }
  ]
}

In the default solve mode, these problems return 400 INVALID_ARGUMENT, with one field violation per error. VALIDATE_ONLY checks the request's fields and constraints; it does not check road reachability or whether the fleet can serve every shipment together. Authentication, body parsing, body size and shipment or vehicle count limits still apply and can return non-200 responses before validation.

Limits per plan

PlanShipmentsVehiclesLocationsLongest solveSolves at once
Hobby25510010 s1
Indie5005050030 s2
Growth2,0002001,00060 s2
  • Shipments counts every entry in model.shipments, ignored ones included. Locations is the distinct places travel is needed between, after duplicates are merged. A request over a count is a 400 INVALID_ARGUMENT that names the limit; split it into smaller requests.
  • On every plan, a request may use at most 16 distinct load type names across all its loadDemands and loadLimits keys. One more is a 400 INVALID_ARGUMENT (OUT_OF_RANGE).
  • Travel matrices may contain at most the plan's location limit squared in total cells: 10,000 for Hobby, 250,000 for Indie and 1,000,000 for Growth. Multiple vehicle matrices share this limit. The 4 MiB body limit also applies to supplied matrices.
  • Longest solve caps timeout. A longer one is clamped, with a Warning: 299 - "timeout clamped to 30s for this plan" header and a TIMEOUT_CLAMPED warning. With no timeout, the solve gets min(cap, max(2, ceil(visits / 25))) seconds, where visits are the pickups and deliveries in the request.
  • Solves at once is per account, across all its keys. One more is a 429 RESOURCE_EXHAUSTED with Retry-After.
  • Road travel and the solver's response share a 140-second budget. A stage that exceeds its allotted time returns 504 DEADLINE_EXCEEDED. Optional polyline generation has an additional 30 seconds, for a total budget of up to 170 seconds. A missing polyline never fails the solve.

These are starting values, set before the service has been measured under production load, and may change.

Pricing

One performed shipment is one call from your plan's included calls, the same pool tiles, geocoding and routing draw on. A performed shipment is one that is visited on a route in a successful (200) solve. Past the included calls, Indie and Growth bill overage at their usual per-call price, and Hobby stops. See Plans & Limits.

Free:

  • VALIDATE_ONLY, whatever it finds.
  • Every response that is not a 200: validation errors, limits, timeouts, failures.
  • Every shipment in skippedShipments, ignored or not served.
  • A shipment re-solved within 24 hours of the solve that first billed it.

Unlike other endpoints, Optimize bills a request from a dev origin (localhost, *.pages.dev, *.workers.dev) like any other. The free re-solve above keeps iterating on the same request cheap.

Re-solves. Dispatchers re-plan when a window moves or a driver calls in sick, and those are still the same deliveries. So a shipment with a label is recognised by its label plus its visit locations, and one without a label by its whole content. Within 24 hours of the solve that first billed it, the same shipment is free; the window is not extended by a re-solve. A recycled label on different stops is a new shipment. When the request brings its own matrices, the identity also covers the matrices and each vehicle's startTags and endTags, so new travel data, or a vehicle switched to another matrix, bills again.

The X-Unmap-Billable-Shipments response header reports the billable count, including 0. Before road travel or solving, Unmap checks whether the remaining quota or spend cap covers every new, non-ignored shipment in the request. Optional shipments count in this estimate even if the solver might skip them. If the estimate exceeds the available calls, the request returns 429 RESOURCE_EXHAUSTED with a top-level code of quota_exceeded or spend_cap.

Re-solve discounts apply across an account's keys. Duplicate shipments in one request count as separate units. If the discount lookup is unavailable after a successful solve, all performed shipments count as billable; check X-Unmap-Billable-Shipments for the actual count.

Response headers

HeaderWhenWhat
X-Unmap-Billable-Shipmentsevery 200Shipments this request billed.
X-Unmap-Optimize-Warningswhen there is a warningA JSON array of { code, field?, path?, count, message }, ASCII only, at most 4 KB.
Warningtimeout was clamped299 - "timeout clamped to <n>s for this plan".
Retry-Afterretryable 429 and 503Seconds to wait.

Warnings never change the response body, which stays the shape a Google client expects. The codes are FIELD_IGNORED (an accepted field Unmap cannot honour), TIMEOUT_CLAMPED, ROAD_TRAVEL_DEGRADED and ROAD_TRAVEL_UNCONFIRMED (a routing budget, not the road network, decided that some cells were unreachable, so a skipped stop may be servable; split the request), ROUTE_POLYLINE_UNAVAILABLE, and WARNINGS_TRUNCATED. A browser can read all four headers: the gateway exposes them through CORS.

Errors

Errors on these paths use Google's error shape, with the gateway's string code beside it, so a Google client and an Unmap client both read them. The status is in error.status (INVALID_ARGUMENT, FAILED_PRECONDITION, RESOURCE_EXHAUSTED and so on), and validation errors list one fieldViolations entry per problem, with the exact field path. Where Google may answer 200 with a mandatory shipment skipped, Unmap refuses with 400 FAILED_PRECONDITION.

Errors has the full body, every status, validation code, warning and skip reason, with the cause and the fix.

From the client library

npm i @unmap/routing

optimizeTours() in @unmap/routing sends the key in X-API-Key and returns the Unmap response in Google's format, alongside the parsed response headers. It ships in @unmap/routing and @unmap/sdk from 0.21.0.

import { optimizeTours, UnmapOptimizeError } from "@unmap/routing";
 
try {
  const { response, warnings, billableShipments, timeoutClamped } = await optimizeTours(
    {
      timeout: "10s",
      model: {
        shipments: [{ label: "order-1", deliveries: [{ arrivalLocation: { latitude: 51.05, longitude: -114.07 } }] }],
        vehicles: [{ label: "van-1", startLocation: { latitude: 51.04, longitude: -114.06 } }],
      },
    },
    { key },
  );
  console.log(response.routes, warnings, billableShipments, timeoutClamped);
} catch (e) {
  if (e instanceof UnmapOptimizeError) console.error(e.status, e.googleStatus, e.details);
}
  • The same call is router.optimizeTours(request) on a Router, and unmap.router.optimizeTours(request) from @unmap/sdk.
  • warnings is the parsed X-Unmap-Optimize-Warnings header, billableShipments the parsed X-Unmap-Billable-Shipments, and timeoutClamped with clampedTimeoutSeconds say whether your timeout was shortened.
  • A failure throws UnmapOptimizeError: status is the HTTP status, code the gateway's string code, googleStatus Google's status name and details Google's error.details. 429, 502, 503 and transport failures are retried by default.

Field support

Every field of the OptimizeToursRequest format used by the Google Maps Platform Route Optimization API, and what Unmap Optimize does with it. Field names and nesting follow Google's public REST reference; the descriptions are Unmap's own. Unmap is not affiliated with or endorsed by Google.

This section is generated from the validator's field table, and repository checks keep it in sync. There are 191 rows: 126 supported, 16 ignored, 5 rejected, 30 planned (V2), 14 planned (V3).

How to read the tables

StatusWhat Unmap does
supportedHonours the field.
ignoredAccepts the field and does nothing with it. A value other than the default adds a FIELD_IGNORED entry to the X-Unmap-Optimize-Warnings response header (one per field, with a count). Never an error.
rejectedRefuses the field with the error shown. Unmap does not plan to support it.
planned (V2), planned (V3)Refuses the field with FIELD_NOT_SUPPORTED_YET when it is set to anything but its default, because dropping a constraint silently would return a plan you did not ask for. Sent at its default (false, 0, "0s", [], {}, an _UNSPECIFIED enum, or the default shown), it is accepted.

A path such as model.shipments[].deliveries[].timeWindows[].startTime names one field: name[] is an element of an array, and name{} is a value of a map whose keys are yours (load type names, such as loadLimits{}). Fields under a refused or ignored field are not listed, because nothing below it is read.

Rules for the whole body:

  • Field names may be camelCase or snake_case. Sending both spellings of one field in the same object is a CONFLICTING_FIELD_NAMES error. Map keys and string values are never rewritten.
  • A field that is not in these tables is an UNKNOWN_FIELD error, so a typo never silently drops a constraint.
  • Durations are strings such as "300s" or "1.5s". Timestamps are RFC 3339 strings or { "seconds": ..., "nanos": ... }. 64-bit integers may be decimal strings or JSON integers up to 2^53 - 1.
  • Validation stops after maxValidationErrors errors (default 100, at most 10,000).
  • A request may use at most 16 distinct load type names, counted over every loadDemands and loadLimits key in the model. The first name past that is an OUT_OF_RANGE error.
  • A Google place ID (placeId) is PLACE_ID_NOT_SUPPORTED; geocode the address with /geocode/search and send coordinates. No field takes a URI: the batch methods that read gs:// Cloud Storage URIs are not available (they answer 404), and GCS_URI_NOT_SUPPORTED is reserved for them; nothing returns it today.

Every error code is explained in Errors.

Field support describes validation and supported behaviour. It does not guarantee that a request has a feasible solution. populatePolylines attempts road geometry for routes with coordinates; supplied-matrix requests return no polylines. See travel times and limits per plan.

Request fields

FieldStatusBehaviour
parentignoredThe Google Cloud project the request belongs to. Type string. Warning FIELD_IGNORED when set: "parent is ignored: Unmap has no projects, so the request runs under the API key's account."
timeoutsupportedTime the solver may spend; capped by the plan, and a default applies when absent. Type duration.
solvingModesupportedWhether to solve the model or only validate it. Type enum. Default DEFAULT_SOLVE. Values: DEFAULT_SOLVE, VALIDATE_ONLY supported; DETECT_SOME_INFEASIBLE_SHIPMENTS refused, planned (V2); TRANSFORM_AND_RETURN_REQUEST refused, planned (V3).
searchModesupportedTrades solution quality for latency: RETURN_FAST stops improving soon after the first solution, CONSUME_ALL_AVAILABLE_TIME (the default behaviour) searches until the timeout. Type enum. Values: SEARCH_MODE_UNSPECIFIED, RETURN_FAST, CONSUME_ALL_AVAILABLE_TIME supported.
injectedFirstSolutionRoutesplanned (V2)Routes the solver starts from instead of building its own first solution. Type message[]. Error FIELD_NOT_SUPPORTED_YET when set to a non-default value: "injectedFirstSolutionRoutes is not supported yet (planned for V2)." Hint: Remove the field or leave it at its default value.
injectedSolutionConstraintplanned (V2)A previous solution whose visits are frozen or constrained, for re-optimization. Type message. Error FIELD_NOT_SUPPORTED_YET when set to a non-default value: "injectedSolutionConstraint is not supported yet (planned for V2)." Hint: Remove the field or leave it at its default value.
refreshDetailsRoutesplanned (V2)Existing routes whose timings and metrics are recomputed without re-solving. Type message[]. Error FIELD_NOT_SUPPORTED_YET when set to a non-default value: "refreshDetailsRoutes is not supported yet (planned for V2)." Hint: Remove the field or leave it at its default value.
interpretInjectedSolutionsUsingLabelsplanned (V2)Matches injected routes to the model by label instead of by index. Type boolean. Error FIELD_NOT_SUPPORTED_YET when set to a non-default value: "interpretInjectedSolutionsUsingLabels is not supported yet (planned for V2)." Hint: Remove the field or leave it at its default value.
considerRoadTrafficignoredAsks for traffic-aware travel times. Type boolean. Warning FIELD_IGNORED when set: "considerRoadTraffic is ignored: Unmap travel times do not use live or historical traffic yet." Ignored rather than rejected: the plan is still valid on free-flow times, and V3 adds historical speeds.
populatePolylinessupportedAdds an encoded polyline for each used route as routes[].routePolyline. Type boolean. Default false.
populateTransitionPolylinesignoredAsks for an encoded polyline per transition between stops. Type boolean. Warning FIELD_IGNORED when set: "populateTransitionPolylines is ignored: Unmap returns one polyline per route, as routes[].routePolyline when populatePolylines is true." Ignored rather than rejected: the route polyline carries the same geometry, so a client loses detail, not correctness.
allowLargeDeadlineDespiteInterruptionRiskignoredAllows a long timeout on Google's infrastructure at the risk of interruption. Type boolean. Warning FIELD_IGNORED when set: "allowLargeDeadlineDespiteInterruptionRisk is ignored: Unmap caps the timeout per plan and clamps anything longer."
useGeodesicDistancessupportedUses straight-line distances and a fixed speed instead of road travel times. Type boolean. Default false.
labelsupportedCaller's label for the request, echoed as requestLabel in the response. Type string.
geodesicMetersPerSecondsupportedSpeed used with useGeodesicDistances, in metres per second; at least 1. Type number. Required when useGeodesicDistances is true: error MISSING_REQUIRED_FIELD.
maxValidationErrorssupportedMost validation errors to return; defaults to 100 and is capped at 10,000. Type int32. Default 100.

Model fields

FieldStatusBehaviour
modelsupportedThe shipments, vehicles and global constraints to plan. Type object.
model.objectivesplanned (V3)Alternative optimization goals that replace the cost model (experimental in GMPRO). Type message[]. Error FIELD_NOT_SUPPORTED_YET when set to a non-default value: "model.objectives is not supported yet (planned for V3)." Hint: Remove the field or leave it at its default value.
model.globalStartTimesupportedStart of the planning horizon; nothing happens before it. Type timestamp. Default 1970-01-01T00:00:00Z.
model.globalEndTimesupportedEnd of the planning horizon; nothing happens after it. At most 999,999,999 seconds (about 31 years) after globalStartTime. Type timestamp. Default 1971-01-01T00:00:00Z.
model.globalDurationCostPerHourplanned (V2)Cost per hour of the whole plan's span, from the earliest start to the latest end. Type number. Error FIELD_NOT_SUPPORTED_YET when set to a non-default value: "model.globalDurationCostPerHour is not supported yet (planned for V2)." Hint: Remove the field or leave it at its default value.
model.durationDistanceMatricessupportedCaller-supplied travel durations and distances, used instead of road or straight-line travel. With them, every stop is placed by tags: visits and vehicles must not carry coordinates, and useGeodesicDistances must not be true. Type object[].
model.durationDistanceMatrices[].rowssupportedOne row per source tag, in durationDistanceMatrixSrcTags order. Type object[].
model.durationDistanceMatrices[].rows[].durationssupportedTravel durations from this row's source to each destination tag. Type duration[].
model.durationDistanceMatrices[].rows[].meterssupportedTravel distances in metres from this row's source to each destination tag. Type number[].
model.durationDistanceMatrices[].vehicleStartTagsupportedThe vehicle start tag that selects this matrix; each vehicle's startTags must match exactly one. Empty applies the matrix to every vehicle and is allowed only when there is one matrix. Type string.
model.durationDistanceMatrixSrcTagssupportedTags naming the matrix rows; unique, not empty, and required exactly when durationDistanceMatrices is set. Type string[].
model.durationDistanceMatrixDstTagssupportedTags naming the matrix columns; unique, not empty, and required exactly when durationDistanceMatrices is set. Type string[].
model.transitionAttributesplanned (V3)Extra costs, delays and limits on travel between tagged stops. Type message[]. Error FIELD_NOT_SUPPORTED_YET when set to a non-default value: "model.transitionAttributes is not supported yet (planned for V3)." Hint: Remove the field or leave it at its default value.
model.shipmentTypeIncompatibilitiesplanned (V2)Shipment types that must not share a vehicle. Type message[]. Error FIELD_NOT_SUPPORTED_YET when set to a non-default value: "model.shipmentTypeIncompatibilities is not supported yet (planned for V2)." Hint: Remove the field or leave it at its default value.
model.shipmentTypeRequirementsplanned (V2)Shipment types that need another type on the same vehicle. Type message[]. Error FIELD_NOT_SUPPORTED_YET when set to a non-default value: "model.shipmentTypeRequirements is not supported yet (planned for V2)." Hint: Remove the field or leave it at its default value.
model.precedenceRulesplanned (V3)Ordering rules between visits of different shipments. Type message[]. Error FIELD_NOT_SUPPORTED_YET when set to a non-default value: "model.precedenceRules is not supported yet (planned for V3)." Hint: Remove the field or leave it at its default value.
model.maxActiveVehiclesplanned (V2)Most vehicles the plan may use. Type int32. Error FIELD_NOT_SUPPORTED_YET when set to a non-default value: "model.maxActiveVehicles is not supported yet (planned for V2)." Hint: Remove the field or leave it at its default value.

Shipment fields

FieldStatusBehaviour
model.shipmentssupportedEverything to pick up or deliver. Type object[].
model.shipments[].displayNamesupportedName for people; it does not affect the solve. Type string.
model.shipments[].pickupssupportedThe shipment's pickup; V1 takes at most one. Type object[]. More than 1: error VALUE_NOT_SUPPORTED_YET, "A shipment with more than one pickup alternative is not supported yet (planned for V2)."
model.shipments[].pickups[].arrivalLocationsupportedCoordinates of the pickup stop, in WGS84 degrees; send either this or arrivalWaypoint, and neither when the model has durationDistanceMatrices. Type object.
model.shipments[].pickups[].arrivalLocation.latitudesupportedLatitude in degrees, from -90 to 90. Type number. Default 0.
model.shipments[].pickups[].arrivalLocation.longitudesupportedLongitude in degrees, from -180 to 180. Type number. Default 0.
model.shipments[].pickups[].arrivalWaypointsupportedThe pickup stop as a waypoint; send either this or arrivalLocation, not both. Type object.
model.shipments[].pickups[].arrivalWaypoint.locationsupportedThe waypoint's position; required, because place IDs are not supported. Type object.
model.shipments[].pickups[].arrivalWaypoint.location.latLngsupportedCoordinates of the waypoint, in WGS84 degrees. Type object.
model.shipments[].pickups[].arrivalWaypoint.location.latLng.latitudesupportedLatitude in degrees, from -90 to 90. Type number. Default 0.
model.shipments[].pickups[].arrivalWaypoint.location.latLng.longitudesupportedLongitude in degrees, from -180 to 180. Type number. Default 0.
model.shipments[].pickups[].arrivalWaypoint.location.headingignoredCompass direction of travel at the waypoint, in degrees. Type int32. Warning FIELD_IGNORED when set: "location.heading is ignored: Unmap snaps a location to the nearest road whatever the direction of travel." Ignored rather than rejected: it only refines how a vehicle approaches a stop, so plans stay feasible and clients that set it on every waypoint keep working; travel times can differ slightly from Google's.
model.shipments[].pickups[].arrivalWaypoint.placeIdrejectedA Google Places identifier for the waypoint. Type string. Error PLACE_ID_NOT_SUPPORTED when set: "Google place IDs are not supported: Unmap cannot resolve them." Hint: Geocode the address with Unmap's /geocode/search and send its coordinates as arrivalWaypoint.location.latLng or arrivalLocation. Rejected: a place ID is a reference into Google's database, and guessing a location for it would route to the wrong place.
model.shipments[].pickups[].arrivalWaypoint.sideOfRoadignoredAsks for the vehicle to stop on the waypoint's side of the road. Type boolean. Warning FIELD_IGNORED when set: "sideOfRoad is ignored: Unmap travel times do not model which side of the road a stop is on." Ignored rather than rejected: it only refines how a vehicle approaches a stop, so plans stay feasible and clients that set it on every waypoint keep working; travel times can differ slightly from Google's.
model.shipments[].pickups[].arrivalWaypoint.vehicleStopoverignoredMarks the waypoint as a place where the vehicle stops to pick up or drop off. Type boolean. Warning FIELD_IGNORED when set: "vehicleStopover is ignored: Unmap snaps every stop to the nearest road a vehicle can use." Ignored rather than rejected: every Unmap stop is already treated as a stopover on a drivable road.
model.shipments[].pickups[].departureLocationplanned (V2)Where the vehicle leaves from after the pickup, when that differs from where it arrived. Type object. Error FIELD_NOT_SUPPORTED_YET when set to a non-default value: "model.shipments[].pickups[].departureLocation is not supported yet (planned for V2)." Hint: Remove the field or leave it at its default value.
model.shipments[].pickups[].departureWaypointplanned (V2)Waypoint the vehicle leaves from after the pickup, when that differs from where it arrived. Type object. Error FIELD_NOT_SUPPORTED_YET when set to a non-default value: "model.shipments[].pickups[].departureWaypoint is not supported yet (planned for V2)." Hint: Remove the field or leave it at its default value.
model.shipments[].pickups[].tagssupportedTags that place the pickup in the rows and columns of durationDistanceMatrices; unique and not empty, and required when the model has matrices. Type string[].
model.shipments[].pickups[].timeWindowssupportedHard time windows for the start of the pickup; sorted, non-overlapping and not touching. Type object[].
model.shipments[].pickups[].timeWindows[].startTimesupportedEarliest allowed time; defaults to model.globalStartTime. Type timestamp.
model.shipments[].pickups[].timeWindows[].endTimesupportedLatest allowed time; defaults to model.globalEndTime. Type timestamp.
model.shipments[].pickups[].timeWindows[].softStartTimesupportedSoft start: an event before it is allowed and costs costPerHourBeforeSoftStartTime per hour early. Only on a list with a single window, at or after startTime, inside the global window. Type timestamp.
model.shipments[].pickups[].timeWindows[].softEndTimesupportedSoft end: an event after it is allowed and costs costPerHourAfterSoftEndTime per hour late. Only on a list with a single window, at or before endTime, inside the global window. Type timestamp.
model.shipments[].pickups[].timeWindows[].costPerHourBeforeSoftStartTimesupportedCost per hour of an event before softStartTime; needs softStartTime. Type number.
model.shipments[].pickups[].timeWindows[].costPerHourAfterSoftEndTimesupportedCost per hour of an event after softEndTime; needs softEndTime. Type number.
model.shipments[].pickups[].durationsupportedTime spent at the stop to perform the pickup. Type duration. Default 0s.
model.shipments[].pickups[].costplanned (V2)Extra cost for choosing this pickup alternative over another. Type number. Error FIELD_NOT_SUPPORTED_YET when set to a non-default value: "model.shipments[].pickups[].cost is not supported yet (planned for V2)." Hint: Remove the field or leave it at its default value.
model.shipments[].pickups[].loadDemandsplanned (V2)Load changes that apply only when this pickup alternative is chosen. Type map<string, object>. Error FIELD_NOT_SUPPORTED_YET when set to a non-default value: "model.shipments[].pickups[].loadDemands is not supported yet (planned for V2)." Hint: Remove the field or leave it at its default value.
model.shipments[].pickups[].visitTypesplanned (V2)Types that add vehicle-specific extra duration to the pickup. Type string[]. Error FIELD_NOT_SUPPORTED_YET when set to a non-default value: "model.shipments[].pickups[].visitTypes is not supported yet (planned for V2)." Hint: Remove the field or leave it at its default value.
model.shipments[].pickups[].labelsupportedCaller's label for the pickup, echoed as visitLabel in the response. Type string.
model.shipments[].pickups[].avoidUTurnsplanned (V3)Asks for routes that avoid U-turns at the pickup stop (experimental in GMPRO). Type boolean. Error FIELD_NOT_SUPPORTED_YET when set to a non-default value: "model.shipments[].pickups[].avoidUTurns is not supported yet (planned for V3)." Hint: Remove the field or leave it at its default value.
model.shipments[].deliveriessupportedThe shipment's delivery; V1 takes at most one. Type object[]. More than 1: error VALUE_NOT_SUPPORTED_YET, "A shipment with more than one delivery alternative is not supported yet (planned for V2)."
model.shipments[].deliveries[].arrivalLocationsupportedCoordinates of the delivery stop, in WGS84 degrees; send either this or arrivalWaypoint, and neither when the model has durationDistanceMatrices. Type object.
model.shipments[].deliveries[].arrivalLocation.latitudesupportedLatitude in degrees, from -90 to 90. Type number. Default 0.
model.shipments[].deliveries[].arrivalLocation.longitudesupportedLongitude in degrees, from -180 to 180. Type number. Default 0.
model.shipments[].deliveries[].arrivalWaypointsupportedThe delivery stop as a waypoint; send either this or arrivalLocation, not both. Type object.
model.shipments[].deliveries[].arrivalWaypoint.locationsupportedThe waypoint's position; required, because place IDs are not supported. Type object.
model.shipments[].deliveries[].arrivalWaypoint.location.latLngsupportedCoordinates of the waypoint, in WGS84 degrees. Type object.
model.shipments[].deliveries[].arrivalWaypoint.location.latLng.latitudesupportedLatitude in degrees, from -90 to 90. Type number. Default 0.
model.shipments[].deliveries[].arrivalWaypoint.location.latLng.longitudesupportedLongitude in degrees, from -180 to 180. Type number. Default 0.
model.shipments[].deliveries[].arrivalWaypoint.location.headingignoredCompass direction of travel at the waypoint, in degrees. Type int32. Warning FIELD_IGNORED when set: "location.heading is ignored: Unmap snaps a location to the nearest road whatever the direction of travel." Ignored rather than rejected: it only refines how a vehicle approaches a stop, so plans stay feasible and clients that set it on every waypoint keep working; travel times can differ slightly from Google's.
model.shipments[].deliveries[].arrivalWaypoint.placeIdrejectedA Google Places identifier for the waypoint. Type string. Error PLACE_ID_NOT_SUPPORTED when set: "Google place IDs are not supported: Unmap cannot resolve them." Hint: Geocode the address with Unmap's /geocode/search and send its coordinates as arrivalWaypoint.location.latLng or arrivalLocation. Rejected: a place ID is a reference into Google's database, and guessing a location for it would route to the wrong place.
model.shipments[].deliveries[].arrivalWaypoint.sideOfRoadignoredAsks for the vehicle to stop on the waypoint's side of the road. Type boolean. Warning FIELD_IGNORED when set: "sideOfRoad is ignored: Unmap travel times do not model which side of the road a stop is on." Ignored rather than rejected: it only refines how a vehicle approaches a stop, so plans stay feasible and clients that set it on every waypoint keep working; travel times can differ slightly from Google's.
model.shipments[].deliveries[].arrivalWaypoint.vehicleStopoverignoredMarks the waypoint as a place where the vehicle stops to pick up or drop off. Type boolean. Warning FIELD_IGNORED when set: "vehicleStopover is ignored: Unmap snaps every stop to the nearest road a vehicle can use." Ignored rather than rejected: every Unmap stop is already treated as a stopover on a drivable road.
model.shipments[].deliveries[].departureLocationplanned (V2)Where the vehicle leaves from after the delivery, when that differs from where it arrived. Type object. Error FIELD_NOT_SUPPORTED_YET when set to a non-default value: "model.shipments[].deliveries[].departureLocation is not supported yet (planned for V2)." Hint: Remove the field or leave it at its default value.
model.shipments[].deliveries[].departureWaypointplanned (V2)Waypoint the vehicle leaves from after the delivery, when that differs from where it arrived. Type object. Error FIELD_NOT_SUPPORTED_YET when set to a non-default value: "model.shipments[].deliveries[].departureWaypoint is not supported yet (planned for V2)." Hint: Remove the field or leave it at its default value.
model.shipments[].deliveries[].tagssupportedTags that place the delivery in the rows and columns of durationDistanceMatrices; unique and not empty, and required when the model has matrices. Type string[].
model.shipments[].deliveries[].timeWindowssupportedHard time windows for the start of the delivery; sorted, non-overlapping and not touching. Type object[].
model.shipments[].deliveries[].timeWindows[].startTimesupportedEarliest allowed time; defaults to model.globalStartTime. Type timestamp.
model.shipments[].deliveries[].timeWindows[].endTimesupportedLatest allowed time; defaults to model.globalEndTime. Type timestamp.
model.shipments[].deliveries[].timeWindows[].softStartTimesupportedSoft start: an event before it is allowed and costs costPerHourBeforeSoftStartTime per hour early. Only on a list with a single window, at or after startTime, inside the global window. Type timestamp.
model.shipments[].deliveries[].timeWindows[].softEndTimesupportedSoft end: an event after it is allowed and costs costPerHourAfterSoftEndTime per hour late. Only on a list with a single window, at or before endTime, inside the global window. Type timestamp.
model.shipments[].deliveries[].timeWindows[].costPerHourBeforeSoftStartTimesupportedCost per hour of an event before softStartTime; needs softStartTime. Type number.
model.shipments[].deliveries[].timeWindows[].costPerHourAfterSoftEndTimesupportedCost per hour of an event after softEndTime; needs softEndTime. Type number.
model.shipments[].deliveries[].durationsupportedTime spent at the stop to perform the delivery. Type duration. Default 0s.
model.shipments[].deliveries[].costplanned (V2)Extra cost for choosing this delivery alternative over another. Type number. Error FIELD_NOT_SUPPORTED_YET when set to a non-default value: "model.shipments[].deliveries[].cost is not supported yet (planned for V2)." Hint: Remove the field or leave it at its default value.
model.shipments[].deliveries[].loadDemandsplanned (V2)Load changes that apply only when this delivery alternative is chosen. Type map<string, object>. Error FIELD_NOT_SUPPORTED_YET when set to a non-default value: "model.shipments[].deliveries[].loadDemands is not supported yet (planned for V2)." Hint: Remove the field or leave it at its default value.
model.shipments[].deliveries[].visitTypesplanned (V2)Types that add vehicle-specific extra duration to the delivery. Type string[]. Error FIELD_NOT_SUPPORTED_YET when set to a non-default value: "model.shipments[].deliveries[].visitTypes is not supported yet (planned for V2)." Hint: Remove the field or leave it at its default value.
model.shipments[].deliveries[].labelsupportedCaller's label for the delivery, echoed as visitLabel in the response. Type string.
model.shipments[].deliveries[].avoidUTurnsplanned (V3)Asks for routes that avoid U-turns at the delivery stop (experimental in GMPRO). Type boolean. Error FIELD_NOT_SUPPORTED_YET when set to a non-default value: "model.shipments[].deliveries[].avoidUTurns is not supported yet (planned for V3)." Hint: Remove the field or leave it at its default value.
model.shipments[].loadDemandssupportedLoad the shipment adds at its pickup and removes at its delivery, keyed by load type name. A request may use at most 16 distinct load type names across every loadDemands and loadLimits map: error OUT_OF_RANGE past that. Type map<string, object>.
model.shipments[].loadDemands{}.amountsupportedUnits of this load type. One type's amounts over every pickup and delivery in the request must add up to at most 2^53 - 1. Type int64. Default 0.
model.shipments[].penaltyCostsupportedCost of leaving the shipment unserved; absent means the shipment is mandatory. Type number.
model.shipments[].allowedVehicleIndicessupportedIndices of the only vehicles that may serve the shipment; empty means any. Type int32[].
model.shipments[].costsPerVehicleplanned (V2)Extra cost when a given vehicle serves the shipment. Type number[]. Error FIELD_NOT_SUPPORTED_YET when set to a non-default value: "model.shipments[].costsPerVehicle is not supported yet (planned for V2)." Hint: Remove the field or leave it at its default value.
model.shipments[].costsPerVehicleIndicesplanned (V2)The vehicles that costsPerVehicle refers to. Type int32[]. Error FIELD_NOT_SUPPORTED_YET when set to a non-default value: "model.shipments[].costsPerVehicleIndices is not supported yet (planned for V2)." Hint: Remove the field or leave it at its default value.
model.shipments[].pickupToDeliveryRelativeDetourLimitplanned (V2)Longest allowed pickup-to-delivery time, relative to the direct trip. Type number. Error FIELD_NOT_SUPPORTED_YET when set to a non-default value: "model.shipments[].pickupToDeliveryRelativeDetourLimit is not supported yet (planned for V2)." Hint: Remove the field or leave it at its default value.
model.shipments[].pickupToDeliveryAbsoluteDetourLimitplanned (V2)Longest allowed extra time between pickup and delivery over the direct trip. Type duration. Error FIELD_NOT_SUPPORTED_YET when set to a non-default value: "model.shipments[].pickupToDeliveryAbsoluteDetourLimit is not supported yet (planned for V2)." Hint: Remove the field or leave it at its default value.
model.shipments[].pickupToDeliveryTimeLimitplanned (V2)Longest allowed time from the start of the pickup to the start of the delivery. Type duration. Error FIELD_NOT_SUPPORTED_YET when set to a non-default value: "model.shipments[].pickupToDeliveryTimeLimit is not supported yet (planned for V2)." Hint: Remove the field or leave it at its default value.
model.shipments[].shipmentTypeplanned (V2)Type name used by incompatibility and requirement rules. Type string. Error FIELD_NOT_SUPPORTED_YET when set to a non-default value: "model.shipments[].shipmentType is not supported yet (planned for V2)." Hint: Remove the field or leave it at its default value.
model.shipments[].labelsupportedCaller's label for the shipment, echoed as shipmentLabel and used to recognise re-solves for billing. Type string.
model.shipments[].ignoresupportedWhen true the shipment is left out of the solve, listed in skippedShipments with reason SHIPMENT_IGNORED, and not billed. Type boolean. Default false.

Vehicle fields

FieldStatusBehaviour
model.vehiclessupportedThe fleet that performs the shipments. Type object[].
model.vehicles[].displayNamesupportedName for people; it does not affect the solve. Type string.
model.vehicles[].travelModesupportedHow the vehicle travels; V1 drives. Type enum. Default DRIVING. Values: TRAVEL_MODE_UNSPECIFIED, DRIVING supported; WALKING refused, planned (V2).
model.vehicles[].routeModifiersplanned (V3)Road preferences such as avoiding tolls, highways or ferries. Type object. Error FIELD_NOT_SUPPORTED_YET when set to a non-default value: "model.vehicles[].routeModifiers is not supported yet (planned for V3)." Hint: Remove the field or leave it at its default value.
model.vehicles[].startLocationsupportedWhere the vehicle starts, in WGS84 degrees; with no start location or waypoint the route starts at its first stop. Not allowed when the model has durationDistanceMatrices. Type object.
model.vehicles[].startLocation.latitudesupportedLatitude in degrees, from -90 to 90. Type number. Default 0.
model.vehicles[].startLocation.longitudesupportedLongitude in degrees, from -180 to 180. Type number. Default 0.
model.vehicles[].startWaypointsupportedThe vehicle's start as a waypoint; send either this or startLocation, not both. Type object.
model.vehicles[].startWaypoint.locationsupportedThe waypoint's position; required, because place IDs are not supported. Type object.
model.vehicles[].startWaypoint.location.latLngsupportedCoordinates of the waypoint, in WGS84 degrees. Type object.
model.vehicles[].startWaypoint.location.latLng.latitudesupportedLatitude in degrees, from -90 to 90. Type number. Default 0.
model.vehicles[].startWaypoint.location.latLng.longitudesupportedLongitude in degrees, from -180 to 180. Type number. Default 0.
model.vehicles[].startWaypoint.location.headingignoredCompass direction of travel at the waypoint, in degrees. Type int32. Warning FIELD_IGNORED when set: "location.heading is ignored: Unmap snaps a location to the nearest road whatever the direction of travel." Ignored rather than rejected: it only refines how a vehicle approaches a stop, so plans stay feasible and clients that set it on every waypoint keep working; travel times can differ slightly from Google's.
model.vehicles[].startWaypoint.placeIdrejectedA Google Places identifier for the waypoint. Type string. Error PLACE_ID_NOT_SUPPORTED when set: "Google place IDs are not supported: Unmap cannot resolve them." Hint: Geocode the address with Unmap's /geocode/search and send its coordinates as startWaypoint.location.latLng or startLocation. Rejected: a place ID is a reference into Google's database, and guessing a location for it would route to the wrong place.
model.vehicles[].startWaypoint.sideOfRoadignoredAsks for the vehicle to stop on the waypoint's side of the road. Type boolean. Warning FIELD_IGNORED when set: "sideOfRoad is ignored: Unmap travel times do not model which side of the road a stop is on." Ignored rather than rejected: it only refines how a vehicle approaches a stop, so plans stay feasible and clients that set it on every waypoint keep working; travel times can differ slightly from Google's.
model.vehicles[].startWaypoint.vehicleStopoverignoredMarks the waypoint as a place where the vehicle stops to pick up or drop off. Type boolean. Warning FIELD_IGNORED when set: "vehicleStopover is ignored: Unmap snaps every stop to the nearest road a vehicle can use." Ignored rather than rejected: every Unmap stop is already treated as a stopover on a drivable road.
model.vehicles[].startTagssupportedTags that place the vehicle's start in durationDistanceMatrices and select its matrix by vehicleStartTag; unique and not empty; with no startTags the route starts at its first stop. Type string[].
model.vehicles[].startTimeWindowssupportedHard time window for the vehicle's start time; V1 takes at most one (GMPRO allows several, sorted, not overlapping and not touching). Type object[]. More than 1: error VALUE_NOT_SUPPORTED_YET, "A vehicle with more than one startTimeWindows entry is not supported yet (planned for V2)."
model.vehicles[].startTimeWindows[].startTimesupportedEarliest allowed time; defaults to model.globalStartTime. Type timestamp.
model.vehicles[].startTimeWindows[].endTimesupportedLatest allowed time; defaults to model.globalEndTime. Type timestamp.
model.vehicles[].startTimeWindows[].softStartTimesupportedSoft start: an event before it is allowed and costs costPerHourBeforeSoftStartTime per hour early. Only on a list with a single window, at or after startTime, inside the global window. Type timestamp.
model.vehicles[].startTimeWindows[].softEndTimesupportedSoft end: an event after it is allowed and costs costPerHourAfterSoftEndTime per hour late. Only on a list with a single window, at or before endTime, inside the global window. Type timestamp.
model.vehicles[].startTimeWindows[].costPerHourBeforeSoftStartTimesupportedCost per hour of an event before softStartTime; needs softStartTime. Type number.
model.vehicles[].startTimeWindows[].costPerHourAfterSoftEndTimesupportedCost per hour of an event after softEndTime; needs softEndTime. Type number.
model.vehicles[].endLocationsupportedWhere the vehicle ends, in WGS84 degrees; with no end location or waypoint the route ends at its last stop. Not allowed when the model has durationDistanceMatrices. Type object.
model.vehicles[].endLocation.latitudesupportedLatitude in degrees, from -90 to 90. Type number. Default 0.
model.vehicles[].endLocation.longitudesupportedLongitude in degrees, from -180 to 180. Type number. Default 0.
model.vehicles[].endWaypointsupportedThe vehicle's end as a waypoint; send either this or endLocation, not both. Type object.
model.vehicles[].endWaypoint.locationsupportedThe waypoint's position; required, because place IDs are not supported. Type object.
model.vehicles[].endWaypoint.location.latLngsupportedCoordinates of the waypoint, in WGS84 degrees. Type object.
model.vehicles[].endWaypoint.location.latLng.latitudesupportedLatitude in degrees, from -90 to 90. Type number. Default 0.
model.vehicles[].endWaypoint.location.latLng.longitudesupportedLongitude in degrees, from -180 to 180. Type number. Default 0.
model.vehicles[].endWaypoint.location.headingignoredCompass direction of travel at the waypoint, in degrees. Type int32. Warning FIELD_IGNORED when set: "location.heading is ignored: Unmap snaps a location to the nearest road whatever the direction of travel." Ignored rather than rejected: it only refines how a vehicle approaches a stop, so plans stay feasible and clients that set it on every waypoint keep working; travel times can differ slightly from Google's.
model.vehicles[].endWaypoint.placeIdrejectedA Google Places identifier for the waypoint. Type string. Error PLACE_ID_NOT_SUPPORTED when set: "Google place IDs are not supported: Unmap cannot resolve them." Hint: Geocode the address with Unmap's /geocode/search and send its coordinates as endWaypoint.location.latLng or endLocation. Rejected: a place ID is a reference into Google's database, and guessing a location for it would route to the wrong place.
model.vehicles[].endWaypoint.sideOfRoadignoredAsks for the vehicle to stop on the waypoint's side of the road. Type boolean. Warning FIELD_IGNORED when set: "sideOfRoad is ignored: Unmap travel times do not model which side of the road a stop is on." Ignored rather than rejected: it only refines how a vehicle approaches a stop, so plans stay feasible and clients that set it on every waypoint keep working; travel times can differ slightly from Google's.
model.vehicles[].endWaypoint.vehicleStopoverignoredMarks the waypoint as a place where the vehicle stops to pick up or drop off. Type boolean. Warning FIELD_IGNORED when set: "vehicleStopover is ignored: Unmap snaps every stop to the nearest road a vehicle can use." Ignored rather than rejected: every Unmap stop is already treated as a stopover on a drivable road.
model.vehicles[].endTagssupportedTags that place the vehicle's end in durationDistanceMatrices; unique and not empty; with no endTags the route ends at its last stop. Type string[].
model.vehicles[].endTimeWindowssupportedHard time window for the vehicle's end time; V1 takes at most one (GMPRO allows several, sorted, not overlapping and not touching). It must not close before startTimeWindows opens. Type object[]. More than 1: error VALUE_NOT_SUPPORTED_YET, "A vehicle with more than one endTimeWindows entry is not supported yet (planned for V2)."
model.vehicles[].endTimeWindows[].startTimesupportedEarliest allowed time; defaults to model.globalStartTime. Type timestamp.
model.vehicles[].endTimeWindows[].endTimesupportedLatest allowed time; defaults to model.globalEndTime. Type timestamp.
model.vehicles[].endTimeWindows[].softStartTimesupportedSoft start: an event before it is allowed and costs costPerHourBeforeSoftStartTime per hour early. Only on a list with a single window, at or after startTime, inside the global window. Type timestamp.
model.vehicles[].endTimeWindows[].softEndTimesupportedSoft end: an event after it is allowed and costs costPerHourAfterSoftEndTime per hour late. Only on a list with a single window, at or before endTime, inside the global window. Type timestamp.
model.vehicles[].endTimeWindows[].costPerHourBeforeSoftStartTimesupportedCost per hour of an event before softStartTime; needs softStartTime. Type number.
model.vehicles[].endTimeWindows[].costPerHourAfterSoftEndTimesupportedCost per hour of an event after softEndTime; needs softEndTime. Type number.
model.vehicles[].unloadingPolicyplanned (V3)Last-in-first-out or first-in-first-out order for deliveries. Type enum. Error FIELD_NOT_SUPPORTED_YET when set to a non-default value: "model.vehicles[].unloadingPolicy is not supported yet (planned for V3)." Hint: Remove the field or leave it at its default value.
model.vehicles[].loadLimitssupportedCapacity per load type name. Its keys count toward the 16 distinct load types a request may use. Type map<string, object>.
model.vehicles[].loadLimits{}.maxLoadsupportedMost units of this load type the vehicle may carry at once; absent means unlimited. Type int64.
model.vehicles[].loadLimits{}.softMaxLoadsupportedLoad above which each extra unit costs costPerUnitAboveSoftMax; absent means 0. Charges nothing without a cost above 0. Type int64.
model.vehicles[].loadLimits{}.costPerUnitAboveSoftMaxsupportedCost per unit of peak load above softMaxLoad (0 when absent). A cost above 0 needs a load type that pickups alone or deliveries alone use. Type number.
model.vehicles[].loadLimits{}.startLoadIntervalplanned (V2)Allowed load when the route starts. Type object. Error FIELD_NOT_SUPPORTED_YET when set to a non-default value: "model.vehicles[].loadLimits{}.startLoadInterval is not supported yet (planned for V2)." Hint: Remove the field or leave it at its default value.
model.vehicles[].loadLimits{}.endLoadIntervalplanned (V2)Allowed load when the route ends. Type object. Error FIELD_NOT_SUPPORTED_YET when set to a non-default value: "model.vehicles[].loadLimits{}.endLoadInterval is not supported yet (planned for V2)." Hint: Remove the field or leave it at its default value.
model.vehicles[].loadLimits{}.costPerKilometerplanned (V3)Load-dependent cost per kilometre (experimental in GMPRO). Type object. Error FIELD_NOT_SUPPORTED_YET when set to a non-default value: "model.vehicles[].loadLimits{}.costPerKilometer is not supported yet (planned for V3)." Hint: Remove the field or leave it at its default value.
model.vehicles[].loadLimits{}.costPerTraveledHourplanned (V3)Load-dependent cost per hour of travel (experimental in GMPRO). Type object. Error FIELD_NOT_SUPPORTED_YET when set to a non-default value: "model.vehicles[].loadLimits{}.costPerTraveledHour is not supported yet (planned for V3)." Hint: Remove the field or leave it at its default value.
model.vehicles[].costPerHoursupportedCost per hour of the route, from vehicle start to vehicle end. Type number. Default 0.
model.vehicles[].costPerTraveledHoursupportedCost per hour spent driving. Type number. Default 0.
model.vehicles[].costPerKilometersupportedCost per kilometre driven. Type number. Default 0.
model.vehicles[].fixedCostsupportedCost charged once if the vehicle serves at least one shipment. Type number. Default 0.
model.vehicles[].usedIfRouteIsEmptyplanned (V2)Charges and times an empty route as if the vehicle drove from start to end. Type boolean. Error FIELD_NOT_SUPPORTED_YET when set to a non-default value: "model.vehicles[].usedIfRouteIsEmpty is not supported yet (planned for V2)." Hint: Remove the field or leave it at its default value.
model.vehicles[].routeDurationLimitsupportedLimits on route duration, from vehicle start to vehicle end. Type object.
model.vehicles[].routeDurationLimit.maxDurationsupportedHard maximum route duration, from vehicle start to vehicle end. Type duration.
model.vehicles[].routeDurationLimit.softMaxDurationsupportedSoft maximum route duration, from vehicle start to vehicle end: going over it costs costPerHourAfterSoftMax per hour; below maxDuration when both are set. Type duration.
model.vehicles[].routeDurationLimit.quadraticSoftMaxDurationplanned (V3)Soft maximum route duration, from vehicle start to vehicle end with a cost that grows with the square of the excess. Type duration. Error FIELD_NOT_SUPPORTED_YET when set to a non-default value: "model.vehicles[].routeDurationLimit.quadraticSoftMaxDuration is not supported yet (planned for V3)." Hint: Remove the field or leave it at its default value.
model.vehicles[].routeDurationLimit.costPerHourAfterSoftMaxsupportedCost per hour over softMaxDuration; needs softMaxDuration. Type number.
model.vehicles[].routeDurationLimit.costPerSquareHourAfterQuadraticSoftMaxplanned (V3)Cost per square hour over the quadratic soft maximum. Type number. Error FIELD_NOT_SUPPORTED_YET when set to a non-default value: "model.vehicles[].routeDurationLimit.costPerSquareHourAfterQuadraticSoftMax is not supported yet (planned for V3)." Hint: Remove the field or leave it at its default value.
model.vehicles[].travelDurationLimitsupportedLimits on driving time over the route. Type object.
model.vehicles[].travelDurationLimit.maxDurationsupportedHard maximum driving time over the route. Type duration.
model.vehicles[].travelDurationLimit.softMaxDurationsupportedSoft maximum driving time over the route: going over it costs costPerHourAfterSoftMax per hour; below maxDuration when both are set. Type duration.
model.vehicles[].travelDurationLimit.quadraticSoftMaxDurationplanned (V3)Soft maximum driving time over the route with a cost that grows with the square of the excess. Type duration. Error FIELD_NOT_SUPPORTED_YET when set to a non-default value: "model.vehicles[].travelDurationLimit.quadraticSoftMaxDuration is not supported yet (planned for V3)." Hint: Remove the field or leave it at its default value.
model.vehicles[].travelDurationLimit.costPerHourAfterSoftMaxsupportedCost per hour over softMaxDuration; needs softMaxDuration. Type number.
model.vehicles[].travelDurationLimit.costPerSquareHourAfterQuadraticSoftMaxplanned (V3)Cost per square hour over the quadratic soft maximum. Type number. Error FIELD_NOT_SUPPORTED_YET when set to a non-default value: "model.vehicles[].travelDurationLimit.costPerSquareHourAfterQuadraticSoftMax is not supported yet (planned for V3)." Hint: Remove the field or leave it at its default value.
model.vehicles[].routeDistanceLimitsupportedLimits on the distance driven over the route. Type object.
model.vehicles[].routeDistanceLimit.maxMeterssupportedHard maximum distance in metres, at most 1,000,000,000 (the solver's unreachable value). Type int64.
model.vehicles[].routeDistanceLimit.softMaxMeterssupportedSoft maximum distance in metres: going over it costs costPerKilometerAboveSoftMax per kilometre; below maxMeters when both are set. Type int64.
model.vehicles[].routeDistanceLimit.costPerKilometerBelowSoftMaxrejectedCost per kilometre up to the soft maximum. Type number. Error INVALID_VALUE when set: "routeDistanceLimit.costPerKilometerBelowSoftMax is not supported: GMPRO does not support it in route_distance_limit either." Hint: Remove the field; costPerKilometer charges every kilometre driven. Rejected: GMPRO's own proto says this cost is not supported in route_distance_limit, so no GMPRO request can depend on it.
model.vehicles[].routeDistanceLimit.costPerKilometerAboveSoftMaxsupportedCost per kilometre over softMaxMeters; needs softMaxMeters. Type number.
model.vehicles[].extraVisitDurationForVisitTypeplanned (V2)Extra time this vehicle spends at visits of a given type. Type map<string, duration>. Error FIELD_NOT_SUPPORTED_YET when set to a non-default value: "model.vehicles[].extraVisitDurationForVisitType is not supported yet (planned for V2)." Hint: Remove the field or leave it at its default value.
model.vehicles[].breakRulesupportedBreaks the driver must take. Type object.
model.vehicles[].breakRule.breakRequestssupportedBreaks, in the order they must happen. Type object[].
model.vehicles[].breakRule.breakRequests[].earliestStartTimesupportedEarliest time the break may start. Type timestamp. Required.
model.vehicles[].breakRule.breakRequests[].latestStartTimesupportedLatest time the break may start. Type timestamp. Required.
model.vehicles[].breakRule.breakRequests[].minDurationsupportedShortest allowed length of the break; must be positive. Type duration. Required.
model.vehicles[].breakRule.frequencyConstraintsplanned (V3)Hours-of-service style rules on how often breaks happen. Type object[]. Error FIELD_NOT_SUPPORTED_YET when set to a non-default value: "model.vehicles[].breakRule.frequencyConstraints is not supported yet (planned for V3)." Hint: Remove the field or leave it at its default value.
model.vehicles[].labelsupportedCaller's label for the vehicle, echoed as vehicleLabel in the response. Type string.
model.vehicles[].ignoreplanned (V2)Leaves the vehicle out of the plan. Type boolean. Error FIELD_NOT_SUPPORTED_YET when set to a non-default value: "model.vehicles[].ignore is not supported yet (planned for V2)." Hint: Remove the field or leave it at its default value.
model.vehicles[].travelDurationMultipleplanned (V2)Multiplies every travel duration for this vehicle, for slower or faster vehicles. Type number. Default 1. Error FIELD_NOT_SUPPORTED_YET when set to a non-default value: "model.vehicles[].travelDurationMultiple is not supported yet (planned for V2)." Hint: Remove the field or leave it at its default value.
model.vehicles[].unmapProfilesupportedUnmap extension: the road network profile the vehicle's travel times come from. Type enum. Default auto. Values: auto supported; truck, pedestrian refused, planned (V2).

Next steps