N-Ways
Developer docsWebhooks

Webhooks

Endpoints on your server that N-Ways calls when a delivery or a vehicle changes.

On this page
Note:

Send your key as key=YOUR_API_KEY on the address, or in the X-NWays-Api-Key header. API keys

Base address: https://nways.ng/maps/v1/fleet

List webhook endpoints

GET https://nways.ng/maps/v1/fleet/webhooks

cURL
curl "https://nways.ng/maps/v1/fleet/webhooks" \
  -H "X-NWays-Api-Key: YOUR_API_KEY"
JavaScript
const res = await fetch('https://nways.ng/maps/v1/fleet/webhooks', {
  headers: { 'X-NWays-Api-Key': process.env.NWAYS_API_KEY },
})
const data = await res.json()
Python
import os
import requests

res = requests.get(
    "https://nways.ng/maps/v1/fleet/webhooks",
    headers={"X-NWays-Api-Key": os.environ["NWAYS_API_KEY"]},
    timeout=10,
)
data = res.json()

Response

Endpoints and the events there are

FieldTypeDescription
webhooksWebhook[]Required.
webhooks[].idstringRequired.
webhooks[].urlstring (uri)Required.
webhooks[].eventsstring[]Required.
webhooks[].activebooleanRequired.
webhooks[].secretstringOnly when made or rotated.
webhooks[].headersstring[]Names of the custom headers sent with each delivery (values are never shown).
webhooks[].createdAtstring (date-time)Required.
webhooks[].updatedAtstring (date-time)Required.
eventsstring[]Required.
200 OK
{
  "webhooks": [
    {
      "id": "wh_Ub3yH9sLk2",
      "url": "string",
      "events": [
        "task.created"
      ],
      "active": true,
      "secret": "whsec_…",
      "headers": [
        "string"
      ],
      "createdAt": "2026-10-15T09:00:00+01:00",
      "updatedAt": "2026-10-15T09:00:00+01:00"
    }
  ],
  "events": [
    "task.created"
  ]
}

Errors

  • default Error

Add an endpoint (up to 10); the answer has its signing secret, shown once

POST https://nways.ng/maps/v1/fleet/webhooks

Request body

FieldTypeDescription
urlstring (uri)Required. https, a public address; redirects are not followed.
eventsstring[]Empty = every event.
headersobjectSent with every delivery (an auth token your CRM asks for). Not X-NWays-*, Host, Content-Type, User-Agent.
cURL
curl -X POST "https://nways.ng/maps/v1/fleet/webhooks" \
  -H "X-NWays-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"string","events":["task.created"],"headers":{}}'
JavaScript
const res = await fetch('https://nways.ng/maps/v1/fleet/webhooks', {
  method: 'POST',
  headers: { 'X-NWays-Api-Key': process.env.NWAYS_API_KEY, 'Content-Type': 'application/json' },
  body: JSON.stringify({
    "url": "string",
    "events": [
      "task.created"
    ],
    "headers": {}
  }),
})
const data = await res.json()
Python
import os
import requests

res = requests.post(
    "https://nways.ng/maps/v1/fleet/webhooks",
    headers={"X-NWays-Api-Key": os.environ["NWAYS_API_KEY"]},
    json={"url": "string", "events": ["task.created"], "headers": {}},
    timeout=10,
)
data = res.json()

Response

Created

FieldTypeDescription
idstringRequired.
urlstring (uri)Required.
eventsstring[]Required.
activebooleanRequired.
secretstringOnly when made or rotated.
headersstring[]Names of the custom headers sent with each delivery (values are never shown).
createdAtstring (date-time)Required.
updatedAtstring (date-time)Required.
201 OK
{
  "id": "wh_Ub3yH9sLk2",
  "url": "string",
  "events": [
    "task.created"
  ],
  "active": true,
  "secret": "whsec_…",
  "headers": [
    "string"
  ],
  "createdAt": "2026-10-15T09:00:00+01:00",
  "updatedAt": "2026-10-15T09:00:00+01:00"
}

Errors

  • default Error

Change an endpoint's url, events or active

PATCH https://nways.ng/maps/v1/fleet/webhooks/{id}

Parameters

NameInTypeDescription
idpathstringRequired.

