Skip to main content

Routes

16 API calls in this section.

Route feature — reusable named delivery lanes from origin to destination.

A Route is a fixed lane. Its optional driver_id is reserved only for automatic scheduled-session creation and does not own the route. A Route Session is one operational run and owns exactly one immutable driver. Different sessions of the same route may use different drivers.

Session lifecycle

open → started → picked_up → in_transit → completed (auto) | cancelled

  • Route driver_id, schedule_time, schedule_days, and start_date must be provided together or all cleared.

  • Changing the route driver affects future scheduled sessions only. Existing sessions never change driver.

  • Manual assign-deliveries requests use their explicit driver_id or session_id and never infer the route driver.

  • driver_id assignment mode reuses the newest open or started session for the same route and driver; it creates a new session only when no accepting session exists.

  • session_id mode adds deliveries only to that exact open/started session.

  • A session driver cannot be changed by any role. Replacement is cancel the pre-pickup session and create a new session with the correct driver.

  • Drivers see only sessions assigned to their own driver record; route list/detail are dispatcher-only.

  • A driver cannot start an empty session; at least one active non-terminal delivery must be assigned.

  • Any session action that moves or resets deliveries is blocked when one of the session deliveries has an open delivery exception; resolve the exception first.

  • Route success, validation, conflict, and failure messages are localized for English, Arabic, Kurdish, and Persian through the existing language query parameter or Accept-Language header.

List Routes​

Delivery & Last-Mile / Routes
GET/delivery/routes

Dispatcher-only list of reusable route lanes. Excludes inactive routes by default. Each row includes optional driver_id and compact driver scheduling configuration plus read-only next_session_at. The route driver is not operational ownership. Driver users obtain route context through their own /delivery/route-sessions rows.

Query parameters

ParameterTypeDescription
pagestring

Page number

Example: 1

limitstring

Items per page (max 100)

Example: 20

searchstring

Partial match on route_name

include_inactivestring

Include soft-deleted routes

Example: false

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Staging
Request
curl
1curl --globoff --request GET "https://staging-api.ondi.io/functions/v1/delivery/routes?page=1&limit=20&search=&include_inactive=false" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}"

Response examples

200OK
Response body
json
1{2  "success": true,3  "message": "Routes retrieved successfully",4  "data": {5    "routes": [6      {7        "id": "route-uuid",8        "route_name": "Warehouse A → Zone 5",9        "driver_id": "driver-uuid",10        "driver": {11          "id": "driver-uuid",12          "full_name": "Scheduled Driver",13          "avatar_url": null14        },15        "origin_type": "warehouse",16        "origin_warehouse_id": "wh-uuid",17        "destination_type": "location",18        "destination_location": {19          "lat": 33.5,20          "lng": 44.2,21          "address": "Zone 5 Hub"22        },23        "is_active": true,24        "schedule_time": "08:00:00",25        "schedule_days": [26          1,27          2,28          3,29          4,30          531        ],32        "start_date": "2026-04-15",33        "next_session_at": "2026-04-15T05:00:00Z",34        "notes": null,35        "created_at": "2026-04-14T10:00:00Z",36        "active_orders_count": 837      }38    ],39    "total": 1,40    "page": 1,41    "limit": 2042  }43}

Create Route​

Delivery & Last-Mile / Routes
POST/delivery/routes

Create a reusable route lane.

  • origin_type / destination_type: warehouse | location

  • If type is warehouse: provide *_warehouse_id

  • If type is location: provide *_location as {lat, lng, address}

  • driver_id is optional and is used only to create future sessions automatically from this route's schedule. It does not own the route and is ignored by manual assign-deliveries requests.

  • driver_id, schedule_time, schedule_days, and start_date must be provided together or all omitted/null.

  • The driver must be active and belong to the authenticated tenant.

  • Scheduled creation uses the tenant IANA timezone and returns read-only next_session_at.

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Content-Typestring

Example: application/json

Body parameters

ParameterTypeDescription
route_namestring—
origin_typestring—
origin_warehouse_idstring—
destination_typestring—
destination_locationobject—
destination_location.latnumber—
destination_location.lngnumber—
destination_location.addressstring—
driver_idstring—
schedule_timestring—
schedule_daysarray<number>—
start_datestring—
Show 1 more body fields

Additional body parameters

