Skip to content
novilDevelopersOpen Novil
Getting startedQuickstart

Quickstart

This walkthrough pushes one dispatched load into Novil and reads back the fuel plan it produces. Every request uses cURL against the production base URL; swap in a sandbox key to run it without reaching a driver.

In the Novil app open Settings › API Keys (app.novilfleet.com/settings/api-keys) and create a key with the scopes routes:write, routes:read, vehicles:read and drivers:read. Copy it when it is shown; it is shown once.

Keep the key in your shell for the rest of this page
export NOVIL_API_KEY="nvl_live_..."

GET /v1/me confirms the key works and tells you which company, scopes and environment it carries.

Terminal window
curl https://app.novilfleet.com/v1/me \
-H "Authorization: Bearer $NOVIL_API_KEY"
200 OK
{
"company": { "id": "titus_transportation", "name": "Titus Transportation", "timeZone": "America/Chicago" },
"key": {
"id": "ak_7Hq2mV9sKd3pL0aX",
"name": "McLeod dispatch",
"prefix": "nvl_live_7Hq2",
"environment": "live",
"scopes": ["routes:write", "vehicles:read", "drivers:read"],
"createdAt": "2026-09-02T15:04:11Z",
"expiresAt": null
},
"rateLimit": { "limit": 600, "window": "1m" }
}

If you get 401 instead, the header is missing or the key was pasted wrong. See Authentication.

Routes name their truck by unit number (or VIN) and their driver by name, matched against what Novil has. Look the unit up first so the route does not land as pending_mapping:

Terminal window
curl "https://app.novilfleet.com/v1/vehicles?unitNumber=4471" \
-H "Authorization: Bearer $NOVIL_API_KEY"
200 OK
{
"data": [
{
"id": "veh_3f9a",
"unitNumber": "4471",
"vin": "3AKJHHDR5NSMK1234",
"licensePlate": "TX R47-1122",
"make": "Freightliner",
"model": "Cascadia",
"year": "2022",
"fuelTankCapacityGallons": 240
}
],
"total": 1,
"limit": 25,
"offset": 0
}

An empty data array means Novil does not know that unit number yet. Matching vehicles and drivers explains how to fix that; you can still push the route and let Novil hold it until the mapping exists.

The same check works for drivers with GET /v1/drivers?name=Whitfield (a case-insensitive substring of the full name).

POST /v1/routes takes your load id, the truck, the driver, the departure time and at least two stops with coordinates. Novil does not geocode addresses, so latitude and longitude are required on every stop.

Terminal window
curl https://app.novilfleet.com/v1/routes \
-H "Authorization: Bearer $NOVIL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"externalId": "ord_88213",
"reference": "Load 23787",
"vehicle": { "unitNumber": "4471" },
"driver": { "name": "Dana Whitfield" },
"scheduledStart": "2026-09-14T06:00:00-05:00",
"stops": [
{ "name": "Dallas DC", "address": "2200 Irving Blvd, Dallas, TX 75207",
"latitude": 32.7876, "longitude": -96.8302 },
{ "name": "Memphis Yard", "address": "4100 Air Park St, Memphis, TN 38118",
"latitude": 35.0512, "longitude": -89.9613,
"arrivalWindow": { "start": "2026-09-14T14:00:00-05:00", "end": "2026-09-14T18:00:00-05:00" } }
]
}'
201 Created
{
"id": "9d1e3d0c-1b7a-4e0a-8a55-7b7c2b0e1f6d",
"externalId": "ord_88213",
"reference": "Load 23787",
"name": "Load 23787",
"status": "draft",
"environment": "live",
"pending": null,
"vehicle": { "id": "veh_3f9a", "unitNumber": "4471" },
"driver": { "id": "drv_a17c", "name": "Dana Whitfield" },
"scheduledStart": "2026-09-14T11:00:00Z",
"stops": [
{ "sequence": 0, "name": "Dallas DC", "address": "2200 Irving Blvd, Dallas, TX 75207",
"latitude": 32.7876, "longitude": -96.8302, "type": "waypoint",
"plannedArrival": null, "actualArrival": null, "actualDeparture": null },
{ "sequence": 1, "name": "Memphis Yard", "address": "4100 Air Park St, Memphis, TN 38118",
"latitude": 35.0512, "longitude": -89.9613, "type": "destination",
"plannedArrival": "2026-09-14T19:00:00Z", "actualArrival": null, "actualDeparture": null }
],
"distanceMiles": 452.3,
"fuelPlan": null,
"createdAt": "2026-09-13T14:42:18Z"
}

Three things to notice:

  • status is draft: the route is in Novil and waiting for the truck. Nothing has been sent to the driver yet.
  • scheduledStart came back in UTC (11:00:00Z for the 06:00-05:00 you sent).
  • id is the route’s Novil id. Keep it, or find the route again later with GET /v1/routes?externalId=ord_88213.

Had the unit number been unknown, the response would have been 202 Accepted with status: "pending_mapping" and a pending.reason of vehicle_unmapped. Sending the same externalId again updates the route and returns 200.

Novil builds the fuel plan when the truck arrives at the first stop, then sends it to the driver. Poll the route and watch status and fuelPlan:

Terminal window
curl https://app.novilfleet.com/v1/routes/9d1e3d0c-1b7a-4e0a-8a55-7b7c2b0e1f6d \
-H "Authorization: Bearer $NOVIL_API_KEY"
status Meaning
draft Imported, waiting for the truck. fuelPlan is null.
planned A fuel plan is ready. fuelPlan holds it: totals and one entry per fuel stop.
active The plan has been sent to the driver. fuel_stop entries appear among stops.
completed The route is finished.

A few polls an hour is plenty; plans do not change from minute to minute. To watch many routes at once, list by status instead: GET /v1/routes?status=planned.

The route is the plan. Once it is planned, active or completed, the same GET /v1/routes/{id} call carries the fuel stops in fuelPlan:

200 OK (abbreviated)
{
"id": "9d1e3d0c-1b7a-4e0a-8a55-7b7c2b0e1f6d",
"status": "active",
"stops": [
{ "sequence": 0, "name": "Dallas DC", "type": "waypoint", "actualDeparture": "2026-09-14T11:31:40Z" },
{ "sequence": 1, "name": "Love's #412", "type": "fuel_stop", "plannedArrival": "2026-09-14T13:10:00Z" },
{ "sequence": 2, "name": "Memphis Yard", "type": "destination", "plannedArrival": "2026-09-14T19:00:00Z" }
],
"fuelPlan": {
"status": "active",
"generatedAt": "2026-09-14T11:06:40Z",
"totalDistanceMiles": 452.3,
"totalGallons": 96.5,
"totalCost": 341.12,
"averagePricePerGallon": 3.535,
"savingsPerGallon": 0.212,
"savingsTotal": 20.46,
"stops": [
{
"sequence": 1,
"name": "Love's #412",
"address": "I-30 Exit 94, Sulphur Springs, TX",
"latitude": 33.1362,
"longitude": -95.6011,
"milesFromOrigin": 83.4,
"gallonsToFill": 96.5,
"pricePerGallon": 3.535,
"totalCost": 341.12,
"arrivalFuelPercent": 22,
"estimatedArrival": "2026-09-14T13:10:00Z"
}
]
}
}

While the route is still draft, fuelPlan is null; that is expected, not an error.