N-Ways
Developer docsReference overview

API reference

Every endpoint of the N-Ways Maps Platform, its parameters, its answer and its errors, generated from our OpenAPI files.

On this page

N-Ways Maps Platform — Maps, Places and Routes API

The stable partner surface of the N-Ways Maps Platform (/maps/v1). It is the source of truth for the developer docs and the client libraries; the server's golden-file contract tests (backend/internal/partnerapi/testdata/golden) pin every answer described here.

Auth. Every call needs a partner API key: X-NWays-Api-Key: nw_… (servers, apps) or ?key=nw_… (browsers, map styles). A key's API restrictions name products; its application restriction (websites, iOS bundle ids, Android package + SHA-1, server IP addresses) says where it may be used. Apps send X-Ios-Bundle-Identifier, or X-Android-Package and X-Android-Cert. Keys work only while N-Ways has the platform switched on: while it is off every call answers 503 PLATFORM_DISABLED (with Retry-After; your key is fine), and while N-Ways keeps it to invited organisations, a key of any other organisation answers 403 PLATFORM_PREVIEW_ONLY.

Products and billing (SKUs). tiles (map tiles; SKUs tiles per tile and map_loads per style read), static_maps, geocode (geocoding, reverse, postcodes, GhanaPost, Plus Codes; a batch counts each code), autocomplete (billed per session), address_validation, search (places; SKUs search basic, places_contact, places_atmosphere by the fields asked for), routing (SKUs routing, and routing_advanced for traffic-aware routes, okada/keke/danfo/motorbike/van/boat/train, more than 10 intermediates or fares), route_matrix (billed per element), route_optimization, fares, traffic. Fonts are free with any Maps key.

Conventions. Coordinates go in as {"lat": 6.45, "lng": 3.39} (or lat,lng in a query) and come out as GeoJSON [lng, lat]. Distances in metres, durations in seconds, money in the currency's main unit. lang=en|fr|pt (pt is European Portuguese). fields= (a comma list; a.b reaches into objects) keeps only those fields of each result and, for places, sets the SKU. Fields are only ever added within v1; breaking changes come as /maps/v2 with 12 months' overlap.