ParameterTypeDescription
notesstring—
Staging
Request
curl
1curl --globoff --request POST "https://staging-api.ondi.io/functions/v1/delivery/routes" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}" \3  --header "Content-Type: application/json" \4  --data-raw '{5  "route_name": "Warehouse A → Zone 5",6  "origin_type": "warehouse",7  "origin_warehouse_id": "wh-uuid",8  "destination_type": "location",9  "destination_location": {10    "lat": 33.5,11    "lng": 44.2,12    "address": "Zone 5 Hub"13  },14  "driver_id": "driver-uuid",15  "schedule_time": "08:00:00",16  "schedule_days": [17    1,18    2,19    3,20    4,21    522  ],23  "start_date": "2026-04-15",24  "notes": "Morning route"25}'

Response examples

201Created
Response body
json
1{2  "success": true,3  "message": "Route created successfully",4  "data": {5    "id": "route-uuid",6    "route_name": "Warehouse A → Zone 5",7    "driver_id": "driver-uuid",8    "driver": {9      "id": "driver-uuid",10      "full_name": "Scheduled Driver",11      "avatar_url": null12    },13    "is_active": true,14    "schedule_time": "08:00:00",15    "schedule_days": [16      1,17      2,18      3,19      4,20      521    ],22    "start_date": "2026-04-15",23    "next_session_at": "2026-04-15T05:00:00Z",24    "created_at": "2026-04-14T10:00:00Z"25  }26}

Get Route​

Delivery & Last-Mile / Routes
GET/delivery/routes/:routeId

Dispatcher-only route detail. Returns the fixed lane, optional reserved scheduling driver_id and compact driver, read-only next_session_at, warehouse/location information, and aggregate session_counts. It does not return an ambiguous singular current session.

Path parameters

ParameterTypeDescription
routeIdRequiredstring

Example: route-uuid

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Staging
Request
curl
1curl --globoff --request GET "https://staging-api.ondi.io/functions/v1/delivery/routes/${ROUTEID}" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}"

Response examples

200OK
Response body
json
1{2  "success": true,3  "message": "Route retrieved successfully",4  "data": {5    "id": "route-uuid",6    "route_name": "Warehouse A → Zone 5",7    "driver_id": "driver-uuid",8    "driver": {9      "id": "driver-uuid",10      "full_name": "Scheduled Driver",11      "avatar_url": null12    },13    "origin_type": "warehouse",14    "origin_warehouse": {15      "id": "wh-uuid",16      "name": "Warehouse A",17      "code": "WH-001"18    },19    "destination_type": "location",20    "destination_location": {21      "lat": 33.5,22      "lng": 44.2,23      "address": "Zone 5 Hub"24    },25    "schedule_time": "08:00:00",26    "schedule_days": [27      1,28      2,29      3,30      4,31      532    ],33    "start_date": "2026-04-15",34    "next_session_at": "2026-04-15T05:00:00Z",35    "session_counts": {36      "active": 2,37      "completed": 1,38      "total": 339    },40    "is_active": true41  }42}

Update Route​

Delivery & Last-Mile / Routes
PUT/delivery/routes/:routeId

Partial route-lane update. Route driver_id is optional reserved configuration used only for future scheduled sessions. Changing it never changes an existing session. When any scheduling field is changed, the resulting driver_id, schedule_time, schedule_days, and start_date must all be present or all null. To disable scheduling, send all four fields as null in the same request. Manual assign-deliveries behavior is unchanged.

Path parameters

ParameterTypeDescription
routeIdRequiredstring

Example: route-uuid

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Content-Typestring

Example: application/json

Body parameters

ParameterTypeDescription
route_namestring—
notesstring—
driver_idstring—
schedule_timestring—
schedule_daysarray<number>—
start_datestring—
Staging
Request
curl
1curl --globoff --request PUT "https://staging-api.ondi.io/functions/v1/delivery/routes/${ROUTEID}" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}" \3  --header "Content-Type: application/json" \4  --data-raw '{5  "route_name": "Warehouse A → Zone 6",6  "notes": "Updated route",7  "driver_id": "driver-uuid",8  "schedule_time": "07:00:00",9  "schedule_days": [10    1,11    3,12    513  ],14  "start_date": "2026-04-15"15}'

Response examples

200OK
Response body
json
1{2  "success": true,3  "message": "Route updated successfully",4  "data": {5    "id": "route-uuid",6    "route_name": "Warehouse A → Zone 6",7    "driver_id": "driver-uuid",8    "driver": {9      "id": "driver-uuid",10      "full_name": "Scheduled Driver",11      "avatar_url": null12    },13    "schedule_time": "07:00:00",14    "schedule_days": [15      1,16      3,17      518    ],19    "start_date": "2026-04-15",20    "next_session_at": "2026-04-15T04:00:00Z",21    "updated_at": "2026-04-14T11:00:00Z"22  }23}