Request body

FieldTypeDescription
urlstring (uri)
eventsstring[]
activebooleanOff: pending deliveries fail and no new ones are queued.
cURL
curl -X PATCH "https://nways.ng/maps/v1/fleet/webhooks/string" \
  -H "X-NWays-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"string","events":["task.created"],"active":true}'
JavaScript
const res = await fetch('https://nways.ng/maps/v1/fleet/webhooks/string', {
  method: 'PATCH',
  headers: { 'X-NWays-Api-Key': process.env.NWAYS_API_KEY, 'Content-Type': 'application/json' },
  body: JSON.stringify({
    "url": "string",
    "events": [
      "task.created"
    ],
    "active": true
  }),
})
const data = await res.json()
Python
import os
import requests

res = requests.patch(
    "https://nways.ng/maps/v1/fleet/webhooks/string",
    headers={"X-NWays-Api-Key": os.environ["NWAYS_API_KEY"]},
    json={"url": "string", "events": ["task.created"], "active": True},
    timeout=10,
)
data = res.json()

Response

Endpoint

FieldTypeDescription
idstringRequired.
urlstring (uri)Required.
eventsstring[]Required.
activebooleanRequired.
secretstringOnly when made or rotated.
headersstring[]Names of the custom headers sent with each delivery (values are never shown).
createdAtstring (date-time)Required.
updatedAtstring (date-time)Required.
200 OK
{
  "id": "wh_Ub3yH9sLk2",
  "url": "string",
  "events": [
    "task.created"
  ],
  "active": true,
  "secret": "whsec_…",
  "headers": [
    "string"
  ],
  "createdAt": "2026-10-15T09:00:00+01:00",
  "updatedAt": "2026-10-15T09:00:00+01:00"
}

Errors

  • default Error

Delete an endpoint and its delivery log

DELETE https://nways.ng/maps/v1/fleet/webhooks/{id}

Parameters

NameInTypeDescription
idpathstringRequired.
cURL
curl -X DELETE "https://nways.ng/maps/v1/fleet/webhooks/string" \
  -H "X-NWays-Api-Key: YOUR_API_KEY"
JavaScript
const res = await fetch('https://nways.ng/maps/v1/fleet/webhooks/string', {
  method: 'DELETE',
  headers: { 'X-NWays-Api-Key': process.env.NWAYS_API_KEY },
})
const data = await res.json()
Python
import os
import requests

res = requests.delete(
    "https://nways.ng/maps/v1/fleet/webhooks/string",
    headers={"X-NWays-Api-Key": os.environ["NWAYS_API_KEY"]},
    timeout=10,
)
data = res.json()

Response

Deleted

No content.

Errors

  • default Error

A new signing secret (shown once); the old one stops at once

POST https://nways.ng/maps/v1/fleet/webhooks/{id}/rotate-secret

Parameters

NameInTypeDescription
idpathstringRequired.
cURL
curl -X POST "https://nways.ng/maps/v1/fleet/webhooks/string/rotate-secret" \
  -H "X-NWays-Api-Key: YOUR_API_KEY"
JavaScript
const res = await fetch('https://nways.ng/maps/v1/fleet/webhooks/string/rotate-secret', {
  method: 'POST',
  headers: { 'X-NWays-Api-Key': process.env.NWAYS_API_KEY },
})
const data = await res.json()
Python
import os
import requests

res = requests.post(
    "https://nways.ng/maps/v1/fleet/webhooks/string/rotate-secret",
    headers={"X-NWays-Api-Key": os.environ["NWAYS_API_KEY"]},
    timeout=10,
)
data = res.json()

Response

Endpoint with secret

FieldTypeDescription
idstringRequired.
urlstring (uri)Required.
eventsstring[]Required.
activebooleanRequired.
secretstringOnly when made or rotated.
headersstring[]Names of the custom headers sent with each delivery (values are never shown).
createdAtstring (date-time)Required.
updatedAtstring (date-time)Required.
200 OK
{
  "id": "wh_Ub3yH9sLk2",
  "url": "string",
  "events": [
    "task.created"
  ],
  "active": true,
  "secret": "whsec_…",
  "headers": [
    "string"
  ],
  "createdAt": "2026-10-15T09:00:00+01:00",
  "updatedAt": "2026-10-15T09:00:00+01:00"
}

