> ## Documentation Index
> Fetch the complete documentation index at: https://docs.solvice.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Integration paths

> Pick the reading order for your mission (add V3 to an application, migrate from V2, build a dispatch UI, or model an operation as an AI agent) and read only the pages that mission needs, in order, with the pitfall at each step.

> [!NOTE]
>
> **V3 is in beta.** General availability (GA) is planned for January 2027.

A path is a reading order for one mission. Each step names the page to read, the call to make, and the mistake it tends to produce. The "Skip" line at the end of each path names the pages you do not need.

On every path ([Requests](https://docs.solvice.io/api-reference/vrp/v3/introduction.md#requests)): the base URL is `https://api.solvice.io`, the key goes in `Authorization` with no `Bearer` prefix, and a field the schema does not define is `400` with item code `unknown_field`.

## Add V3 to an existing application

Use this path when the application exists and V3 should plan its routes: a synchronous `POST /v3/routing/solve` first, then `evaluate` and `suggest`.

1. Read [Authentication](https://docs.solvice.io/api-reference/vrp/v3/introduction.md#authentication) and [Requests](https://docs.solvice.io/api-reference/vrp/v3/introduction.md#requests). Write coordinates as `[longitude, latitude]`. Pitfall: a `401` comes from the gateway with an empty body or one that is not the error body; branch on the HTTP status before you parse JSON.
2. Read [Your first solve](https://docs.solvice.io/guides/vrp/v3/quickstart.md#your-first-solve). Send a minimal `problem` with `jobs[]` and `vehicles[]` to `POST /v3/routing/solve` with `options.time_limit_s` of a few seconds. Pitfall: a timestamp without an offset returns `400` with a `pointer` to the field; write `+02:00` or `Z` on every `from`, `to` and window.
3. Read [A request at a glance](https://docs.solvice.io/guides/vrp/v3/api-design.md#a-request-at-a-glance), then [Jobs](https://docs.solvice.io/guides/vrp/v3/api-design.md#jobs) and [Vehicles and shifts](https://docs.solvice.io/guides/vrp/v3/api-design.md#vehicles-and-shifts). One `jobs[]` entry per visit, one `shipments[]` entry per pickup-then-delivery pair, one `shifts[]` entry per vehicle per working day, each with an `id`. Pitfall: a field name carried over from another routing API returns `400` with item code `unknown_field`; fix the field at the `pointer` using the reference's name.
4. Read [Capacity](https://docs.solvice.io/guides/vrp/v3/constraints.md#capacity), [Time windows](https://docs.solvice.io/guides/vrp/v3/constraints.md#time-windows) and [Skills](https://docs.solvice.io/guides/vrp/v3/constraints.md#skills). Turn on only the rules your data asserts. Pitfall: a misspelt capacity dimension silently gives the vehicle capacity `0` in it, and the job comes back in `unassigned[]` with `capacity`; match names exactly, and omit `capacity` rather than send `{}` to mean "no limit".
5. Read [Objective](https://docs.solvice.io/guides/vrp/v3/api-design.md#objective) and [the V3 defaults](https://docs.solvice.io/guides/vrp/v3/concepts/scoring-explanation.md#if-you-set-nothing-the-v3-defaults). Pitfall: an omitted `objective` silently prices every route at 35 per route hour and 0.20 per kilometre; set `objective.costs` to your rates before comparing plans.
6. In CI, send every request to `POST /v3/routing/validate/solve` ([Dry run](https://docs.solvice.io/api-reference/vrp/v3/introduction.md)): `200 {"valid": true, "warnings": []}` or the real call's `400`, not billed. Pitfall: validate checks `problem` and `options` only; a bad warm-start `plan` and coordinates spanning more than one region are reported by the real call alone.
7. Read [Response](https://docs.solvice.io/guides/vrp/v3/api-design.md#response) and [Unassigned tasks](https://docs.solvice.io/guides/vrp/v3/api-design.md#unassigned-tasks). Pitfall: a plan that leaves tasks out is still HTTP `200`, so a client that checks the status alone silently ships an incomplete plan; check `summary.tasks.unassigned_required`, then `unassigned[].category` and `reasons[].constraint`.
8. Read [Solve time](https://docs.solvice.io/guides/vrp/v3/limits.md#solve-time) and [Client timeout](https://docs.solvice.io/guides/vrp/v3/concepts/performance-guide.md#client-timeout). Pitfall: `time_limit_s` bounds the search only, so an HTTP timeout equal to it silently aborts requests about to succeed; set it to the budget plus the measured overhead. A budget above `60` returns `400` with item code `limit_exceeded` at `/options/time_limit_s`; use an [async run](https://docs.solvice.io/guides/vrp/v3/runs.md) instead.
9. Read [Evaluate](https://docs.solvice.io/guides/vrp/v3/evaluate.md#overview) and [Violations](https://docs.solvice.io/guides/vrp/v3/evaluate.md#violations). Send `problem` plus a returned `plan` to `POST /v3/routing/evaluate` to score a hand-edited plan. Pitfall: evaluate takes no `options`; sending one returns `400` with item code `unknown_field`. A plan with violations is `200`: read `summary.score.violation_count`.
10. Read [The request](https://docs.solvice.io/guides/vrp/v3/suggest.md#the-request) and [Applying a suggestion](https://docs.solvice.io/guides/vrp/v3/suggest.md#applying-a-suggestion). Add the new task to `problem`, leave it out of `plan`, and call `POST /v3/routing/suggest`. Pitfall: an empty `insertions` list is `200` with a `reason.code`, and only `infeasible` is final; raise `options.time_limit_s` for `time_limit` and `options.max_insertions` for `candidates_exhausted`.
11. Read [What your client must do](https://docs.solvice.io/guides/vrp/v3/versioning.md#what-your-client-must-do). Pitfall: a generated client with closed enums fails on a new `category`, `constraint` or error `code`; accept unknown values and never parse `message`.

Skip: [Migration from V2](https://docs.solvice.io/guides/vrp/v3/migration-from-v2.md) and the V2 tab, [Async runs](https://docs.solvice.io/guides/vrp/v3/runs.md) unless a problem has more than 400 tasks or needs more than 60 seconds, and the Routing Concepts pages beyond the two sections above. Return to [Constraints](https://docs.solvice.io/guides/vrp/v3/constraints.md) one rule at a time.

## Migrate from V2

Use this path when you call `/v2/vrp/*` today. [How to port a request](https://docs.solvice.io/guides/vrp/v3/migration-from-v2.md#how-to-port-a-request) is the procedure; this path adds the pitfalls.

1. Read [Overview](https://docs.solvice.io/guides/vrp/v3/migration-from-v2.md#overview) and [Endpoints and lifecycle](https://docs.solvice.io/guides/vrp/v3/migration-from-v2.md#endpoints-and-lifecycle). `POST /v2/vrp/sync/solve` becomes `POST /v3/routing/solve`; the job lifecycle (`POST /v2/vrp/solve`, `GET /v2/vrp/jobs/{id}`) becomes `POST /v3/routing/runs` and `GET /v3/routing/runs/{id}`. Pitfall: the V2 `hook` has no counterpart; a body with `callback` returns `400` with item code `not_supported` at `/callback`; poll the run.
2. Read [Conventions](https://docs.solvice.io/guides/vrp/v3/migration-from-v2.md#conventions). Pitfall: V2 read a timestamp without an offset as UTC and accepted a zone id such as `[Europe/Brussels]`; V3 returns `400` for both; send the part before `[`, with its offset. Coordinates become `[longitude, latitude]`; a swapped pair is not an error, so every job silently comes back `unassigned` with `unreachable`.
3. Read [V2 fields with no direct V3 equivalent](https://docs.solvice.io/guides/vrp/v3/migration-from-v2.md#v2-fields-with-no-direct-v3-equivalent) and [Feature parity](https://docs.solvice.io/guides/vrp/v3/migration-from-v2.md#feature-parity). Decide each untranslated field's replacement before you rewrite anything. Pitfall: a leftover V2 key returns `400` with item code `unknown_field`; the `pointer` names it.
4. Read [Vehicles](https://docs.solvice.io/guides/vrp/v3/migration-from-v2.md#vehicles-resources), [Shifts](https://docs.solvice.io/guides/vrp/v3/migration-from-v2.md#shifts) and [Jobs](https://docs.solvice.io/guides/vrp/v3/migration-from-v2.md#jobs). Wrap the problem in `problem`, rename to snake\_case with unit suffixes, give every shift an `id`. Pitfall: capacity moves from a positional array of up to 5 to a named map of up to 4 dimensions; a fifth name returns `400` with item code `limit_exceeded` at `/problem/vehicles`.
5. Read [Relations](https://docs.solvice.io/guides/vrp/v3/migration-from-v2.md#relations) and [Pickup and delivery pairs](https://docs.solvice.io/guides/vrp/v3/migration-from-v2.md#pickup-and-delivery-pairs). Each `PICKUP_AND_DELIVERY` pair becomes a `shipments[]` entry. Pitfall: a relation that names the shipment's own id returns `400` with item code `not_supported`; name its legs `{shipment}:pickup` and `{shipment}:delivery`.
6. Read [Default objective](https://docs.solvice.io/guides/vrp/v3/migration-from-v2.md#default-objective) and [From weights to prices](https://docs.solvice.io/guides/vrp/v3/migration-from-v2.md#from-weights-to-prices). Pitfall: a native V3 request without `objective` is silently optimised for the V3 defaults, not for what your V2 weights expressed; set each price in `objective.costs` and on the fields (`drop_cost`, `per_late_hour`).
7. Read [Options](https://docs.solvice.io/guides/vrp/v3/migration-from-v2.md#options). `?millis=5000` becomes `options.time_limit_s: 5`. Pitfall: V2 priced TomTom traffic at request time, while a V3 request without `travel.departure_time` silently defaults to free-flow `osm`; set `travel.engine: "tomtom"` to keep the V2 behaviour. A budget above 60 seconds, which V2 lowered, returns `400` with item code `limit_exceeded`.
8. Read [Existing plans](https://docs.solvice.io/guides/vrp/v3/migration-from-v2.md#existing-plans). Replace `initialResource` and `initialArrival` with a request-level `plan` whose routes name `vehicle` and `shift`. Pitfall: a `plan` freezes nothing, so dispatched stops silently move on re-solve; set `plan.routes[].locked_count`.
9. Send the ported body to `POST /v3/routing/validate/solve` and fix each `errors[]` item at its `pointer`, then solve the same instance on both APIs and read it with [Response mapping](https://docs.solvice.io/guides/vrp/v3/migration-from-v2.md#response-mapping). Pitfall: V2 response times are UTC, V3 writes them in the offset of the shift's `from`; a parser that assumes `Z` silently shifts every arrival. Compare `unassigned[]`, route totals and stop order before switching traffic.

Skip: [Quickstart](https://docs.solvice.io/guides/vrp/v3/quickstart.md) beyond [What's different from V2](https://docs.solvice.io/guides/vrp/v3/quickstart.md#whats-different-from-v2), the V2 tab, and [AI agents](https://docs.solvice.io/guides/vrp/v3/ai-agents.md).

## Build a dispatch UI with warm starts and re-planning

Use this path when dispatchers edit routes during the day. The `plan` object is the state your UI holds: the API keeps none.

1. Read [Plan](https://docs.solvice.io/guides/vrp/v3/api-design.md#plan) and [Warm-starting with `plan`](https://docs.solvice.io/guides/vrp/v3/api-design#warm-starting-with-plan). Store `plan` from every response. Pitfall: on `solve`, `plan` is a hint; an unlocked stop that breaks a hard rule is silently moved or dropped to `unassigned[]`; set `locked_count` on the dispatched prefix, or `"pinned": true` on a stop that must keep its vehicle.
2. Re-plan by sending `problem` plus the edited `plan` to `POST /v3/routing/solve`. Pitfall: a locked or pinned stop that breaks a hard rule returns `400` with item code `invalid_value` at `/plan`; send the same body to `POST /v3/routing/evaluate` to see which rule.
3. Read [The plan shape](https://docs.solvice.io/guides/vrp/v3/evaluate.md#the-plan-shape) and [Violations](https://docs.solvice.io/guides/vrp/v3/evaluate.md#violations). After each drag-and-drop, call `POST /v3/routing/evaluate` and render `routes[].stops[].violations`, `routes[].violations` and `summary.score.violation_count`. Pitfall: a window priced with `per_late_hour` is not a violation when missed, so a UI that renders `violations[]` alone silently hides late stops; render `stops[].lateness_s` too.
4. Read [What evaluate checks, and when](https://docs.solvice.io/guides/vrp/v3/evaluate.md#what-evaluate-checks-and-when). Pitfall: an unknown stop id returns `400` with item code `unknown_reference`, a stop listed twice `duplicate_id`, a shipment split across routes `invalid_value`, all at `/plan/routes/{i}/stops/{k}`, and the real call checks these when it imports the plan; check the plan in your UI first.
5. Read [Reading an insertion](https://docs.solvice.io/guides/vrp/v3/suggest.md#reading-an-insertion) and [Applying a suggestion](https://docs.solvice.io/guides/vrp/v3/suggest.md#applying-a-suggestion). For one new order, add it to `problem`, leave it out of `plan`, call `POST /v3/routing/suggest`, and show `placements[].service_start` and `delta.cost` per insertion. Pitfall: each task is ranked against the plan you sent, so applying one insertion silently invalidates the others; call suggest again with the updated plan.
6. Read [When a task gets no insertions](https://docs.solvice.io/guides/vrp/v3/suggest.md#when-a-task-gets-no-insertions). Branch on `reason.code`, never on `reason.message`. Pitfall: `options.max_insertions` above `10` returns `400` with item code `limit_exceeded`, and `0` returns `out_of_range`; neither is clamped.
7. Read [Shipments and related tasks](https://docs.solvice.io/guides/vrp/v3/suggest.md#shipments-and-related-tasks) if orders are shipments or crews. Pitfall: a shipment with one leg in `plan` returns `400`; leave both legs out.
8. Read [Submit a run](https://docs.solvice.io/guides/vrp/v3/runs.md#submit-a-run), [Poll a run](https://docs.solvice.io/guides/vrp/v3/runs.md#poll-a-run) and [Fetch the plan](https://docs.solvice.io/guides/vrp/v3/runs.md#fetch-the-plan). For the overnight plan, or more than 400 tasks, `POST /v3/routing/runs` with the solve body, store the `id`, poll `GET /v3/routing/runs/{id}` until `status` is final, then `GET /v3/routing/runs/{id}/result`. Pitfall: 401 or more tasks on `POST /v3/routing/solve` returns `400` with item code `limit_exceeded` at `/problem/jobs`; `/result` before `succeeded` returns `409` with a `message` starting `run_not_finished`.
9. Read [Cancel a run](https://docs.solvice.io/guides/vrp/v3/runs.md#cancel-a-run) and [Limits](https://docs.solvice.io/guides/vrp/v3/runs.md#limits). Pitfall: a cancelled run counts toward the 10 queued-or-running runs until its worker stops; the eleventh submission returns `429` with `Retry-After: 30`.
10. Read [Re-optimizing from an existing plan](https://docs.solvice.io/guides/vrp/v3/concepts/performance-guide.md#re-optimizing-from-an-existing-plan) and [See how fast the search converges](https://docs.solvice.io/guides/vrp/v3/concepts/performance-guide.md#see-how-fast-the-search-converges). Size `options.time_limit_s` for re-plans from `search_progress`. Pitfall: an omitted `seed` is `42`, so a "try again" button silently returns the same plan; send a different `seed`. Call the API from your server, never from the browser ([Server-side proxy pattern](https://docs.solvice.io/guides/platform/authentication.md#server-side-proxy-pattern)).

Skip: [Migration from V2](https://docs.solvice.io/guides/vrp/v3/migration-from-v2.md), [Constraints](https://docs.solvice.io/guides/vrp/v3/constraints.md) beyond the rules dispatchers can break, and [AI agents](https://docs.solvice.io/guides/vrp/v3/ai-agents.md). [Field service day](https://docs.solvice.io/guides/vrp/v3/examples/field-service.md#score-a-manual-change-with-evaluate) walks evaluate and suggest on one dataset.

## Model a customer's operation as an AI agent

Use this path when an agent turns a description of an operation into requests.

1. Read [AI agents](https://docs.solvice.io/guides/vrp/v3/ai-agents.md): what to ask before you model, and what never to default.
2. Read the [Constraints overview](https://docs.solvice.io/guides/vrp/v3/constraints.md#overview) table and map each business rule to one field, hard or soft. Pitfall: a rule the schema has no field for cannot be expressed by inventing one; that returns `400` with item code `unknown_field`. Tell the user what cannot be modelled instead of approximating it silently.
3. Send every request to `POST /v3/routing/validate/solve` before `POST /v3/routing/solve`. It is not billed. Pitfall: `validate` does not read `plan`; the real call checks a warm start.
4. Read [Unassigned jobs](https://docs.solvice.io/guides/vrp/v3/errors.md#unassigned-jobs) and [Unassigned tasks](https://docs.solvice.io/guides/vrp/v3/api-design.md#unassigned-tasks). Pitfall: a `relaxations[]` entry widens a window or reduces a load; applying it silently changes the customer's rule. Surface it and let the user decide.
5. Read [Problem size](https://docs.solvice.io/guides/vrp/v3/limits.md#problem-size). Pitfall: more than 400 tasks on `POST /v3/routing/solve` returns `400` with item code `limit_exceeded`; submit a [run](https://docs.solvice.io/guides/vrp/v3/runs.md#submit-a-run) instead.

Skip: [Migration from V2](https://docs.solvice.io/guides/vrp/v3/migration-from-v2.md) unless the customer calls `/v2/vrp/*` today, the V2 tab, and the full OpenAPI document: these pages name every field you need.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.