Delete Route (Deactivate)​

Delivery & Last-Mile / Routes
DELETE/delivery/routes/:routeId

Soft-deactivate a route (sets is_active=false). Rejected if the route has open/started sessions with active deliveries.

Path parameters

ParameterTypeDescription
routeIdRequiredstring

Example: route-uuid

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Staging
Request
curl
1curl --globoff --request DELETE "https://staging-api.ondi.io/functions/v1/delivery/routes/${ROUTEID}" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}"

Response examples

200OK
Response body
json
1{2  "success": true,3  "message": "Route deactivated successfully",4  "data": null5}

Assign Deliveries to Route​

Delivery & Last-Mile / Routes
POST/delivery/routes/:routeId/assign-deliveries

Prepared P&D/operational-return deliveries use the same assignment owner as direct assignment. In the route transaction, each eligible row keeps its completed pick and reserved warehouse stock and receives one pending handoff for the route driver. Waybill and unrelated deliveries keep their existing route behavior. Realtime mobile contract: the existing consolidated delivery_realtime_assignments row emits assignment_status: "assigned" and requires_driver_response: false. Mobile keeps the current informational notification and does not show Accept/Reject. Missing or unknown actionability is non-actionable.

Assign deliveries to a route. The operation is all-or-nothing and accepts at most 100 UUIDs.

Exactly one session mode is required

  • driver_id: reuses the newest open or started session for the same route and driver and returns assignment_outcome: existing_session; if none exists, it intentionally creates one open session and returns new_session_created.

  • session_id: appends to that exact session when it is open or started and returns existing_session. If it is past started or otherwise cannot accept orders, the backend creates exactly one replacement session for the batch and returns fallback_session_created.

For a fallback, requested_session identifies what the dispatcher requested, session identifies where every delivery in the successful batch was assigned, and fallback_reason.code is authoritative. In driver_id mode, requested_session is always null because no session ID was requested; session is the reused or newly created target. Supported reason codes are SESSION_NOT_ACCEPTING_ORDERS, SESSION_COMPLETED, SESSION_CANCELLED, and SESSION_ROUTE_MISMATCH. If the immutable driver cannot be used, HTTP 409 returns SESSION_DRIVER_UNAVAILABLE and no replacement session remains.

The selected driver must be active/available, have a primary active vehicle, have route-zone access, and have enough capacity. Deliveries must be pending internal child/standalone deliveries with no trip, Waybill, route, route session, or active assignment. The route/session locks, one optional session insert, batch assignments, delivery membership, and capacity update commit atomically in one database transaction; complete failure rolls everything back. Realtime and push notification remain best effort after commit.

Frontend integration contract

  • Send {delivery_ids, driver_id} to reuse-or-create for that route and driver, or {delivery_ids, session_id} to request an exact session. Never send both.

  • Branch only on data.assignment_outcome. existing_session may have requested_session: null when the request used driver_id; always use data.session as the actual target. For fallback_session_created, show the localized data.fallback_reason.message, name/link data.session, and retain data.requested_session only as context.

  • Do not compare IDs or parse localized message text. data.fallback_reason.code is the stable reason contract.

Path parameters

ParameterTypeDescription
routeIdRequiredstring

Example: route-uuid

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Content-Typestring

Example: application/json

Body parameters

ParameterTypeDescription
delivery_idsarray<string>—
driver_idstring—
Staging
Request
curl
1curl --globoff --request POST "https://staging-api.ondi.io/functions/v1/delivery/routes/${ROUTEID}/assign-deliveries" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}" \3  --header "Content-Type: application/json" \4  --data-raw '{5  "delivery_ids": [6    "delivery-uuid-1",7    "delivery-uuid-2",8    "delivery-uuid-3"9  ],10  "driver_id": "driver-uuid"11}'

Response examples