Errors

  • default Error

Queue a ping event to the endpoint

POST https://nways.ng/maps/v1/fleet/webhooks/{id}/test

Parameters

NameInTypeDescription
idpathstringRequired.
cURL
curl -X POST "https://nways.ng/maps/v1/fleet/webhooks/string/test" \
  -H "X-NWays-Api-Key: YOUR_API_KEY"
JavaScript
const res = await fetch('https://nways.ng/maps/v1/fleet/webhooks/string/test', {
  method: 'POST',
  headers: { 'X-NWays-Api-Key': process.env.NWAYS_API_KEY },
})
const data = await res.json()
Python
import os
import requests

res = requests.post(
    "https://nways.ng/maps/v1/fleet/webhooks/string/test",
    headers={"X-NWays-Api-Key": os.environ["NWAYS_API_KEY"]},
    timeout=10,
)
data = res.json()

Response

Queued

FieldTypeDescription
deliveryIdstringRequired.
202 OK
{
  "deliveryId": "dlv_Qa71ZcR0mX"
}

Errors

  • default Error

The endpoint's delivery log, newest first (kept 30 days)

GET https://nways.ng/maps/v1/fleet/webhooks/{id}/deliveries

Parameters

NameInTypeDescription
idpathstringRequired.
statusquerystringOne of: pending, delivered, failed.
limitqueryintegerDefault: 50. ≥ 1, ≤ 200.
cURL
curl "https://nways.ng/maps/v1/fleet/webhooks/string/deliveries" \
  -H "X-NWays-Api-Key: YOUR_API_KEY"
JavaScript
const res = await fetch('https://nways.ng/maps/v1/fleet/webhooks/string/deliveries', {
  headers: { 'X-NWays-Api-Key': process.env.NWAYS_API_KEY },
})
const data = await res.json()
Python
import os
import requests

res = requests.get(
    "https://nways.ng/maps/v1/fleet/webhooks/string/deliveries",
    headers={"X-NWays-Api-Key": os.environ["NWAYS_API_KEY"]},
    timeout=10,
)
data = res.json()

Response

Deliveries

FieldTypeDescription
deliveriesDelivery[]Required.
deliveries[].idstringRequired.
deliveries[].eventstringRequired.
deliveries[].statusstringRequired. One of: pending, delivered, failed.
deliveries[].attemptsintegerRequired.
deliveries[].lastStatusintegerRequired. Your endpoint's last HTTP status (0 = no answer).
deliveries[].lastErrorstringRequired.
deliveries[].nextAttemptAtstring (date-time) | nullRequired.
deliveries[].createdAtstring (date-time)Required.
deliveries[].deliveredAtstring (date-time) | nullRequired.
deliveries[].payloadEventRequired.
deliveries[].payload.idstringRequired.
deliveries[].payload.typestringRequired. An EventType, or ping from the test call.
deliveries[].payload.versionstringRequired. The payload shape's version: fields are only added within a version.
deliveries[].payload.createdAtstring (date-time)Required.
deliveries[].payload.dataobjectRequired.
deliveries[].payload.data.taskTask
deliveries[].payload.data.vehicleanyA Vehicle; {id, name} in zone events.
deliveries[].payload.data.zoneobject
deliveries[].payload.data.atstring (date-time)
deliveries[].payload.data.positionobject
deliveries[].payload.data.alertAlertEvent
deliveries[].payload.data.changedstring[]task.updated: the fields the edit changed.
200 OK
{
  "deliveries": [
    {
      "id": "dlv_Qa71ZcR0mX",
      "event": "string",
      "status": "pending",
      "attempts": 0,
      "lastStatus": 0,
      "lastError": "string",
      "nextAttemptAt": "2026-10-15T09:00:00+01:00",
      "createdAt": "2026-10-15T09:00:00+01:00",
      "deliveredAt": "2026-10-15T09:00:00+01:00",
      "payload": {
        "id": "evt_9bQ2xLm0aZc4RwYeT7Kp",
        "type": "string",
        "version": "2026-10-16",
        "createdAt": "2026-10-15T09:00:00+01:00",
        "data": {
          "task": {},
          "vehicle": "string",
          "zone": {},
          "at": "2026-10-15T09:00:00+01:00",
          "position": {},
          "alert": {},
          "changed": []
        }
      }
    }
  ]
}

