N-Ways
Developer docsConnect your ERP

Connect your ERP

Run deliveries in the N-Ways console and keep your ERP in step: every event reaches your system as it happens, signed and retried, and can be pulled again at any time.

On this page
Note:

For companies that run their deliveries in the N-Ways console and want the same data in their own ERP. Choose Run deliveries here, synced to our ERP when you sign up, and the console puts webhooks, the delivery log and this guide first. Send this page to whoever connects your ERP: your IT team or your integration partner.

How the data flows

  1. Your dispatchers work in the console (or the N-Ways Business app): deliveries, riders, N-Ways drivers, alerts.
  2. Every change is an event. N-Ways sends it to your HTTPS endpoint within seconds, signed with your endpoint’s secret.
  3. Your ERP updates its record, found by the reference you gave the delivery or by N-Ways’ id.
  4. Nothing is lost. Failed calls are retried for about 22 hours; you can retry any of them from the delivery log, and pull every event again by date with the export API.

1. Add your endpoint

In the console: Settings → Webhooks → Add webhook. Give an HTTPS address reachable from the internet and choose every event or only those your ERP needs. The signing secret (whsec_…) is shown once: store it with your other secrets. Up to 10 endpoints per organisation, each with its own custom headers (an API key for your gateway, for example).

Or with a server key:

cURL
curl -X POST "https://nways.ng/maps/v1/fleet/webhooks" \
  -H "X-NWays-Api-Key: YOUR_SERVER_KEY" -H "Content-Type: application/json" \
  -d '{"url": "https://erp.example.com/nways/events"}'

2. Send a test event

Send test on the webhook posts a ping event. The console’s Logs → Webhook deliveries shows each attempt, your server’s answer and when the next try is due. Answer with any 2xx within 10 seconds; do slow work (posting to the ERP) after answering.

3. Check the signature

Every call carries X-NWays-Signature: t=<unix seconds>,v1=<hex>, where v1 is HMAC-SHA256 of "<t>.<raw body>" with your endpoint’s secret. Check it on the raw body, before parsing, and refuse a timestamp more than 5 minutes off.

C# (Dynamics 365, Azure)
using System.Security.Cryptography;
using System.Text;

static bool Verified(string raw, string header, string secret)
{
    var parts = header.Split(',').Select(p => p.Split('=', 2)).ToDictionary(p => p[0], p => p.Length > 1 ? p[1] : "");
    if (!long.TryParse(parts.GetValueOrDefault("t"), out var t)) return false;
    if (Math.Abs(DateTimeOffset.UtcNow.ToUnixTimeSeconds() - t) > 300) return false;
    using var mac = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
    var want = Convert.ToHexString(mac.ComputeHash(Encoding.UTF8.GetBytes($"{t}.{raw}"))).ToLowerInvariant();
    return CryptographicOperations.FixedTimeEquals(Encoding.UTF8.GetBytes(want), Encoding.UTF8.GetBytes(parts.GetValueOrDefault("v1", "")));
}
Groovy (SAP Integration Suite)
import com.sap.gateway.ip.core.customdev.util.Message
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec

def Message processData(Message message) {
    def raw = message.getBody(String)
    def parts = message.getHeaders()['X-NWays-Signature'].split(',').collectEntries { it.split('=', 2) as List }
    long t = parts.t as long
    if (Math.abs(System.currentTimeMillis() / 1000 - t) > 300) throw new Exception('old signature')
    def mac = Mac.getInstance('HmacSHA256')
    mac.init(new SecretKeySpec(message.getProperty('NWAYS_SECRET').bytes, 'HmacSHA256'))
    def want = mac.doFinal("${t}.${raw}".bytes).encodeHex().toString()
    if (!java.security.MessageDigest.isEqual(want.bytes, (parts.v1 ?: '').bytes)) throw new Exception('bad signature')
    return message
}
Python (Odoo)
import hashlib, hmac, time

def verified(raw: bytes, header: str, secret: str) -> bool:
    parts = dict(p.split("=", 1) for p in header.split(",") if "=" in p)
    if abs(time.time() - int(parts.get("t", 0))) > 300:
        return False
    want = hmac.new(secret.encode(), parts["t"].encode() + b"." + raw, hashlib.sha256).hexdigest()
    return hmac.compare_digest(want, parts.get("v1", ""))

4. Map the fields

Where each field of a delivery event (data.task) usually goes. Keep N-Ways’ id as an external reference on your record, so every later event finds it; put your own document number in the delivery’s reference when you create it, and it comes back in every event.

MeaningN-WaysSAP S/4HANAOdooDynamics 365
Your own referencereferenceOutbound delivery (LIKP-VBELN)stock.picking originShipment ID
Delivery numberid, refExternal ID (LIKP-LIFEX)A custom field, e.g. x_nways_idExternal reference
StatusstatusPOD status (LIKP-PDSTK)stateShipment status
CustomerrecipientName, recipientPhoneShip-to party (KUNNR)partner_idDelivery name and contact
Drop-offdropoff.address, dropoff.lat/lngShip-to address (ADRC)Partner addressDelivery address
RidervehicleIdShipment (VTTK), means of transportcarrier_id or a driver fieldCarrier, vehicle
TimespickedUpAt, endedAtActual goods issue (WADAT_IST)date_doneActual ship and delivery dates
Proof of deliveryproofs[]POD (VLPOD) attachmentAttachment on the transferDocument attachment

Statuses: created → assigned → picked_up → arriving → completed, or failed (with failureReason) or cancelled. Events can arrive twice or out of order: keep the event ids you’ve handled and trust the task’s status over the order of arrival. There are no money fields, by design.

5. Reconcile

If your ERP was down, nothing is lost. Retry any delivery from Logs → Webhook deliveries, or pull the events again by date (JSON or CSV, the same payloads the webhooks send):

cURL
curl "https://nways.ng/maps/v1/fleet/events/export?format=json&from=2026-10-01&to=2026-10-16" \
  -H "X-NWays-Api-Key: YOUR_SERVER_KEY"

Deliveries, vehicles and drivers can also be read at any time from the fleet API (GET /maps/v1/fleet/tasks, /vehicles, /drivers), for a nightly check that both sides agree. Use a key restricted to your servers’ IP addresses and to the fleet API.

Which events

Every event in the webhooks list: deliveries (task.*, delivery.rated), drivers and vehicles (connection.*, driver.availability_changed, vehicle.offline), requests to N-Ways drivers (dispatch.*), zones and alerts (zone.*, asset.*). Payloads carry version 2026-10-16; fields are only ever added within a version.