200OK
Response body
json
1{2  "success": true,3  "message": "Orders assigned successfully",4  "data": {5    "assignment_outcome": "fallback_session_created",6    "requested_session": {7      "id": "requested-session-uuid",8      "session_number": 1,9      "status": "in_transit"10    },11    "session": {12      "id": "new-session-uuid",13      "session_number": 2,14      "status": "open",15      "route_id": "route-uuid",16      "driver_id": "driver-uuid",17      "tenant_id": "tenant-uuid",18      "created_at": "2026-08-06T10:00:00Z",19      "started_at": null,20      "updated_at": "2026-08-06T10:00:00Z",21      "cancelled_at": null,22      "completed_at": null,23      "picked_up_at": null,24      "proof_requirements": null,25      "proof_of_pickup": null,26      "proof_of_delivery": null,27      "driver": {28        "id": "driver-uuid",29        "full_name": "Ahmed Hassan",30        "avatar_url": null31      }32    },33    "fallback_reason": {34      "code": "SESSION_NOT_ACCEPTING_ORDERS",35      "message": "The requested session can no longer accept orders."36    },37    "assigned_count": 238  }39}

Remove Deliveries from Route​

Delivery & Last-Mile / Routes
POST/delivery/routes/:routeId/remove-deliveries

Remove deliveries from the route session. Only allowed if the session is open or started. The submitted batch is one transaction. Each current assignment is preserved as unassigned audit history, route/session membership is cleared, delivery status returns to pending, and driver capacity is released. For prepared P&D work, the completed pick and stock reservation remain; the same pending handoff keeps its identity but clears assignment and driver context until reassignment.

Path parameters

ParameterTypeDescription
routeIdRequiredstring

Example: route-uuid

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Content-Typestring

Example: application/json

Body parameters

ParameterTypeDescription
delivery_idsarray<string>—
Staging
Request
curl
1curl --globoff --request POST "https://staging-api.ondi.io/functions/v1/delivery/routes/${ROUTEID}/remove-deliveries" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}" \3  --header "Content-Type: application/json" \4  --data-raw '{5  "delivery_ids": [6    "delivery-uuid-1"7  ]8}'

Response examples

200OK
Response body
json
1{2  "success": true,3  "message": "Deliveries removed from route successfully",4  "data": {5    "removed_count": 16  }7}

List Sessions for Route​

Delivery & Last-Mile / Routes
GET/delivery/routes/:routeId/sessions

Dispatcher view — paginated sessions for a route ordered newest first. Every session includes its immutable driver_id and driver summary. The statuses matrix is unaffected by the active status filter. orders_count is current membership for active sessions or snapshot size for cancelled sessions; released_orders_count reports released cancellation history.

Path parameters

ParameterTypeDescription
routeIdRequiredstring

Example: route-uuid

Query parameters

ParameterTypeDescription
statusstring

Filter: open | started | picked_up | completed | cancelled

pagestring

Page number (default: 1)

Example: 1

limitstring

Items per page (default: 20, max: 100)

Example: 20

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Staging
Request
curl
1curl --globoff --request GET "https://staging-api.ondi.io/functions/v1/delivery/routes/${ROUTEID}/sessions?status=&page=1&limit=20" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}"

Response examples

200OK
Response body
json
1{2  "success": true,3  "message": "Route sessions retrieved successfully",4  "data": {5    "sessions": [6      {7        "id": "session-uuid-2",8        "session_number": 2,9        "status": "open",10        "driver_id": "driver-b",11        "driver": {12          "id": "driver-b",13          "full_name": "Driver B",14          "avatar_url": null15        },16        "orders_count": 3,17        "released_orders_count": 0,18        "created_at": "2026-04-14T05:00:00Z"19      },20      {21        "id": "session-uuid",22        "session_number": 1,23        "status": "completed",24        "driver_id": "driver-a",25        "driver": {26          "id": "driver-a",27          "full_name": "Driver A",28          "avatar_url": null29        },30        "orders_count": 4,31        "released_orders_count": 0,32        "created_at": "2026-04-13T07:55:00Z"33      }34    ],35    "statuses": [36      {37        "status": "open",38        "label": "Open",39        "count": 140      },41      {42        "status": "completed",43        "label": "Completed",44        "count": 145      }46    ],47    "total": 2,48    "page": 1,49    "limit": 2050  }51}

List Driver Route Sessions​

Delivery & Last-Mile / Routes
GET/delivery/route-sessions

Driver-facing. Driver-role callers receive only sessions whose immutable driver_id matches their authenticated driver record; query parameters cannot widen that scope. Dispatcher-only callers can list all tenant sessions and filter by driver_id or route_id. Rows include route context, immutable driver_id, driver summary, and order counts.

Query parameters

ParameterTypeDescription
statusstring

Filter: open | started | picked_up | completed | cancelled