Errors

  • default Error

Send a delivery again now (failed, or delivered and wanted again); its attempts are kept

POST https://nways.ng/maps/v1/fleet/webhooks/{id}/deliveries/{deliveryId}/retry

Parameters

NameInTypeDescription
idpathstringRequired.
deliveryIdpathstringRequired.
cURL
curl -X POST "https://nways.ng/maps/v1/fleet/webhooks/string/deliveries/string/retry" \
  -H "X-NWays-Api-Key: YOUR_API_KEY"
JavaScript
const res = await fetch('https://nways.ng/maps/v1/fleet/webhooks/string/deliveries/string/retry', {
  method: 'POST',
  headers: { 'X-NWays-Api-Key': process.env.NWAYS_API_KEY },
})
const data = await res.json()
Python
import os
import requests

res = requests.post(
    "https://nways.ng/maps/v1/fleet/webhooks/string/deliveries/string/retry",
    headers={"X-NWays-Api-Key": os.environ["NWAYS_API_KEY"]},
    timeout=10,
)
data = res.json()

Response

Queued

FieldTypeDescription
idstringRequired.
statusanyRequired.
202 OK
{
  "id": "string",
  "status": "string"
}

Errors

  • default Error

An event, POSTed to your endpoint

POST your endpoint’s URL

Headers: X-NWays-Event (the type), X-NWays-Delivery (dlv_…, the same on retries), X-NWays-Signature: t=<unix seconds>,v1=<hex> where v1 = HMAC-SHA256(secret, "<t>.<raw body>"). Check it with your endpoint's secret and refuse timestamps more than 5 minutes off. Answer any 2xx within 10 s; anything else is retried after 30 s, 2 min, 10 min, 30 min, 1 h, 3 h, 6 h and 12 h, then marked failed. Events may arrive out of order or more than once: use the event id and the task's status. Events: task.created, task.assigned, task.arriving, task.completed, task.failed (data.task, a Task), vehicle.offline (data.vehicle, a Vehicle; once per silence longer than offlineAfterS), and zone.entered / zone.exited (data.zone {id, name}, data.vehicle {id, name}, data.at, data.position {lat, lng}).

Parameters

NameInTypeDescription
X-NWays-SignatureheaderstringRequired.
X-NWays-EventheaderstringRequired. One of: task.created, task.assigned, task.arriving, task.completed, task.failed, vehicle.offline, zone.entered, zone.exited, asset.geofence_exit, asset.geofence_enter, asset.silent, asset.idle, asset.alert_resolved, connection.approved, connection.declined, connection.revoked, connection.ended, driver.availability_changed, dispatch.offered, dispatch.accepted, dispatch.declined, dispatch.expired, dispatch.withdrawn, task.started, delivery.rated, fleet.crash.detected, fleet.crash.resolved, task.updated.
X-NWays-DeliveryheaderstringRequired.

Request body

