Skip to main content
Kardinal’s Route Optimization API is built around two steps: you submit a plan (resources such as vehicles, orders, and constraints), and you retrieve the solution the engine computes for it. This tutorial walks through both, using the smallest possible plan.
Access to the API is self-service — see Authentication and API keys for how to sign up and what account structure it creates.

Step 1 — Authenticate

Kardinal uses JWT authentication. Every request needs a long-term API token, which you generate once from the Console and reuse as a bearer token for as long as it’s valid. Log in to the Console, then go to API Keys in the sidebar to generate your token. Keep it — every following request in this tutorial uses it as a bearer token. It’s valid for 366 days; see Authentication and API keys for the full authentication model.

Step 2 — Submit a minimal plan

A plan is created with POST /plans — the service generates the plan’s id, returned in the response; an id sent in this request is ignored and replaced by a generated one. Here is the smallest viable plan: one resource and three pickup-only orders in Paris.
Response (abridged)
Keep the id from the response — every request below uses it in place of {planId}.
maxOptimizationDuration and other duration fields use ISO 8601 duration syntax: PT1M reads as “period, time, 1 minute.”
"type": "fly" is a crow-fly vehicle profile — it’s fast to compute and ideal for a first test. To see real road-network routing instead, replace it with "vehicleProfile": { "type": "car" } (no kmph needed) and re-run the same request with a production token — the response shape is identical. The sandbox agency computes every route crow-fly, whatever the vehicle profile, so road-network routing only shows up in production. Real integrations typically use car or truck profiles (see the data model reference).
As soon as the plan is accepted, optimization starts automatically — there’s no separate “start” call.

Step 3 — Retrieve the solution

The response wraps the result in an item field and gives you, per resource, the ordered list of stops (tours[].wayPoints) with arrival/departure times, plus any stop that couldn’t be planned:
Response (abridged)
The timings follow from the plan itself: at the fly profile’s 20 km/h, the 10.5 km tour takes about 31 minutes of travel, plus 5 minutes 30 seconds at each of the three stops. unaffectedStopIds lists stops that couldn’t be planned within hard constraints, and unusedResourceIds lists resources left with nothing assigned — both empty here since this minimal plan has no constraints to violate. See Why a stop, not always the problem, becomes infeasible for the full picture. For a small test plan like this one, the solution is typically ready within seconds — for larger plans, poll GET /plans/{planId}/state until it returns state: "optimized" — the plan goes through states such as waiting, processing, and optimizing first (see Polling the state and the solution for the polling interval, the other end states, and why an early solution can be empty).

Next steps

  • PUT /plans/{planId} with the complete plan (the same resources and orders, plus your change) to see interactive re-optimization in action — read How the optimization engine works first to understand what happens on update.
  • Add time windows, capacities, and skills to your orders and resources — see the full data model reference.
  • Move from crow-fly (fly) to a real vehicle profile (car, truck) before going further than a smoke test — with a production token, since the sandbox always computes crow-fly.
  • Size maxOptimizationDuration for your real volumes: PT1M only suits this three-stop test. See Sizing maxOptimizationDuration for a large problem.
Modeling a real client’s data next, not just this tutorial? Read Start here if you’re an AI agent before writing that integration — it covers the checks and decisions that most commonly get skipped.