pagestring

Page number (default: 1)

Example: 1

limitstring

Items per page (default: 20, max: 100)

Example: 20

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Staging
Request
curl
1curl --globoff --request GET "https://staging-api.ondi.io/functions/v1/delivery/route-sessions?status=&page=1&limit=20" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}"

Response examples

200OK
Response body
json
1{2  "success": true,3  "message": "Route sessions retrieved successfully",4  "data": {5    "sessions": [6      {7        "id": "session-uuid",8        "session_number": 2,9        "status": "started",10        "driver_id": "driver-uuid",11        "driver": {12          "id": "driver-uuid",13          "full_name": "Ahmed Hassan",14          "avatar_url": null15        },16        "orders_count": 4,17        "released_orders_count": 0,18        "created_at": "2026-04-14T05:00:00Z",19        "route": {20          "id": "route-uuid",21          "route_name": "Warehouse A → Zone 5",22          "origin_type": "warehouse",23          "destination_type": "location",24          "destination_location": {25            "lat": 33.5,26            "lng": 44.2,27            "address": "Zone 5 Hub"28          }29        }30      }31    ],32    "statuses": [33      {34        "status": "started",35        "label": "Started",36        "count": 137      }38    ],39    "total": 1,40    "page": 1,41    "limit": 2042  }43}

Get Route Session Detail​

Delivery & Last-Mile / Routes
GET/delivery/route-sessions/:sessionId

Returns session detail with route info and summary. This endpoint does not include delivery rows or the raw cancellation order snapshot.

session.proof_requirements is the stored strictest requirement calculated when deliveries are added or removed. session.proof_of_pickup and session.proof_of_delivery expose the shared proof after capture for both driver and dispatcher/tenant-admin screens.

For a cancelled session, session.cancellation contains cancelled_at, reason, and the authenticated cancelled_by actor snapshot. session.orders_count and session.released_orders_count and summary.total come from the immutable cancellation snapshot. Its collection summary is zero because released orders no longer belong operationally to that run.

Driver-role callers can access only sessions assigned to their own driver record. Dispatcher-only callers can access any session in the tenant.

Path parameters

ParameterTypeDescription
sessionIdRequiredstring

Example: session-uuid

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Staging
Request
curl
1curl --globoff --request GET "https://staging-api.ondi.io/functions/v1/delivery/route-sessions/${SESSIONID}" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}"

Response examples

200OK
Response body
json
1{2  "success": true,3  "message": "Route session retrieved successfully",4  "data": {5    "session": {6      "id": "session-uuid",7      "session_number": 2,8      "status": "started",9      "driver_id": "driver-uuid",10      "driver": {11        "id": "driver-uuid",12        "full_name": "Ahmed Hassan",13        "avatar_url": null14      },15      "started_at": "2026-04-14T05:05:00Z",16      "picked_up_at": null,17      "proof_requirements": {18        "pickup": {19          "photo_required": true,20          "signature_required": false21        },22        "delivery": {23          "photo_required": true,24          "signature_required": true25        }26      },27      "proof_of_pickup": null,28      "proof_of_delivery": null29    },30    "route": {31      "id": "route-uuid",32      "route_name": "Warehouse A → Zone 5",33      "origin_type": "warehouse",34      "destination_type": "location"35    },36    "summary": {37      "total": 5,38      "status_breakdown": {39        "assigned": 3,40        "picked_up": 241      },42      "collection_summary": {43        "total_to_collect": 15000,44        "pickup_total_to_collect": 0,45        "delivery_total_to_collect": 15000,46        "deliveries_to_collect_count": 1,47        "delivery_collection_required_count": 1,48        "requires_collection_confirmation": true49      }50    }51  }52}

List Route Session Deliveries​

Delivery & Last-Mile / Routes
GET/delivery/route-sessions/:sessionId/deliveries

Paginated deliveries for a route session. Driver-role callers can access only sessions assigned to their own driver record; dispatcher-only callers can access any tenant session. Search is a case-insensitive partial match over delivery code, barcode, and external reference.

Active sessions return currently linked deliveries with released_orders_count=0. Cancelled sessions return immutable snapshot rows in the same deliveries array. A released historical row has status=pending (its release-time operational state), status_at_cancellation, session_membership_status=released, and released_at; it is not labelled cancelled unless the delivery itself was genuinely cancelled. orders_count is the unfiltered session membership total, while total is the filtered result count. Released-session collection totals are zero. Existing page, limit, status, and search behavior remains available.