FieldTypeDescription
idstringRequired.
typestringRequired. An EventType, or ping from the test call.
versionstringRequired. The payload shape's version: fields are only added within a version.
createdAtstring (date-time)Required.
dataobjectRequired.
data.taskTask
data.task.idstringRequired.
data.task.refstringRequired. The delivery's short number in the project, given by N-Ways ("1042", shown #1042): for people, in alerts and lists.
data.task.statusstringRequired. One of: created, assigned, picked_up, arriving, completed, failed, cancelled.
data.task.vehicleIdstring | nullRequired.
data.task.pickupStop | nullRequired.
data.task.dropoffStopRequired.
data.task.dropoff.latnumberRequired.
data.task.dropoff.lngnumberRequired.
data.task.dropoff.namestringUp to 120 characters.
data.task.dropoff.addressstringUp to 300 characters.
data.task.recipientNamestringRequired.
data.task.recipientPhonestring
data.task.notesstringRequired.
data.task.langstringRequired. One of: en, fr, pt.
data.task.arrivingSintegerRequired.
data.task.etaETA | nullRequired. Null until a fresh position is in, and once the task ends.
data.task.dueAtstring (date-time) | nullRequired.
data.task.latebooleanRequired. Open and past dueAt, or expected after it.
data.task.trackingUrlstring (uri) | nullRequired. Null once expired.
data.task.trackingExpiresAtstring (date-time) | nullRequired.
data.task.failureReasonstring
data.task.createdAtstring (date-time)Required.
data.task.assignedAtstring (date-time) | nullRequired.
data.task.pickedUpAtstring (date-time) | nullRequired.
data.task.arrivingAtstring (date-time) | nullRequired.
data.task.endedAtstring (date-time) | nullRequired.
data.task.areaArea | nullThe delivery's own area: leaving it raises asset.geofence_exit.
data.task.proofsProof[]Only in GET /tasks/{id}: the rider's photos and signatures (N-Ways Business).
data.task.proofs[].idstringRequired.
data.task.proofs[].kindstringRequired. One of: photo, signature.
data.task.proofs[].contentTypestringRequired. One of: image/jpeg, image/png, image/webp.
data.task.proofs[].bytesintegerRequired.
data.task.proofs[].latnumber | nullRequired.
data.task.proofs[].lngnumber | nullRequired.
data.task.proofs[].bystringRequired. The rider's name.
data.task.proofs[].createdAtstring (date-time)Required.
data.task.endNotestringOnly in GET /tasks/{id}: what the rider wrote when finishing.
data.task.recipientEmailstring
data.task.proofRequiredstring[]The delivery's own, else your settings'.
data.task.proofRequiredOverridestring[] | null
data.task.packageSizestring | nullOne of: small, medium, large, null.
data.task.itemDescriptionstring
data.task.referencestring
data.task.scheduledAtstring (date-time) | null
data.task.connectionIdstring | nullThe N-Ways driver's connection that accepted it.
data.task.acceptedAtstring (date-time) | null
data.task.startedAtstring (date-time) | null
data.task.editablestringWhat PATCH /tasks/{id} may change now: everything, only notes and dueAt (picked up or started), or nothing (ended). One of: all, limited, none.
data.task.editedAtstring (date-time) | nullThe last edit.
data.task.editedFieldsstring[]What the last edit changed.
data.task.pinDeliveryPinDeliveryHow the delivery PIN is reaching the customer, when the delivery asks for one and PINs go on WhatsApp. Never the PIN itself.
data.task.pinDelivery.channelstringRequired. One of: whatsapp, email, none.
data.task.pinDelivery.statestringRequired. One of: sending, sent, delivered, read, failed.
data.task.pinDelivery.whatsappErrorCodeinteger | nullRequired. WhatsApp's failure (131026: not on WhatsApp), kept after the PIN went by email.
data.task.pinDelivery.needsEmailbooleanRequired. The customer hasn't got the PIN: send it with POST /tasks/{id}/pin-email.
data.task.pinDelivery.updatedAtstring (date-time)Required.
data.vehicleanyA Vehicle; {id, name} in zone events.
data.zoneobject
data.zone.idstring
data.zone.namestring
data.atstring (date-time)
data.positionobject
data.position.latnumber
data.position.lngnumber
data.alertAlertEvent
data.alert.idstringRequired.
data.alert.ruleIdstringRequired. A rule id, or default:dispatch for a delivery area with no rule of its own, default:crash for a crash with no crash rule.
data.alert.ruleNamestring
data.alert.kindstringRequired. One of: geofence_exit, geofence_enter, silent, idle, crash, tracker_alarm.
data.alert.vehicleIdstringRequired.
data.alert.vehicleNamestring
data.alert.subjectstringtask:<id> or zone:<id> for geofences; crash:<crs_id> for a crash.
data.alert.statestringRequired. One of: open, acknowledged, resolved.
data.alert.detailobjectRequired. position, at, taskId, area, zoneId, zoneName, lastSeenAt, since…; a crash: crashId, at, position, accuracyM, speedBeforeKmh, road, driverName, driverPhone (when you have it), taskId, taskRef, taskLabel, state (help | resolved), resolvedHow (ok | help_here).
data.alert.startedAtstring (date-time)Required.
data.alert.notifiedAtstring (date-time) | null
data.alert.notificationsinteger
data.alert.acknowledgedBystring
data.alert.acknowledgedAtstring (date-time) | null
data.alert.resolvedBystring
data.alert.resolvedAtstring (date-time) | null
data.alert.autoResolvedbooleanCleared by itself (back inside, heard from again, moving).
data.changedstring[]task.updated: the fields the edit changed.
Example
{
  "id": "evt_9bQ2xLm0aZc4RwYeT7Kp",
  "type": "string",
  "version": "2026-10-16",
  "createdAt": "2026-10-15T09:00:00+01:00",
  "data": {
    "task": {
      "id": "string",
      "ref": "string",
      "status": "created",
      "vehicleId": "string",
      "pickup": {
        "lat": 0,
        "lng": 0,
        "name": "string",
        "address": "string"
      },
      "dropoff": {
        "lat": 0,
        "lng": 0,
        "name": "string",
        "address": "string"
      },
      "recipientName": "string",
      "recipientPhone": "string",
      "notes": "string",
      "lang": "en",
      "arrivingS": 0,
      "eta": {
        "at": "2026-10-15T09:00:00+01:00",
        "durationS": 0,
        "distanceM": 0,
        "trafficDelayS": 0,
        "pickupAt": "2026-10-15T09:00:00+01:00",
        "updatedAt": "2026-10-15T09:00:00+01:00"
      },
      "dueAt": "2026-10-15T09:00:00+01:00",
      "late": true,
      "trackingUrl": "string",
      "trackingExpiresAt": "2026-10-15T09:00:00+01:00",
      "failureReason": "string",
      "createdAt": "2026-10-15T09:00:00+01:00",
      "assignedAt": "2026-10-15T09:00:00+01:00",
      "pickedUpAt": "2026-10-15T09:00:00+01:00",
      "arrivingAt": "2026-10-15T09:00:00+01:00",
      "endedAt": "2026-10-15T09:00:00+01:00",
      "area": {
        "type": "circle",
        "lat": 6.48,
        "lng": 3.43,
        "radiusM": 2500
      },
      "proofs": [
        {
          "id": "string",
          "kind": "photo",
          "contentType": "image/jpeg",
          "bytes": 0,
          "lat": 0,
          "lng": 0,
          "by": "string",
          "createdAt": "2026-10-15T09:00:00+01:00"
        }
      ],
      "endNote": "string",
      "recipientEmail": "string",
      "proofRequired": [
        "photo"
      ],
      "proofRequiredOverride": [
        "string"
      ],
      "packageSize": "small",
      "itemDescription": "string",
      "reference": "string",
      "scheduledAt": "2026-10-15T09:00:00+01:00",
      "connectionId": "string",
      "acceptedAt": "2026-10-15T09:00:00+01:00",
      "startedAt": "2026-10-15T09:00:00+01:00",
      "editable": "all",
      "editedAt": "2026-10-15T09:00:00+01:00",
      "editedFields": [
        "string"
      ],
      "pinDelivery": {
        "channel": "whatsapp",
        "state": "sending",
        "whatsappErrorCode": 0,
        "needsEmail": true,
        "updatedAt": "2026-10-15T09:00:00+01:00"
      }
    },
    "vehicle": "string",
    "zone": {
      "id": "string",
      "name": "string"
    },
    "at": "2026-10-15T09:00:00+01:00",
    "position": {
      "lat": 0,
      "lng": 0
    },
    "alert": {
      "id": "alr_8KwP2nVd5T",
      "ruleId": "string",
      "ruleName": "string",
      "kind": "geofence_exit",
      "vehicleId": "string",
      "vehicleName": "string",
      "subject": "string",
      "state": "open",
      "detail": {},
      "startedAt": "2026-10-15T09:00:00+01:00",
      "notifiedAt": "2026-10-15T09:00:00+01:00",
      "notifications": 0,
      "acknowledgedBy": "string",
      "acknowledgedAt": "2026-10-15T09:00:00+01:00",
      "resolvedBy": "string",
      "resolvedAt": "2026-10-15T09:00:00+01:00",
      "autoResolved": true
    },
    "changed": [
      "string"
    ]
  }
}

Response

Received