Errors. One envelope for key, quota and product errors: {"error": {"code": 400, "status": "INVALID_ARGUMENT", "reason": "LAT_LNG_REQUIRED", "message": "…", "details": […]}}. reason is stable (act on it); message says exactly what happened and how to fix it; details holds Google's ErrorInfo (the reason, domain: nways.ng, and metadata with the facts: docs, and per reason what was seen and what's allowed, which cap and when it resets) and a Help link to the reason's entry on https://nways.ng/developers/errors. Refusals carry CORS headers for the calling page, so browser code can read them. Key, platform and quota reasons, in the order they are checked: PLATFORM_DISABLED (503), API_KEY_MISSING, API_KEY_INVALID (a key N-Ways never issued), API_KEY_REVOKED (deleted, or the old value of a regenerated key) (401); PLATFORM_PREVIEW_ONLY, ORGANISATION_CLOSED, ORGANISATION_SUSPENDED, API_ACCESS_OFF (the organisation runs its deliveries on N-Ways without the API), API_TURNED_OFF (N-Ways turned the organisation's API off; the message gives the reason), PROJECT_SUSPENDED, API_KEY_PAUSED, API_KEY_SERVICE_BLOCKED, API_KEY_HTTP_REFERRER_BLOCKED, API_KEY_IP_ADDRESS_BLOCKED, API_KEY_IOS_APP_BLOCKED, API_KEY_ANDROID_APP_BLOCKED (403); RATE_LIMIT_EXCEEDED, QUOTA_EXCEEDED, TEMPORARILY_BLOCKED (429); BILLING_DISABLED (402: the free monthly allowance is used up and there's no credit left: add credit in the console, Billing); BACKEND_ERROR (503). Product reasons are listed with each operation. Any operation may also answer NOT_FOUND (an unknown path), INVALID_JSON and BODY_TOO_LARGE (a request body), INTERNAL (500: send the X-Request-Id) and, where routes are planned, ROUTING_UNAVAILABLE (503, with Retry-After). Okada, motorbike, keke and danfo trips the law keeps off answer 422 with OKADA_BANNED_ORIGIN, OKADA_BANNED_DESTINATION, OKADA_BANNED_STOP, OKADA_BANNED_ROUTE; MOTORBIKE_BANNED_ORIGIN, MOTORBIKE_BANNED_DESTINATION, MOTORBIKE_BANNED_STOP, MOTORBIKE_BANNED_ROUTE; KEKE_BANNED_ORIGIN, KEKE_BANNED_DESTINATION, KEKE_BANNED_STOP, KEKE_BANNED_ROUTE; DANFO_BANNED_ORIGIN, DANFO_BANNED_DESTINATION, DANFO_BANNED_STOP, DANFO_BANNED_ROUTE (the ban is in the message).

Headers. Every answer carries X-Request-Id (send it to support). Allowed calls carry X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset (seconds); a 429 and PLATFORM_DISABLED carry Retry-After.

Hosts. api.nways.ng/maps/v1 for everything. Hosted styles read the PMTiles archives and fonts from the CDN tiles.nways.ng/maps/v1 (/tiles/{tileset}.pmtiles, /tiles/countries.json, /fonts/…, same paths): a public copy, with no key there and nothing counted; your key is checked and the map load counted when the style is read. One-tile .mvt URLs stay on the API, keyed. When the routing engine is down, routes, matrices and optimisation answer 503 ROUTING_UNAVAILABLE with Retry-After: send the same request again.

Rate limits (defaults per key and minute): tiles 6,000, static_maps 600, geocode 600, autocomplete 1,200, address_validation 300, search 600, routing 300, route_matrix 3,000 elements, route_optimization 60, fares 120, traffic 60.

Attribution. Answers built on OpenStreetMap carry attribution; show "© OpenStreetMap contributors" wherever you show the data (the ODbL requires it). See https://nways.ng/developers/terms.

Base addresshttps://nways.ng/maps/v1
Version1.0.0
OpenAPI filepartner-v1.openapi.yaml · JSON
EndpointsDescription
GET /styles/{style}.jsonA hosted map style (one map load)
GET /tiles/countries.jsonEvery country's tile archives
GET /tiles/{tileset}.pmtilesRead a country's PMTiles archive (range requests)
GET /tiles/{tileset}/{z}/{x}/{y}.mvtOne vector tile
GET /fonts/{stack}/{range}.pbfFont glyphs for map labels (free)
GET /staticmapA map image
GET /geocodeSearch for places, addresses and codes
GET /reverseWhat's at a point
GET /postcodes/{code}A NIPOST postcode
POST /postcodes/batchUp to 100 NIPOST postcodes at once
GET /ghanapost/{code}A GhanaPost GPS address
GET /pluscodes/{code}A Plus Code's area and what's there
GET /autocompleteSuggestions as someone types (billed per session)
POST /addresses/validateIs this address real, where is it, what is it properly?
GET /places/nearbyBusinesses and places near a point
GET /places/searchBusinesses and places by text
GET /places/{id}One place's details
POST /routes/computeA route, with up to 25 intermediates
POST /routes/matrixTravel times from many origins to many destinations (per element)
POST /routes/optimizeWhich vehicle visits which stops, in what order
GET /fares/estimateWhat a ride costs
GET /trafficLive traffic in an area

N-Ways Maps Platform — Fleet and logistics API

The logistics suite of the N-Ways Maps Platform (partner products fleet and traffic_feed). Every call needs a partner API key that includes the product; keys work only while N-Ways has partner access switched on for you.

Auth. Send the key as X-NWays-Api-Key: nw_… (servers, rider apps) or ?key=nw_…. Fleet calls write your own data (vehicles, tasks, webhooks), so use a key restricted to your servers' IP addresses or your rider app (Android package + SHA-1, iOS bundle id). Browsers: only the public tracking page.

Errors. Key and quota errors and product errors share one envelope: {"error": {"code": 404, "status": "NOT_FOUND", "reason": "TASK_NOT_FOUND", "message": "…", "details": […]}} (details: Google's ErrorInfo with the facts in metadata and a Help link to the reason on https://nways.ng/developers/errors). Platform and key reasons: PLATFORM_DISABLED (503, Retry-After: the platform is switched off, your key is fine), PLATFORM_PREVIEW_ONLY, API_KEY_MISSING, API_KEY_INVALID, API_KEY_REVOKED, ORGANISATION_CLOSED, ORGANISATION_SUSPENDED, API_ACCESS_OFF, API_TURNED_OFF, PROJECT_SUSPENDED, API_KEY_PAUSED, API_KEY_SERVICE_BLOCKED, API_KEY_*_BLOCKED, BILLING_DISABLED, RATE_LIMIT_EXCEEDED, QUOTA_EXCEEDED, TEMPORARILY_BLOCKED, BACKEND_ERROR. BILLING_DISABLED is a 402: the free monthly allowance is used up and there's no credit left (add credit in the console, Billing).

Features N-Ways turned off. N-Ways can turn one feature off for an organisation (403, the message says why and whom to contact; nothing else changes): DELIVERIES_OFF (new tasks), CONTRACTORS_OFF (sending tasks to N-Ways drivers, inviting or hiring them), SMART_PRICE_OFF (pay suggestions and Smart Price: send payMax yourself), WEBHOOKS_OFF (no webhook sends; adding or testing an endpoint is refused), ERP_OFF (the events export), TRACKERS_OFF (adding trackers, and their positions and alarms; the trackers stay registered). A suspended organisation's new tasks are refused with ORGANISATION_SUSPENDED, like its keys; tasks under way carry on.

Rate limits. Defaults per key and minute: fleet 6,000, traffic_feed 60. Answers carry X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset.

Ids. Vehicles, drivers and tasks are addressed by your own ids (1–64 of A–Z a–z 0–9 _ . : -), unique per project. What N-Ways makes (webhooks, alert rules and alerts, rider links and invites, proofs, webhook deliveries) has an opaque string id: a prefix, _ and 10 letters or digits (wh_Ub3yH9sLk2, rule_…, alr_…, lnk_…, rin_…, prf_…, dlv_…). Treat ids as strings and never parse them. The older numbered forms (wh_12, rule_7, inv_3…) are still accepted in paths until 1 April 2027. Times are RFC 3339 in UTC.

Data terms. Positions you send are kept as trip history for retentionDays (30 by default, 1–90), then deleted. ETA and traffic figures serve the trip only: they are cleared when a task ends. Your positions feed N-Ways' public live traffic only if you opt in (shareTraffic, off by default); then they are used anonymously, like any phone's (a session per vehicle and day, never your ids). The traffic feed is aggregated over many devices and never contains a trace.

Base addresshttps://nways.ng/maps/v1/fleet
Version1.0.0
OpenAPI filepartner-fleet.openapi.yaml · JSON
EndpointsDescription
GET /settingsGet the project's fleet settings
PUT /settingsChange fleet settings (fields left out keep their value)
GET /driversList drivers
POST /driversAdd a driver
GET /drivers/{id}Get a driver
PATCH /drivers/{id}Change a driver
DELETE /drivers/{id}Delete a driver (vehicles keep running without one)
GET /vehiclesList vehicles with their last known positions
POST /vehiclesAdd a vehicle
GET /vehicles/{id}Get a vehicle and its last known position
PATCH /vehicles/{id}Change a vehicle (driverId "" removes the driver)
DELETE /vehicles/{id}Delete a vehicle and its trip history
GET /vehicles/{id}/historyA vehicle's trip history (oldest first)
GET /vehicles/{id}/eventsA vehicle's timeline from its tracker (ignition on and off, power cut and back), newest first
POST /locationsSend positions (batched)
GET /tasks/{id}/proofsThe task's proof of delivery (photos, signatures) and the rider's note
GET /proofs/{proofId}A proof's image (JPEG, PNG or WebP), kept as long as its task
GET /tasksList tasks, newest first
POST /tasksCreate a delivery
GET /tasks/{id}Get a task with its live ETA
PATCH /tasks/{id}Edit a delivery
POST /tasks/{id}/assignAssign (or reassign) a vehicle — created|assigned → assigned; event task.assigned
POST /tasks/{id}/pickupMark picked up — assigned → picked_up
POST /tasks/{id}/completeMark delivered — assigned|picked_up|arriving → completed; event task.completed
POST /tasks/{id}/failMark failed — any open status → failed; event task.failed
POST /tasks/{id}/cancelCancel — any open status → cancelled
POST /tasks/{id}/pin-emailSend the customer's delivery PIN to their email
GET /whatsapp/numberWhether a delivery PIN can go to a number on WhatsApp
POST /tasks/{id}/areaSet or clear a delivery's own area
GET /placesList your pickup places (the default first)
POST /placesAdd a pickup place (up to 200)
GET /places/{placeId}Get a pickup place
PATCH /places/{placeId}Edit a pickup place
DELETE /places/{placeId}Delete a pickup place
GET /customersList your customers, most recent delivery first
GET /customers/{customerId}Get a customer
DELETE /customers/{customerId}Forget a customer and their addresses
GET /customers/{customerId}/addressesList a customer's addresses, last used first
POST /tasks/{id}/shareThe recipient's tracking link (a new one if it expired; none once the task ended)
GET /webhooksList webhook endpoints
POST /webhooksAdd an endpoint (up to 10); the answer has its signing secret, shown once
PATCH /webhooks/{id}Change an endpoint's url, events or active
DELETE /webhooks/{id}Delete an endpoint and its delivery log
POST /webhooks/{id}/rotate-secretA new signing secret (shown once); the old one stops at once
POST /webhooks/{id}/testQueue a ping event to the endpoint
GET /webhooks/{id}/deliveriesThe endpoint's delivery log, newest first (kept 30 days)
POST /webhooks/{id}/deliveries/{deliveryId}/retrySend a delivery again now (failed, or delivered and wanted again); its attempts are kept
GET /alert-rulesList alert rules
POST /alert-rulesAdd an alert rule (up to 100)
GET /alert-rules/{ruleId}Get a rule
PATCH /alert-rules/{ruleId}Change a rule (the kind stays); active false pauses it and resolves its open alerts
DELETE /alert-rules/{ruleId}Delete a rule (its open alerts are resolved)
GET /alert-eventsAlerts raised, newest first
GET /alert-events/{alertId}Get an alert
POST /alert-events/{alertId}/acknowledgeAcknowledge an open alert (no more reminders; it stays until it clears or is resolved)
POST /alert-events/{alertId}/resolveResolve an alert (sends asset.alert_resolved)
GET /events/exportDownload every event (CSV for Excel or Google Sheets, or JSON)
GET /traffic-feedRoad speeds in a box (product traffic_feed; enterprise)
GET /offline/regionsRegions offline packs are made for
GET /offline/packs/{region}A region's pack manifest (202 while it's built; ask again after Retry-After)
GET /offline/packs/{region}/{file}Download a pack file (Range requests supported; ETag = its hash)
GET /trackersList your trackers, with counts by state
POST /trackersAdd a tracker by its IMEI (first claim wins)
GET /trackers/catalogThe device catalogue, your country's SIM networks and their APNs, and where devices report
POST /trackers/importAdd many trackers from a CSV (check it first with dryRun)
GET /trackers/setup.csvThe installers' sheet, every tracker's setup SMS in order (CSV)
GET /trackers/forwardingForwarding from your tracking platform, its addresses, and the units it sends
POST /trackers/forwardingTurn forwarding on, or give it a new token (the old one stops at once)
DELETE /trackers/forwardingTurn forwarding off (the units stay listed)
POST /trackers/forwarding/units/{unitId}/addAdd a forwarded unit as one of your trackers (a new vehicle named after it, unless you give one)
DELETE /trackers/forwarding/units/{unitId}Forget a forwarded unit (it comes back if your platform sends it again)
GET /tracker-transfersYour requests to move trackers here, and trackers that moved away
POST /tracker-transfersAsk for a tracker another organisation holds (owners and admins)
POST /tracker-transfers/{transferId}/startYou're making the device log in afresh now (its restart SMS); a fresh login within 3 minutes moves it
DELETE /tracker-transfers/{transferId}Withdraw a request
GET /trackers/{trackerId}Get a tracker, its setup steps and what's wrong
PATCH /trackers/{trackerId}Edit a tracker (its vehicle, model, SIM)
DELETE /trackers/{trackerId}Remove a tracker (it can then be added by anyone)
GET /trackers/{trackerId}/logThe device log (connections, raw frames, decoded fields, answers sent, errors), oldest first
GET /trackers/{trackerId}/streamThe device log and state as they change (server-sent events)
POST /trackers/{trackerId}/virtualDrive a virtual tracker for this one (its own protocol, through N-Ways' gateway)
DELETE /trackers/{trackerId}/virtualStop its virtual tracker
GET /trackers/{trackerId}/commandsCommands sent to a tracker (the last 30), and which it takes
POST /trackers/{trackerId}/commandsSend a command (owners and admins); its answer comes in the device log
GET /liveEvery vehicle now, with its rider, state, current delivery and zones
GET /alertsWhat needs a look now, most urgent first
GET /routesWhere each vehicle on a delivery is going
GET /zonesThe project's zones (circles), each with how many vehicles are in it now
POST /zonesAdd a zone (at most 200 a project)
GET /zones/{id}Get a zone
PATCH /zones/{id}Rename, move or resize a zone (lat and lng together)
DELETE /zones/{id}Delete a zone
GET /vehicles/{id}/linksThe vehicle's rider links (never their addresses), newest first
POST /vehicles/{id}/linksA link a rider opens on their phone to share its position for this vehicle (no app needed)
DELETE /links/{linkId}Stop a rider link at once (its page then says it has ended)
GET /drivers/{id}/invitesThe rider's invitations to N-Ways Business (never their codes), newest first
POST /drivers/{id}/invitesInvite the rider to N-Ways Business
DELETE /invites/{inviteId}Withdraw an invitation (its code stops working)
DELETE /drivers/{id}/appTake the rider's N-Ways account off this driver

Files

The same reference as machine-readable files, to generate your own client or import into an API tool. The Postman collection has every request with {{apiKey}} and {{baseUrl}} variables.