G2G origin display

every live data.deliveries[] row includes nullable source_transfer_manifest_id and g2g_origin. A non-null origin contains { "branchName": string|null, "branchAddress": string|null } from a valid saved manifest_origin_v1 snapshot. A staged child uses its own usable snapshot first, then its same-tenant, non-deleted parent only when the child manifest ID is absent or matches the parent. Origin fields are inherited together; partial fields are not combined from different snapshots. Normal orders return null for both fields. A known manifest-linked order with no usable saved origin keeps its manifest ID and returns g2g_origin=null. Frontend integration: show a separate read-only Original G2G location line for known manifest orders, displaying any genuine branch name/address available or Origin unavailable when origin is null. Hide it for non-G2G orders. Keep pickup/delivery locations, customer, route navigation, collection amounts, actions, pagination, and totals unchanged. The existing paginated delivery query reads the origin snapshot and parent together; no per-card lookup is needed. Cancelled-session historical snapshots do not acquire these fields.

Path parameters

ParameterTypeDescription
sessionIdRequiredstring

Example: session-uuid

Query parameters

ParameterTypeDescription
pagestring

Page number (default: 1)

Example: 1

limitstring

Items per page (default: 50, max: 100)

Example: 50

statusOptionalstring

Optional delivery status filter, including the terminal failed status

searchOptionalstring

Optional search by delivery_code, barcode, or external_reference_id

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Staging
Request
curl
1curl --globoff --request GET "https://staging-api.ondi.io/functions/v1/delivery/route-sessions/${SESSIONID}/deliveries?page=1&limit=50&status=&search=" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}"

Response examples

200OK
Response body
json
1{2  "success": true,3  "message": "Route session deliveries retrieved successfully",4  "data": {5    "deliveries": [6      {7        "id": "del-uuid",8        "delivery_code": "3213",9        "barcode": "DEL3213",10        "status": "assigned",11        "payment_method": "cod",12        "payer_type": "receiver",13        "cod_amount": 10000,14        "delivery_fee": 5000,15        "feeToCollectAtPickup": 0,16        "feeToCollectAtDelivery": 15000,17        "amountToCollect": 15000,18        "source_transfer_manifest_id": "manifest-uuid",19        "g2g_origin": {20          "branchName": "Original branch",21          "branchAddress": "Original branch address"22        }23      }24    ],25    "orders_count": 1,26    "released_orders_count": 0,27    "summary": {28      "total_to_collect": 15000,29      "pickup_total_to_collect": 0,30      "delivery_total_to_collect": 15000,31      "deliveries_to_collect_count": 1,32      "delivery_collection_required_count": 1,33      "requires_collection_confirmation": true34    },35    "proof_requirements": {36      "pickup": {37        "photo_required": true,38        "signature_required": false39      },40      "delivery": {41        "photo_required": true,42        "signature_required": true43      }44    },45    "proof_of_pickup": null,46    "proof_of_delivery": null,47    "total": 1,48    "page": 1,49    "limit": 50,50    "has_more": false51  }52}

Start Session (Driver)​

Delivery & Last-Mile / Routes
POST/delivery/route-sessions/:sessionId/start

Driver action. Transitions session: open → started and sets started_at. The session must contain at least one active non-terminal delivery.

Returns 409 session_has_no_deliveries when the session is empty. Blocked with delivery_has_open_exception when any non-terminal delivery in the session has an open exception.

Frontend integration contract

  • Disable the Start action when the session delivery total is 0.

  • If the API returns HTTP 409 with the localized empty-session message, keep the session open and show that message.

Path parameters

ParameterTypeDescription
sessionIdRequiredstring

Example: session-uuid

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Staging
Request
curl
1curl --globoff --request POST "https://staging-api.ondi.io/functions/v1/delivery/route-sessions/${SESSIONID}/start" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}"

Response examples

200OK
Response body
json
1{2  "success": true,3  "message": "Route session started successfully",4  "data": {5    "id": "session-uuid",6    "status": "started",7    "started_at": "2026-04-14T08:05:00Z"8  }9}

Pickup Session (Driver)​

Delivery & Last-Mile / Routes
POST/delivery/route-sessions/:sessionId/pickup

Driver action. Transitions session: started → picked_up and keeps one pickup button for the route session.

Read proof_requirements.pickup from the session response. Submit photo_urls when photo_required=true and signature_url when signature_required=true; notes is optional. If both flags are false, the request body may be empty and pickup remains one-click.

