Skip to main content

Overview

The V3 Routing API is a ground-up rewrite with a new, faster solver engine replacing Timefold. The V2 endpoint (/v2/vrp/sync/solve) remains available for backwards compatibility — internally it maps to V3 and back.

Endpoint change

V2 (OnRoute)V3 (Routing)
URLPOST /v2/vrp/sync/solvePOST /v3/routing/solve
AuthAuthorization: API_KEYAuthorization: API_KEY
Coordinate format{"latitude": 50.85, "longitude": 4.35}{ "coordinate": [4.35, 50.85] } (GeoJSON: lon, lat)
Time formatISO 8601 stringsISO 8601 strings
Field namingcamelCasesnake_case

Request mapping

Vehicles (Resources → Vehicles)

{
  "name": "truck-1",
  "shifts": [
    {
      "from": "2026-04-01T08:00:00+02:00",
      "to": "2026-04-01T17:00:00+02:00",
      "start": { "latitude": 50.8503, "longitude": 4.3517 },
      "end": { "latitude": 50.8503, "longitude": 4.3517 }
    }
  ],
  "capacity": [100],
  "tags": ["refrigerated"]
}
{
  "id": "truck-1",
  "capacity": { "weight": 100 },
  "skills": ["refrigerated"],
  "shifts": [
    {
      "from": "2026-04-01T08:00:00+02:00",
      "to": "2026-04-01T17:00:00+02:00",
      "start": { "coordinate": [4.3517, 50.8503] },
      "end":   { "coordinate": [4.3517, 50.8503] }
    }
  ]
}
Key differences:
  • nameid
  • shifts[].start/end move to inside shifts[] as a Place ({ "coordinate": [lon, lat] }) — this is still inside the shift in both V2 and V3, but the coordinate format changes
  • Coordinates: { "latitude", "longitude" } object → { "coordinate": [longitude, latitude] } Place (GeoJSON order, wrapped in object)
  • capacity: array [100] → named map { "weight": 100 } (dimension names must match job demand keys)
  • tagsskills (note: V2 tags are not automatically mapped when using the V2 compat endpoint)
  • New: limits.max_distance_m, limits.max_drive_time_s for vehicle range constraints

Jobs

{
  "name": "delivery-1",
  "location": { "latitude": 50.8798, "longitude": 4.7005 },
  "duration": 300,
  "load": [10],
  "windows": [
    {
      "from": "2026-04-01T09:00:00+02:00",
      "to": "2026-04-01T12:00:00+02:00",
      "hard": true
    }
  ]
}
{
  "id": "delivery-1",
  "location": { "coordinate": [4.7005, 50.8798] },
  "service_duration_s": 300,
  "demand": { "weight": 10 },
  "time_windows": [
    {
      "from": "2026-04-01T09:00:00+02:00",
      "to": "2026-04-01T12:00:00+02:00"
    }
  ]
}
Key differences:
  • nameid
  • location: { "latitude", "longitude" } → Place object { "coordinate": [longitude, latitude] }
  • durationservice_duration_s (still in seconds)
  • load array → demand named map (e.g. [10]{ "weight": 10 })
  • windowstime_windows (no hard flag — all time windows are hard in V3 Phase 1)
  • Skills and committed vehicle: skills: [{ "name": "refrigerated" }], eligible_vehicles: { "allowed": ["truck-1"] } — Coming soon (Phase 2)

Relations

V3 uses a tagged-union format with job_ids (not jobs). V2 relation types map as follows:
V2 typeV3 typeNotes
SEQUENCEorderedjob_ids instead of jobs; resource pin not yet supported
FIRST_JOBordered with a single entry in job_idsPin job to position 1 in route
{
  "type": "SEQUENCE",
  "jobs": ["job-A", "job-B", "job-C"],
  "resource": "truck-1"
}
{
  "type": "ordered",
  "job_ids": ["job-A", "job-B", "job-C"]
}
Note: V2’s resource field (pin relation to a specific vehicle) is not yet supported in V3. Use eligible_vehicles on individual jobs as a workaround — Coming soon.

Options

{
  "solvingTime": 30
}
{
  "options": {
    "runtime": {
      "time_limit_ms": 5000,
      "seed": 42
    }
  }
}
Key differences:
  • solvingTime (seconds) → options.runtime.time_limit_ms (milliseconds)
  • New: options.runtime.seed for reproducible results
  • New: options.runtime.matrix — supply custom distance/duration matrices (bypasses Solvice Maps)
  • Objective tuning (minimize time vs. distance) is configured via the objective field — Coming soon

Response mapping

