Getting startedErrors
Errors
Every error response is an RFC 9457 problem-details document with the media type application/problem+json. On top of the standard fields, Novil adds a stable, machine-readable code. Branch on code and status; title and detail are for people and may be reworded.
{ "type": "about:blank", "title": "Forbidden", "status": 403, "code": "insufficient_scope", "detail": "This endpoint needs the routes:write scope."}| Field | Type | Meaning |
|---|---|---|
type |
string | Always about:blank; the code identifies the problem instead. |
title |
string | Short human-readable summary. |
status |
integer | The HTTP status, repeated. |
code |
string | One of the codes below. Stable across releases. |
detail |
string | What went wrong with this particular request, for logs and error messages. |
errors |
array | Only on validation_error: one entry per invalid field, see below. |
Error codes
Section titled “Error codes”| Code | Status | When |
|---|---|---|
validation_error |
400 |
The request body failed validation. errors lists each field and what was wrong with it. |
unauthorized |
401 |
The API key is missing, malformed, revoked or expired. Do not retry; fix the key. |
insufficient_scope |
403 |
The key is valid but was not created with the scope this endpoint needs. detail names the scope. |
not_found |
404 |
No route with that id belongs to this company. Ids are company-scoped, so a valid id from another company is also not_found. |
route_active |
409 |
The route has already started. Stops on an active route cannot change, and active or finished routes cannot be canceled through the API. Cancel is only for pending*, draft and planned routes; for an active one, push a new route. |
rate_limited |
429 |
More than 600 requests in a minute from this key. Wait the number of seconds in the Retry-After header, then retry. See Rate limits. |
internal_error |
500 |
Something failed on Novil’s side. Nothing about the request needs to change; retry it after a short delay. |
Validation errors
Section titled “Validation errors”A 400 carries an errors array naming each field that failed, using JSON paths into the request body:
{ "type": "about:blank", "title": "Validation failed", "status": 400, "code": "validation_error", "detail": "The request body is invalid.", "errors": [ { "field": "stops", "message": "At least two stops are required." }, { "field": "stops[1].latitude", "message": "Latitude is required." } ]}The rules that most often trip a first integration, all from POST /v1/routes:
externalId,vehicle,driver,scheduledStartandstopsare required.stopsneeds at least 2 and at most 50 entries, each withname,latitudeandlongitude. Addresses are display-only; nothing is geocoded.latitudeis between -90 and 90,longitudebetween -180 and 180.vehicleneedsunitNumberor a 17-charactervin;driverneedsnameorexternalId.scheduledStartand arrival windows are RFC 3339 timestamps with an offset.externalId,referenceand stop names are at most 200 characters; addresses at most 400.
Which errors each endpoint returns
Section titled “Which errors each endpoint returns”| Endpoint | Possible errors |
|---|---|
GET /v1/me |
401 |
POST /v1/routes |
400, 401, 403, 409 route_active, 429 |
GET /v1/routes |
401, 403, 429 |
GET /v1/routes/{id} |
401, 403, 404 not_found, 429 |
DELETE /v1/routes/{id} |
401, 403, 404 not_found, 409, 429 |
GET /v1/vehicles |
401, 403, 429 |
GET /v1/drivers |
401, 403, 429 |
Handling errors well
Section titled “Handling errors well”- Treat
4xxother than429as bugs in the request or the configuration: logcodeanddetail, do not retry blindly. - Retry
429afterRetry-Afterand500with a short backoff. - A
202is not an error. It means the route was accepted and is being held (pending_mappingorpending_window); Novil retries on its own. See Matching vehicles and drivers.