The shared proof is stored in route_sessions.proof_of_pickup, copied to deliveries.proof_of_pickup for every included delivery, and normalized photo/signature rows are created in delivery_proofs.

For lifecycle-managed P&D/operational-return members, the route action uses the same atomic warehouse Pickup owner: existing preparation and current assignment/handoff are required; stock leaves the warehouse, a pick movement is recorded, custody becomes in_transit, and delivery/assignment/handoff/picking statuses advance together. Ordinary members keep the established route transition. A session with no eligible deliveries still preserves the existing session pickup behavior. Pickup conflicts return HTTP 409 with the same localized stable code as individual Pickup, including pickup_picking_order_required, pickup_handoff_required, delivery_has_open_exception, warehouse_item_exception_open, and pickup_stock_unavailable; the complete session transaction remains unchanged.

Payment behavior

each zero-fee delivery is marked paid without a payment transaction. A sender-paid cash delivery records its fee-only pickup collection and delivery-fee payment exactly once. Retrying after the session reached picked_up repairs any missing payment side effect without changing the session again.

Path parameters

ParameterTypeDescription
sessionIdRequiredstring

Example: session-uuid

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Body parameters

ParameterTypeDescription
signature_urlstring—
photo_urlsarray<string>—
notesstring—
Staging
Request
curl
1curl --globoff --request POST "https://staging-api.ondi.io/functions/v1/delivery/route-sessions/${SESSIONID}/pickup" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}" \3  --header "Content-Type: application/json" \4  --data-raw '{5  "signature_url": "https://example.com/signature.png",6  "photo_urls": [7    "https://example.com/pickup.jpg"8  ],9  "notes": "Shared route pickup proof"10}'

Response examples

200OK
Response body
json
1{2  "success": true,3  "message": "Route session picked up successfully",4  "data": {5    "id": "session-uuid",6    "status": "picked_up",7    "picked_up_at": "2026-04-14T08:20:00Z",8    "proof_requirements": {9      "pickup": {10        "photo_required": true,11        "signature_required": false12      },13      "delivery": {14        "photo_required": true,15        "signature_required": true16      }17    },18    "proof_of_pickup": {19      "signature": null,20      "photos": [21        "https://example.com/pickup.jpg"22      ],23      "notes": "Shared route pickup proof"24    },25    "proof_of_delivery": null26  }27}

Deliver Session (Driver)​

Delivery & Last-Mile / Routes
POST/delivery/route-sessions/:sessionId/deliver

Driver action. Transitions outstanding session deliveries to delivered with one shared route-session proof capture.

Read proof_requirements.delivery from the session response. Submit photo_urls when photo_required=true and signature_url when signature_required=true; notes is optional. If both flags are false, the request body may be empty.

The shared proof is stored in route_sessions.proof_of_delivery, copied to deliveries.proof_of_delivery for every included delivery, and normalized photo/signature rows are created in delivery_proofs.

Payment behavior

receiver cash combines goods COD and delivery fee in one physical dropoff collection; receiver wallet charges the receiver at completion; sender wallet was settled at creation; sender cash was settled at pickup; zero-fee deliveries are paid automatically. COD audit/payout and one driver earning per delivered child are idempotent. Retrying a completed session repairs missing payment or earning side effects without another charge.

Path parameters

ParameterTypeDescription
sessionIdRequiredstring

Example: session-uuid

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Body parameters

ParameterTypeDescription
signature_urlstring—
photo_urlsarray<string>—
notesstring—
Staging
Request
curl
1curl --globoff --request POST "https://staging-api.ondi.io/functions/v1/delivery/route-sessions/${SESSIONID}/deliver" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}" \3  --header "Content-Type: application/json" \4  --data-raw '{5  "signature_url": "https://example.com/signature.png",6  "photo_urls": [7    "https://example.com/delivery.jpg"8  ],9  "notes": "Shared route delivery proof"10}'

Response examples

200OK
Response body
json
1{2  "success": true,3  "message": "Route session delivered successfully",4  "data": {5    "id": "session-uuid",6    "status": "completed",7    "completed_at": "2026-04-14T10:00:00Z",8    "proof_requirements": {9      "pickup": {10        "photo_required": true,11        "signature_required": false12      },13      "delivery": {14        "photo_required": true,15        "signature_required": true16      }17    },18    "proof_of_pickup": {19      "signature": null,20      "photos": [21        "https://example.com/pickup.jpg"22      ]23    },24    "proof_of_delivery": {25      "signature": "https://example.com/signature.png",26      "photos": [27        "https://example.com/delivery.jpg"28      ],29      "notes": "Shared route delivery proof"30    }31  }32}