{
  "trips": [
    {
      "resource": "truck-1",
      "visits": [
        {
          "job": "delivery-1",
          "arrival": "2026-04-01T09:30:00+02:00",
          "location": { "latitude": 50.8798, "longitude": 4.7005 }
        }
      ],
      "travelTime": 3600,
      "distance": 30000
    }
  ],
  "unserved": ["delivery-3"],
  "unservedReasons": {
    "delivery-3": ["Time window violated"]
  },
  "totalTravelTimeInSeconds": 3600,
  "totalTravelDistanceInMeters": 30000
}
{
  "summary": {
    "status": "solved",
    "total_distance_m": 142000,
    "total_duration_s": 7200,
    "vehicles_used": 1,
    "jobs_assigned": 2,
    "jobs_unassigned": 0,
    "estimated_cost": {
      "total": 7200,
      "components": {
        "travel": 7200,
        "waiting": 0,
        "overtime": 0,
        "lateness": 0,
        "vehicles_fixed": 0,
        "unassigned": 0,
        "preferences": 0,
        "imbalance": 0
      }
    },
    "elapsed_ms": 850,
    "iterations": 1200
  },
  "routes": [
    {
      "vehicle": { "type": "truck-1", "instance": 1 },
      "distance_m": 142000,
      "duration_s": 7200,
      "overtime_s": 0,
      "stops": [
        {
          "type": "start",
          "location": [4.3517, 50.8503],
          "departure": "2026-04-01T08:00:00+02:00"
        },
        {
          "type": "job",
          "id": "delivery-2",
          "location": [3.7303, 51.0500],
          "arrival": "2026-04-01T08:40:00+02:00",
          "departure": "2026-04-01T08:50:00+02:00",
          "wait_s": 0,
          "service_s": 600,
          "travel_time_s": 2400,
          "load_after": { "weight": 20 }
        },
        {
          "type": "job",
          "id": "delivery-1",
          "location": [4.7005, 50.8798],
          "arrival": "2026-04-01T09:50:00+02:00",
          "departure": "2026-04-01T09:55:00+02:00",
          "wait_s": 0,
          "service_s": 300,
          "travel_time_s": 3600,
          "load_after": { "weight": 30 }
        },
        {
          "type": "end",
          "location": [4.3517, 50.8503],
          "arrival": "2026-04-01T10:30:00+02:00"
        }
      ]
    }
  ],
  "unassigned": []
}
Key differences:
  • tripsroutes; resourcevehicle object { "type": "truck-1", "instance": 1 }
  • visitsstops (typed: start, job, end, etc.)
  • V3 stops carry type, id, arrival, departure, wait_s, service_s, travel_time_s, load_after (named map)
  • V3 routes use distance_m / duration_s (not bare distance / duration)
  • unserved (string list) → unassigned (objects with job_id and reasons[])
  • New summary block with jobs_assigned/jobs_unassigned, total_distance_m, total_duration_s, estimated_cost, elapsed_ms, iterations
  • Coordinates in response are bare [longitude, latitude] arrays

V2 features not mapped via compat endpoint

When using the V2 endpoint (/v2/vrp/sync/solve), the following V2 fields are silently dropped during conversion:
  • resources[].tags — V2 skill tags are not forwarded to the V3 solver
  • relations[].resource — pinning a relation to a specific vehicle is ignored
  • windows[].hard — soft time window flag has no V3 equivalent yet
  • Per-visit serviceTime, travelTime, distance — not populated in the V2 response

Feature parity

V3 currently implements a subset of V2’s features. The rest are on the roadmap.
FeatureV2V3Notes
Capacity (single dimension)Named map e.g. { "weight": 100 }
Time windows (hard, single)
Skills / tags
Sequence / precedenceV3 type: ordered with job_ids
First-job constraintV3: ordered with a single job_ids entry
Vehicle range (max distance/duration)limits.max_distance_m, limits.max_drive_time_s
Committed vehicleeligible_vehicles.allowed
Custom distance matrixoptions.runtime.matrix
Auto distance matrix (Solvice Maps)
Multi-dimensional capacity🔜Coming soon
Multiple time windows per job🔜Coming soon
Driver breaks🔜Coming soon
Job priority🔜Coming soon
Soft time windows🔜Coming soon
Skill levels / proficiency🔜Coming soon
Job relations (same route, etc.)🔜Coming soon
Break management🔜Coming soon
Tag ranking🔜Coming soon
Inherited location🔜Coming soon
Pickup & delivery (shipments)🔜Coming soon
Workload balancing / fairness🔜Coming soon
Rules engine🔜Coming soon
Multi-day planning🔜Coming soon
Real-time re-optimization🔜Coming soon
SSE streaming progressNew in V3
Job proximity constraintNew in V3
V3 is under active development. The V2 endpoint (/v2/vrp/sync/solve) remains available and internally maps to the V3 solver — you get V3’s performance with the V2 API contract.