Route Optimization Cost Parameters

Define a custom cost model for a route optimization run

Cost Model per Request

costParams is an optional top-level object on POST /api/v2/optimizations/scheduled. It defines a custom ROv3 cost model for the run, in place of the preset mode. This field is fully optional — omitting it preserves current behavior (either the preset mode you send, or the org's route optimization profile default).

Send exactly one of mode or costParams on a given request. Sending both, or neither, results in a 400 response.

NameTypeDescription
costParamsobjectOptional. A plain object of cost-model keys (see below), camelCase, all individually optional. Cannot be sent together with mode.
📘

Supported Keys

All keys are optional — send any subset. Each is converted to its LogisticsOS snake_case equivalent under the hood.

Must be >= 0:
costPerOrder, costPerPickup, costPerDropoff, costPerUnitTime, costPerUnitDistance, costPerUnitLateTime, costPerOverorder, costPerUnitOvertime, costPerUnitOverdistance, overAdjacentDistanceCost, fixedCost

Must be > 0 (threshold "start" values):
overorderStart, overtimeStart, overdistanceStart, overAdjacentDistanceStart

costParams Field Reference

FieldRuleTypeDescription
costPerOrder>= 0Flat per-stop costFlat cost charged once per completed order (stop), regardless of order size or distance traveled. Penalizes stop count independent of everything else.
costPerPickup>= 0Flat per-stop costSame as costPerOrder, but applies only to pickup stops. Lets you weight pickups differently than dropoffs.
costPerDropoff>= 0Flat per-stop costSame as costPerOrder, but applies only to dropoff stops.
costPerUnitTime>= 0Base rateCost per unit of time spent traveling (not idle/service time — travel only). Defaults to 1 if unset, meaning total travel time is minimized by default.
costPerUnitDistance>= 0Base rateCost per unit of distance traveled. Your fuel/mileage-based cost proxy.
costPerUnitLateTime>= 0Penalty rateExtra cost per unit of time an order is delivered past its time window. Charged in addition to the normal time cost, not instead of it.
costPerOverorder>= 0Overage rateCost per order once a route's stop count exceeds overorderStart. Only applies to orders past the threshold, not the whole route.
overorderStart> 0ThresholdThe number of orders on a route after which the higher costPerOverorder rate kicks in instead of costPerOrder.
costPerUnitOvertime>= 0Overage rateCost per unit of time once a vehicle's total time exceeds overtimeStart. Applies only to time past the threshold.
overtimeStart> 0ThresholdThe amount of time (per vehicle, per shift) after which "overtime" begins and costPerUnitOvertime replaces costPerUnitTime for the excess.
costPerUnitOverdistance>= 0Overage rateCost per unit of distance once a vehicle's total distance exceeds overdistanceStart. Applies only to distance past the threshold.
overdistanceStart> 0ThresholdThe distance (per vehicle, per route) after which "overdistance" begins and costPerUnitOverdistance replaces costPerUnitDistance for the excess.
overAdjacentDistanceCost>= 0Penalty (per hop)Flat penalty applied to a single hop between two consecutive stops when that hop's distance exceeds overAdjacentDistanceStart. Not cumulative across the route — evaluated per adjacent pair.
overAdjacentDistanceStart> 0ThresholdThe distance between two adjacent stops beyond which overAdjacentDistanceCost is applied to that specific hop. Useful for discouraging long jumps between clusters.
fixedCost>= 0Per-vehicle flat costOne-time cost charged for dispatching a vehicle at all — once per vehicle used, independent of stops, time, or distance. Higher values push the solver toward using fewer vehicles/routes.

Reading the two rule types:

  • >= 0 (rates and flat costs): these are literal dollar/cost-unit amounts — 0 is valid and just means "this cost doesn't apply."
  • > 0 (the *Start thresholds): these mark where an overage tier begins on the time/distance/order-count axis, not a cost amount — 0 isn't a meaningful threshold, so it's disallowed. If you don't want an overage tier at all, omit the key rather than setting it to 0.

Unacceptable Values

A request is rejected with a 400 response if costParams is:

  • not a plain object
  • an empty object
  • an object containing an unsupported key
  • an object containing a value that is not a finite number, or that violates the sign rule above (e.g. a negative costPerUnitDistance, or an overtimeStart of 0)

Example Request

curl -X POST "https://onfleet.com/api/v2/optimizations/scheduled" \
     -u "<your_onfleet_api_key>:" \
     -d '{"tasks": [
             "atnk3APxhJrgfx9mgWaRhByQ",
             "4kA7bOH*Kz7K0YA6~NpqWDke"
           ],
           "teams": {
             "5y1jHFp7OBfFU6SekbwwwRfL": [
             "GLlLB2123ABCkXOJAHPklMPZ"
             ]
           },
           "date": 1781000000000,
           "timezone": "America/Los_Angeles",
           "schedulingMethod": 1,
           "serviceTime": 120,
           "maxViolationTime": 10,
           "defaultSchedule": [
             {
               "date": 1780981200000,
               "slots": [{"start": "0000", "end": "2359"}],
               "owner": "GLlLB2123ABCkXOJAHPklMPZ",
               "timezone": "America/Los_Angeles"
             }
           ],
           "costParams": {
             "costPerUnitDistance": 2,
             "costPerUnitTime": 1,
             "costPerOrder": 0,
             "overtimeStart": 3600
           }}'

Do not also send mode on this request — costParams replaces it entirely.

Organization Cost Model Default

An organization can set a default costParams on its route optimization profile so that it applies automatically to every scheduled optimization request that doesn't specify one directly. This setting requires Route Optimization v3 (ROv3) and is not exposed anywhere in the Onfleet dashboard — there is currently no UI to view, set, or confirm it. To enable a default clustering level for your org, contact [email protected] or your Customer Success Manager; they will configure it on the backend route optimization profile for your organization. Because there's no UI indicator, the only way to know what default (if any) is currently set for your org is to ask your CSM or Onfleet support directly.

Profiles Cannot Be Mixed

A route optimization profile is configured as either a mode profile or a costParams profile — never both. Sending a request whose type doesn't match the org's configured profile returns a 400 response:

  • a mode-profile org sending a request with costParams → 400
  • a costParams-profile org sending a request with mode → 400

Pick a profile type compatible with the requests your integration sends.

Scope and Precedence

The per-request costParams (the field sent on the POST /optimizations/scheduled call) is the topmost — it takes precedence over everything else.
The order works like this, highest priority first:

  • Request-level costParams (sent in the JSON body of that specific call) — wins if present, no matter what's set at the org level.
  • Org-level default (the costParams profile configured for your organization) — only applies if the request omits costParams entirely.
  • Preset mode — used only if neither a request-level nor an org-level costParams is in play, per the org's mode profile or the request's mode field.

Organization profile setting is a fallback, not an override — it only kicks in when a given request doesn't specify its own costParams.