Skip to content
novilDevelopersOpen Novil
GuidesMatching vehicles and drivers

Matching vehicles and drivers

A pushed route names its truck and driver the way your TMS knows them. Novil resolves those names to its own vehicle and driver records through the same mappings fuel-card transactions use, so once a unit or a driver is mapped for one purpose it is mapped for all of them.

You send Matched against
vehicle.unitNumber The unit number exactly as it appears in Novil.
vehicle.vin The 17-character VIN, for fleets whose unit numbers differ between systems.
driver.name The driver’s full name as it appears in Novil (the name field on GET /v1/drivers).
driver.externalId The telematics provider’s driver id, when drivers are synced to Novil from that provider.

Send one of the two fields for each. VIN and externalId are the more stable choices when you have them; unit numbers and names are what most TMSs have.

Two read-only endpoints let you see how a truck or driver will match:

Exact unit number or VIN
curl "https://app.novilfleet.com/v1/vehicles?unitNumber=4471" -H "Authorization: Bearer $NOVIL_API_KEY"
curl "https://app.novilfleet.com/v1/vehicles?vin=3AKJHHDR5NSMK1234" -H "Authorization: Bearer $NOVIL_API_KEY"
Case-insensitive substring of the driver's full name
curl "https://app.novilfleet.com/v1/drivers?name=whitfield" -H "Authorization: Bearer $NOVIL_API_KEY"
200 OK
{
"data": [{ "id": "drv_a17c", "name": "Dana Whitfield", "displayName": "Dana W.", "externalId": null }],
"total": 1,
"limit": 25,
"offset": 0
}

Both lists are paged with limit (up to 100) and offset. Pulling the full vehicle list once and reconciling it against your fleet is a good first step in any rollout: every unit number that is in your TMS but not in Novil will become a pending_mapping route the first time it is dispatched.

If the vehicle or driver is unknown, POST /v1/routes still succeeds: it returns 202 Accepted, assigns a route id, and holds the route with status: "pending_mapping":

202 Accepted
{
"id": "b4f1c1e2-6f4b-4a8c-9a3e-2c5d8e7f1a90",
"externalId": "ord_88214",
"reference": "Load 23788",
"name": "Load 23788",
"status": "pending_mapping",
"environment": "live",
"pending": {
"reason": "vehicle_unmapped",
"detail": "Unit number 4488 is not mapped to a vehicle. Map it under Settings › Vehicle Matching."
},
"vehicle": { "id": null, "unitNumber": "4488" },
"driver": { "id": "drv_a17c", "name": "Dana Whitfield" },
"scheduledStart": "2026-09-15T11:00:00Z",
"stops": [
{ "sequence": 0, "name": "Dallas DC", "type": "waypoint" },
{ "sequence": 1, "name": "Memphis Yard", "type": "destination" }
],
"distanceMiles": null,
"fuelPlan": null,
"createdAt": "2026-09-13T14:42:17Z"
}

pending.reason is vehicle_unmapped or driver_unmapped, and pending.detail names the value that did not match. The matched side (here the driver) already has its Novil id.

Someone with access to the Novil app maps the value under Settings › Vehicle Matching or Settings › Driver Matching, pointing the unmapped unit number or name at the right record. Novil then imports the held route on its next pass, which runs every five minutes. Nothing needs to be resent: the route keeps its id and moves to draft on its own.

The mapping is remembered, so every later route with the same unit number or name matches immediately.

Your integration does not need to do anything about a 202, but dispatchers usually want to know. Two options:

  • Surface the 202 and its pending.detail in the TMS, so whoever dispatched the load sees that Novil needs a mapping.
  • Periodically list GET /v1/routes?status=pending_mapping and raise the ones that have been waiting.

Poll GET /v1/routes/{id} (or the list) to see the status change; there is no need to re-push.

Routes scheduled further out than the company’s import window, 7 days by default, are accepted with 202 as pending_window with pending.reason of outside_import_window. They are imported automatically when their scheduledStart enters the window. Push loads as soon as they are dispatched regardless of how far out they are; the window is handled for you.

pending.reason Status Meaning
vehicle_unmapped pending_mapping vehicle.unitNumber or vehicle.vin did not match a vehicle.
driver_unmapped pending_mapping driver.name or driver.externalId did not match a driver.
outside_import_window pending_window scheduledStart is beyond the import window.
not_attempted pending Accepted, import not tried yet.
error error Import failed; pending.detail explains.