List Eligible Deliveries for Route​

Delivery & Last-Mile / Routes
GET/delivery/routes/:routeId/eligible-deliveries

Returns paginated deliveries eligible for route assignment.

Eligibility criteria

  • status is pending and carrier_type is internal.

  • trip_id, waybill_id, route_id, and route_session_id are all null.

  • P&D parent orders are excluded; only operational child legs can appear.

Search behavior

  • search performs a case-insensitive partial match against delivery_code and external_reference_id.

  • PostgREST search control characters are escaped, so %, _, backslashes, quotes, commas, and parentheses are treated as literal text.

Frontend integration contract

use this endpoint for the route picker, but treat assignment-time HTTP 409 results as authoritative because eligibility is revalidated atomically. No navigation action is returned.

Path parameters

ParameterTypeDescription
routeIdRequiredstring

Example: route-uuid

Query parameters

ParameterTypeDescription
pagestring

Page number

Example: 1

limitstring

Items per page (max 100)

Example: 20

searchstring

Search by delivery_code or external_reference_id

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Staging
Request
curl
1curl --globoff --request GET "https://staging-api.ondi.io/functions/v1/delivery/routes/${ROUTEID}/eligible-deliveries?page=1&limit=20&search=" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}"

Response examples

200OK
Response body
json
1{2  "success": true,3  "message": "Eligible deliveries retrieved successfully",4  "data": {5    "deliveries": [6      {7        "id": "del-uuid",8        "delivery_code": 1865,9        "barcode": "BAR1865",10        "external_reference_id": "ORD-001",11        "status": "pending",12        "unit_size": 1,13        "pickup_location": {14          "address": "Erbil, 60m Street"15        },16        "delivery_location": {17          "address": "Sulaymaniyah, Salim St"18        },19        "customer": {20          "id": "cust-uuid",21          "full_name": "Ahmad Karimi",22          "phone": "+9647501111111"23        }24      }25    ],26    "total": 24,27    "page": 1,28    "limit": 2029  }30}

Cancel Session (Dispatcher)​

Delivery & Last-Mile / Routes
POST/delivery/route-sessions/:sessionId/cancel

Dispatcher action. Requires a non-empty reason of at most 500 characters. The cancelling actor is always resolved from the authenticated request and cannot be supplied by the client. Cancels an open or started session; picked_up, in_transit, completed, and already-cancelled sessions are rejected.

In one atomic transaction, the backend snapshots the cancellation actor, reason, timestamp, and session order grid fields; marks active assignments unassigned with the route_session_released audit context; releases non-terminal orders to pending with route/session links cleared; releases driver capacity; and marks the session cancelled. The marked release is permitted by the route ownership guard, while generic assignment unassign and driver-switch operations remain blocked. The historical snapshot survives later reassignment.

Blocked with delivery_has_open_exception when any non-terminal delivery in the session has an open exception.

Frontend integration contract

  • Require reason in the cancellation dialog and submit exactly { reason }.

  • Use orders_count and released_orders_count from session lists.

  • Continue opening the existing session deliveries endpoint; render session_membership_status=released rows as history and disable selection/removal actions.

  • Display session.cancellation.cancelled_at, .reason, and .cancelled_by. No new navigation action is returned.

Path parameters

ParameterTypeDescription
sessionIdRequiredstring

Example: session-uuid

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Body parameters

ParameterTypeDescription
reasonstring—
Staging
Request
curl
1curl --globoff --request POST "https://staging-api.ondi.io/functions/v1/delivery/route-sessions/${SESSIONID}/cancel" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}" \3  --header "Content-Type: application/json" \4  --data-raw '{5  "reason": "Driver unavailable"6}'

Response examples

200OK
Response body
json
1{2  "success": true,3  "message": "Route session cancelled successfully",4  "data": {5    "id": "session-uuid",6    "status": "cancelled",7    "cancelled_at": "2026-08-03T10:00:00Z",8    "orders_count": 4,9    "released_orders_count": 4,10    "cancellation": {11      "cancelled_at": "2026-08-03T10:00:00Z",12      "reason": "Driver unavailable",13      "cancelled_by": {14        "id": "user-uuid",15        "full_name": "Admin Name"16      }17    }18  }19}