Pushing routes from a TMS
A TMS integration with Novil is one call per dispatched load: POST /v1/routes. Novil takes it from there, matching the truck and driver, computing the routed distance, and building and sending the fuel plan when the truck arrives at the first stop. This guide covers the details that make that one call robust.
The shape of a route
Section titled “The shape of a route”| Field | Required | What to send |
|---|---|---|
externalId |
yes | Your TMS’s identifier for the load (an order or movement id). Used for idempotency and to find the route again. Max 200 characters. |
reference |
no | The load or order number as dispatchers know it, for example Load 23787. Becomes the route’s name in Novil and pairs re-pushed loads for superseding. |
vehicle |
yes | unitNumber exactly as it appears in Novil, or a 17-character vin. |
driver |
yes | name (full name as it appears in Novil) or externalId (your driver id, when drivers are synced from a telematics provider). |
scheduledStart |
yes | When the truck should leave the first stop, RFC 3339 with an offset. |
stops |
yes | 2 to 50 stops in driving order; the last one is the destination. Each needs name, latitude and longitude; address is display-only and arrivalWindow (start, end) records an appointment. |
Novil does not geocode. If your TMS stores only addresses, geocode them before pushing.
Idempotency: the same externalId updates
Section titled “Idempotency: the same externalId updates”POST /v1/routes is idempotent by externalId. The first push creates the route and returns 201; every later push with the same externalId updates it in place and returns 200. That makes the integration simple: whenever a load changes in the TMS (new stop, new truck, new departure time), push the whole load again. There is no separate update call and no need to track what changed.
Updates are accepted while the route is held (pending*), draft or planned. Once the route is active the truck is already driving it and the stops cannot change: the push is rejected with 409 and the code route_active. For a load that changes after departure, cancel is not available either (see below); push the changed load under a new externalId so it becomes a fresh route.
Superseding: a new externalId, the same reference
Section titled “Superseding: a new externalId, the same reference”Some TMSs assign a new id when a load is re-planned or re-dispatched. If a load you already pushed comes back under a new externalId but the same reference, Novil supersedes the old draft instead of creating a duplicate, when the company has supersede enabled. Send reference consistently (the load number dispatchers use) and this happens without any extra work on your side.
The route lifecycle
Section titled “The route lifecycle”The status field tells you where a route is. The pending* statuses mean the route was accepted but is not imported yet; the rest follow the truck.
| Status | Meaning | What you see |
|---|---|---|
pending |
Accepted, import not attempted yet. | pending.reason is not_attempted. Short-lived. |
pending_mapping |
The vehicle or driver is not mapped in Novil. | 202 on push. pending.reason is vehicle_unmapped or driver_unmapped; pending.detail says which. Resolves on its own once the mapping exists. |
pending_window |
Scheduled further out than the company’s import window (7 days by default). | 202 on push. pending.reason is outside_import_window. Imported when the start enters the window. |
draft |
Imported and waiting for the truck. | fuelPlan is null; distanceMiles is set once routed. |
planned |
A fuel plan is ready. | fuelPlan holds the plan: totals and one entry per fuel stop. |
active |
The plan has been sent to the driver. | fuel_stop entries appear among stops; actualArrival and actualDeparture fill in from the truck’s GPS. |
completed |
The route is finished. | Terminal. Plan and actuals remain readable. |
error |
Import failed. | Terminal. pending.reason is error; pending.detail explains. |
canceled |
Canceled through the API or in Novil. | Terminal. |
When the fuel plan appears
Section titled “When the fuel plan appears”Novil builds the fuel plan when the truck arrives at the route’s first stop, exactly as it does for routes that come in through a telematics or TMS integration. That is when status becomes planned and fuelPlan stops being null. When the plan is sent to the driver the route becomes active and the plan’s own status becomes active. There is no separate resource to fetch: the route is the plan, and GET /v1/routes/{id} always returns the current one.
Reading the plan
Section titled “Reading the plan”fuelPlan has one entry per fuel stop: where (name, address, coordinates, milesFromOrigin), how much (gallonsToFill), at what price (pricePerGallon, net including tax, and totalCost), the expected arrivalFuelPercent and estimatedArrival. Totals and savingsPerGallon (average corridor price minus the plan’s average price) sit at the top. An empty stops list is a valid plan: the truck can reach its destination on the fuel it has. The active plan is returned once one has been sent to the driver; before that, the plan currently selected for the route. Once the route is active, the same fuel stops also appear in the route’s stops timeline as fuel_stop entries, in driving order between the dispatch stops.
Canceling a route
Section titled “Canceling a route”DELETE /v1/routes/{id} cancels a route that has not started and returns 204:
- A held route (
pending,pending_mapping,pending_window) is dropped. - A
draftorplannedroute is retired in Novil with the reason “Canceled via API”.
Active and completed routes cannot be canceled here; the call returns 409. After a cancel, pushing the same externalId again creates a fresh route rather than reviving the old one, so a “cancel and re-push” is a safe way to start over.
Finding routes again
Section titled “Finding routes again”GET /v1/routes lists routes created through the API, newest scheduled start first, with filters for status, externalId (returns at most one route), vehicleId, and a from/to range on the scheduled start. limit is 1 to 100 (default 25) and offset pages through the rest. GET /v1/routes/{id} returns one route by the id you were given at push time.
Timing recommendations
Section titled “Timing recommendations”- Push a load as soon as it is dispatched. Nothing is sent to the driver until the truck reaches the first stop, so pushing early costs nothing and gives Novil time to resolve mappings.
- Re-push on every change in the TMS. Idempotency makes this safe.
- Do not poll a route more often than every few minutes. Use
GET /v1/routes?status=pending_mappingto find routes that need a human, and?status=plannedto pick up plans.