Skip to main content

Trips

20 API calls in this section.

Trip management — driver work plans with ordered delivery stops.

A Trip groups multiple deliveries for one driver. Storage is delivery-level (each delivery holds trip_id + trip_sequence), while an optional trip-level stop order stores dispatcher-defined stop priorities. The GET detail endpoint returns the effective stop_list for the dispatcher and driver execution views. Pickup stops are grouped only when their coordinates match, and drop-off stops are grouped only when their coordinates match. Address IDs, labels, names, and formatted addresses are not used for stop matching. A grouped drop-off is always placed after every pickup containing one of its parcels. Each stop exposes a stable stop_key, and each delivery exposes its active assignment_status, next_action, and bulk_eligible state for driver group actions. Successful pickup rows from the stop bulk-action endpoint are published as one bounded notification operation; customer and driver events remain per applicable delivery while dispatchers/admins receive one operation summary. Notification delivery is asynchronous and is never the source of trip or pickup truth. Every trip domain error returns a stable machine-readable code, a localized display message, and data (usually null; ordered delivery validation uses it for per-order results).

List Trips​

Delivery & Last-Mile / Trips
GET/delivery/trips

List trips with optional filters, column-level filters, sorting, and pagination. Returns item_count per trip.

Query parameters

ParameterTypeDescription
pagestring

Page number

Example: 1

limitstring

Items per page (max 100)

Example: 20

statusstring

Filter by status: planned, in_progress, completed, cancelled

Example: planned

driver_idstring

Filter by driver UUID

trip_typestring

Filter by type: multi_pickup, multi_dropoff, mixed

trip_numberstring

Search by trip number (partial)

from_datestring

Start date YYYY-MM-DD (normalized to 00:00:00)

to_datestring

End date YYYY-MM-DD (normalized to 23:59:59)

sort_bystring

Sort by: trip_number, status, trip_type, notes, created_at, updated_at, started_at, completed_at

Example: created_at

sort_orderstring

Sort order: asc or desc

Example: desc

column_filtersstring

JSON object. Supported keys: trip_number, status, trip_type, driver_id, vehicle_id, notes, created_at, updated_at, started_at, completed_at. Date keys accept {"from":"...","to":"..."}.

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Staging
Request
curl
1curl --globoff --request GET "https://staging-api.ondi.io/functions/v1/delivery/trips?page=1&limit=20&status=planned&driver_id=&trip_type=&trip_number=&from_date=&to_date=&sort_by=created_at&sort_order=desc&column_filters=" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}"

Response examples

200OK
Response body
json
1{2  "success": true,3  "message": "Trips retrieved successfully",4  "data": {5    "trips": [6      {7        "id": "trip-uuid",8        "trip_number": "TRIP-00001",9        "status": "planned",10        "trip_type": "multi_pickup",11        "notes": "Morning run",12        "started_at": null,13        "completed_at": null,14        "created_at": "2026-04-11T10:00:00Z",15        "updated_at": "2026-04-11T10:00:00Z",16        "item_count": 3,17        "driver": {18          "id": "driver-uuid",19          "full_name": "Ahmed Hassan",20          "phone": "+9647501234567"21        },22        "vehicle": {23          "id": "vehicle-uuid",24          "name": "Toyota Hiace",25          "code": "VH-001"26        }27      }28    ],29    "total": 1,30    "page": 1,31    "limit": 2032  }33}

Create Trip​

Delivery & Last-Mile / Trips
POST/delivery/trips

Realtime mobile contract

a newly created Trip assignment is immediately assigned. Its existing delivery_realtime_assignments notification therefore emits assignment_status: "assigned" and requires_driver_response: false. Mobile displays it as information and does not show Accept/Reject. Missing or unknown actionability is also non-actionable.

Create a new trip. Metadata fields are optional when creating an empty trip. driver_id is required when deliveries is non-empty.

  • trip_type: multi_pickup | multi_dropoff | mixed (optional, informational)

  • deliveries: optional array of {delivery_id, sequence?}. Sequence auto-assigned if not provided.

  • All submitted deliveries are validated before the trip row or assignment rows are created. Validation results follow request order.

  • Unassigned pending deliveries are assigned immediately to the trip driver using the normal driver, primary-vehicle, capacity, zone, team, service-policy, audit, realtime, and notification rules. Shared driver and policy context is loaded once, and all missing assignments are written through the atomic batch-assignment transaction.

  • Newly assigned rows use status=assigned; route calculation is scheduled asynchronously after commit. One bulk realtime summary and one bulk driver notification represent the newly assigned rows, with assignment_status=assigned and requires_driver_response=false.

  • A delivery already assigned to the same driver is reused. A delivery assigned to another driver is rejected and is never automatically reassigned.

  • Eligible delivery statuses: pending, assigned. Only internal operational deliveries are accepted.

  • Deliveries linked to any Waybill, route, route session, or another trip are rejected.

  • P&D parent orders and inactive draft legs are rejected; attach only the currently active internal child leg.

Frontend integration contract

  • Select a trip driver before sending a non-empty deliveries array.

  • After success, use the returned deliveries; newly assigned deliveries have status=assigned.

  • On HTTP 409 with code=trip_delivery_validation_failed, keep the form and show each data.results[].error_message against its matching delivery_id.

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Content-Typestring

Example: application/json

Body parameters

ParameterTypeDescription
driver_idstring—
vehicle_idstring—
trip_typestring—
notesstring—
deliveriesarray<object>—
deliveries.delivery_idstring—
deliveries.sequencenumber—
Staging
Request
curl
1curl --globoff --request POST "https://staging-api.ondi.io/functions/v1/delivery/trips" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}" \3  --header "Content-Type: application/json" \4  --data-raw '{5  "driver_id": "driver-uuid",6  "vehicle_id": "vehicle-uuid",7  "trip_type": "multi_pickup",8  "notes": "Morning delivery run",9  "deliveries": [10    {11      "delivery_id": "delivery-uuid-1",12      "sequence": 113    },14    {15      "delivery_id": "delivery-uuid-2",16      "sequence": 217    }18  ]19}'

Response examples

201Created
Response body
json
1{2  "success": true,3  "message": "Trip created successfully",4  "data": {5    "id": "trip-uuid",6    "trip_number": "TRIP-00001",7    "status": "planned",8    "trip_type": "multi_pickup",9    "notes": "Morning delivery run",10    "driver": {11      "id": "driver-uuid",12      "full_name": "Ahmed Hassan",13      "phone": "+9647501234567"14    },15    "vehicle": {16      "id": "vehicle-uuid",17      "name": "Toyota Hiace",18      "code": "VH-001"19    },20    "deliveries": [21      {22        "id": "delivery-uuid-1",23        "delivery_code": 1001,24        "barcode": "BC001",25        "status": "assigned",26        "trip_sequence": 127      },28      {29        "id": "delivery-uuid-2",30        "delivery_code": 1002,31        "barcode": "BC002",32        "status": "assigned",33        "trip_sequence": 234      }35    ],36    "summary": {37      "total": 2,38      "planned": 2,39      "in_progress": 0,40      "delivered": 0,41      "returned": 0,42      "cancelled": 0,43      "with_return_request": 044    },45    "driver_execution": {46      "stop_list": [47        {48          "stop_type": "pickup",49          "location": {50            "latitude": 33.3,51            "longitude": 44.452          },53          "deliveries": [54            {55              "delivery_id": "delivery-uuid-1",56              "delivery_code": 1001,57              "barcode": "BC001",58              "status": "assigned",59              "trip_sequence": 160            }61          ],62          "is_complete": false63        }64      ],65      "current_stop": {66        "stop_type": "pickup",67        "location": {},68        "deliveries": []69      },70      "next_stop": null71    }72  }73}

Get Trip Detail​

Delivery & Last-Mile / Trips
GET/delivery/trips/{{trip_id}}

Get full trip detail including

  • Trip header (number, status, type, general notes) and concurrency token updated_at

  • status_updates: status-only timeline with previous/new status, timestamp, reason, and notes

  • trip_events: complete append-only staff history for users with trip-management permission

  • available_actions: manager actions currently allowed (cancel, switch_driver)

  • Driver and vehicle info

  • Ordered deliveries and deliveries_mode (live or historical)

  • Summary counts (total, per-status)

  • driver_execution: coordinate-grouped stop_list, current_stop, next_stop

Live deliveries and stop delivery references include assignment_status plus the literal next_action: start_delivery, pickup, in_transit, complete, or null. bulk_eligible is true only when one of those actions is available. Each stop includes the stable stop_key that must be returned unchanged to stop-order or bulk-action requests. Assignment enrichment is one batched query for the whole trip, never one query per delivery.

trip_events[] records trip_created, trip_updated, driver_assigned, driver_unassigned, driver_changed, deliveries_added, deliveries_removed, deliveries_reordered, stops_reordered, trip_started, trip_completed, trip_cancelled, deliveries_moved_out, and deliveries_moved_in. Each event returns event_type, timestamp, actor fields (actor_id, actor_name, actor_role), status context, reason/notes, and optional changes. New move events also return source_trip_id, source_trip_number, target_trip_id, and target_trip_number; older move events may contain only the IDs. Historical events created before this feature may have null actor fields because the previous data did not preserve the actor; the API never invents one. Driver-only callers do not receive trip_events, available_actions, or historical delivery snapshots.

For a cancelled trip, the manager response returns the cancellation-time snapshot through data.deliveries, sets data.deliveries_mode=historical, calculates summary from that snapshot, returns no available actions, and keeps driver_execution.stop_list, current_stop, and next_stop empty. Each historical delivery contains id, delivery_code, barcode, pickup_location, dropoff_location, unit_size, cancellation-time status, payment_method, cod_amount, external_reference_id, and trip_sequence. Cancelled trips created before snapshot storage may return an empty historical list because former membership is not invented without persisted evidence.

Stop ordering rules

  • Pickup locations are grouped only by exact valid latitude/longitude coordinates.

  • Drop-off locations are grouped only by exact valid latitude/longitude coordinates.

  • Pickup and drop-off remain separate stop types even when their coordinates match.

  • Locations without valid coordinates are not grouped.

  • Address IDs, labels, names, and formatted addresses do not determine grouping.

  • A drop-off is placed only after every pickup stop required by the deliveries inside that drop-off.

  • A saved dispatcher stop order is preferred; obsolete keys are ignored and new keys are inserted in a valid deterministic position.

  • A pickup or drop-off shared by every current order remains first or last respectively.

  • Without a saved stop order, delivery trip_sequence remains the preference whenever it does not violate pickup-before-drop-off.

  • current_stop is the first incomplete stop in the effective order; next_stop is the following stop.

Frontend integration contract

  • Build the Trip Events section from data.trip_events in array order, using event_type as the machine-readable display mapping key.

  • Show actor name/role and timestamp for each event; show reason, notes, and changes only when present. For move events, display source_trip_number and target_trip_number when present, falling back to their IDs for older history.

  • Show Cancel Trip and Switch Driver only when their literal values are present in data.available_actions.

  • Render data.deliveries in the existing Deliveries tab. When data.deliveries_mode=historical, keep the list read-only.

  • Keep data.status_updates only for the compact status timeline. Do not treat top-level data.notes as an event note.

  • Render data.driver_execution.stop_list, current_stop, and next_stop directly. Use stop_key and select only deliveries whose next_action equals the group button action.

G2G origin display

each live data.deliveries[] row includes nullable source_transfer_manifest_id and g2g_origin. A non-null g2g_origin contains { "branchName": string|null, "branchAddress": string|null } from a valid saved manifest_origin_v1 snapshot; staged children inherit usable origin from their same-tenant, non-deleted parent when needed only if their manifest ID is absent or matches the parent. Normal orders and orders without usable saved origin return null. When source_transfer_manifest_id is non-null, render a separate read-only Original G2G location line on the delivery card, using any available branch name or address; if g2g_origin is null, show Origin unavailable. Hide the line only when the manifest ID is null or absent. Keep pickup_location, dropoff_location, driver_execution, stop grouping, maps, and action targets unchanged. The same batched delivery query returns the metadata; do not fetch per-card details or current branch addresses. Historical cancelled-trip snapshots do not acquire these new fields and their origin line remains hidden.

Path parameters

ParameterTypeDescription
trip_idRequiredstring

Variable used inside the request path.

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

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

Response examples

200OK
Response body
json
1{2  "success": true,3  "message": "Trip retrieved successfully",4  "data": {5    "id": "trip-uuid",6    "trip_number": "TRIP-00001",7    "status": "in_progress",8    "updated_at": "2026-09-10T10:00:00.000Z",9    "deliveries": [10      {11        "id": "d1",12        "delivery_code": 1001,13        "status": "picked_up",14        "trip_sequence": 1,15        "assignment_status": "picked_up",16        "next_action": "in_transit",17        "source_transfer_manifest_id": "manifest-uuid",18        "g2g_origin": {19          "branchName": "Original branch",20          "branchAddress": "Original branch address"21        }22      },23      {24        "id": "d2",25        "delivery_code": 1002,26        "status": "en_route_to_pickup",27        "trip_sequence": 2,28        "assignment_status": "en_route_to_pickup",29        "next_action": "pickup",30        "source_transfer_manifest_id": null,31        "g2g_origin": null32      }33    ],34    "driver_execution": {35      "stop_list": [36        {37          "stop_key": "pickup:33.3,44.4",38          "stop_type": "pickup",39          "location": {40            "latitude": 33.3,41            "longitude": 44.442          },43          "deliveries": [44            {45              "delivery_id": "d1",46              "delivery_code": 1001,47              "status": "picked_up",48              "trip_sequence": 1,49              "assignment_status": "picked_up",50              "next_action": null,51              "bulk_eligible": false52            },53            {54              "delivery_id": "d2",55              "delivery_code": 1002,56              "status": "en_route_to_pickup",57              "trip_sequence": 2,58              "assignment_status": "en_route_to_pickup",59              "next_action": "pickup",60              "bulk_eligible": true61            }62          ],63          "is_complete": false64        }65      ],66      "current_stop": {67        "stop_key": "pickup:33.3,44.4",68        "stop_type": "pickup",69        "is_complete": false70      },71      "next_stop": null72    }73  }74}

Update Trip​

Delivery & Last-Mile / Trips
PATCH/delivery/trips/{{trip_id}}

Update planned-trip metadata only. Supported fields are optional trip_type and notes. Driver and vehicle changes are intentionally rejected with HTTP 409 code=trip_driver_switch_endpoint_required; use POST /delivery/trips/{id}/switch-driver so assignment, custody, capacity, audit, and both-driver notifications stay consistent.

Path parameters

ParameterTypeDescription
trip_idRequiredstring

Variable used inside the request path.

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Content-Typestring

Example: application/json

Body parameters

ParameterTypeDescription
trip_typestring—
notesstring—
Staging
Request
curl
1curl --globoff --request PATCH "https://staging-api.ondi.io/functions/v1/delivery/trips/${TRIP_ID}" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}" \3  --header "Content-Type: application/json" \4  --data-raw '{5  "trip_type": "multi_dropoff",6  "notes": "Updated notes"7}'

Response examples

200OK
Response body
json
1{2  "success": true,3  "message": "Trip updated successfully",4  "data": {5    "id": "trip-uuid",6    "status": "planned"7  }8}

Switch Trip Driver​

Delivery & Last-Mile / Trips
POST/delivery/trips/{{trip_id}}/switch-driver

Changes the driver of a planned or in_progress trip. Requires trip-management permission. driver_id and reason are required; notes is optional. The backend selects the new driver's primary active vehicle.

Business rules

  • The new driver must be active and available, have an active primary vehicle with enough remaining capacity, and satisfy every active trip delivery's zone, team, and service-vehicle rules.

  • Completed, failed, cancelled, and returned deliveries are not reassigned. Every non-terminal member must have exactly one active assignment to the current trip driver.

  • The switch is atomic and preserves trip membership, trip sequence, stop order, delivery progress, pickup/delivery timestamps, and trip status. Active assignment ownership, custody, vehicle, and capacity move immediately to the new driver. Pre-pickup assignment acceptance follows tenant driver settings.

  • Repeating the request with the current driver returns changed=false and creates no duplicate event or notification.

  • One in-app and push notification is created for each affected driver. The previous driver receives a critical modal requiring acknowledgement. The new driver receives a normal toast before pickup or an important persistent banner after pickup. Post-pickup notifications include the counterparty contact actions. Notification acknowledgement does not delay the custody switch.

Success data contains id, trip_number, authoritative status, changed, previous_driver_id, driver_id, vehicle_id, delivery_count, and picked_up_delivery_count.

Important errors

  • HTTP 400 code=validation_error for missing/invalid fields.

  • HTTP 403 code=insufficient_permissions or assignment_change_disabled.

  • HTTP 404 code=trip_not_found or driver_not_found.

  • HTTP 409 code=trip_driver_switch_not_allowed, trip_driver_required, new_driver_not_active, new_driver_has_no_vehicle, delivery_assigned_to_different_driver, invalid_delivery_status_for_driver_switch, zone/team/service-vehicle errors, or insufficient_vehicle_capacity_for_new_driver.

Frontend integration contract

  • Show Switch Driver only when the trip has a driver and status is planned or in_progress.

  • Send only driver_id, reason, and optional notes; do not send vehicle_id.

  • On success, replace the displayed driver and vehicle using the returned IDs or reload trip detail.

  • Branch on code and display the localized message.

Path parameters

ParameterTypeDescription
trip_idRequiredstring

Variable used inside the request path.

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Content-Typestring

Example: application/json

Body parameters

ParameterTypeDescription
driver_idstring—
reasonstring—
notesstring—
Staging
Request
curl
1curl --globoff --request POST "https://staging-api.ondi.io/functions/v1/delivery/trips/${TRIP_ID}/switch-driver" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}" \3  --header "Content-Type: application/json" \4  --data-raw '{5  "driver_id": "new-driver-uuid",6  "reason": "Driver shift changed",7  "notes": "Coordinate the package handover with dispatch"8}'

Response examples

200OK
Response body
json
1{2  "success": true,3  "message": "Trip driver changed successfully",4  "data": {5    "id": "trip-uuid",6    "trip_number": "TRIP-00016",7    "status": "in_progress",8    "changed": true,9    "previous_driver_id": "old-driver-uuid",10    "driver_id": "new-driver-uuid",11    "vehicle_id": "vehicle-uuid",12    "delivery_count": 3,13    "picked_up_delivery_count": 214  }15}

Change Trip Status​

Delivery & Last-Mile / Trips
PATCH/delivery/trips/{{trip_id}}/status

Starts, completes, or cancels an Eaglepost trip. Requires manage:operations:tenant, manage:module:delivery, or system-admin access.

Start rules

  • in_progress requires a planned trip with a driver and at least one delivery.

  • Every member may be assigned, en_route_to_pickup, picked_up, or in_transit and must have exactly one active assignment to the trip driver. This allows a planned trip to start after receiving already-active orders through the move endpoint without resetting their progress.

  • Starting changes only the trip status and start timestamp; it does not reset order or assignment progress.

  • Repeating in_progress is idempotent with changed=false.

Completion rules

  • completed is accepted only for an in_progress or already completed trip.

  • Existing delivered, failed, cancelled, and returned members are retained as successful unchanged results.

  • Every remaining member is marked delivered through the same individual delivery-status flow used by POST /delivery/deliveries/{deliveryId}/status. This preserves assignment completion, payment/COD handling, package outcomes, capacity, earnings, notifications, audit history, P&D child handling, and the individual flow's validation behavior. The trip driver is supplied if that flow must recreate a missing assignment.

  • After delivery processing, the endpoint atomically rechecks current membership. When every current member is terminal, it explicitly completes the trip and records its completion timestamp/history without changing order state, assignment, driver, or order history. This also handles a source trip left with only terminal orders after a move.

  • Delivery processing uses bounded concurrency. Results are returned in trip sequence order.

  • A fully successful retry of an already completed trip is idempotent.

  • If any member fails, successful member changes remain persisted, the API returns HTTP 409 code=trip_deliveries_not_completed, and data.status remains authoritative.

Move interaction

  • Moving orders never starts or completes either trip. Only this later explicit status request may change the trip status.

Cancellation rules

  • cancelled delegates to the same atomic cancellation service as POST /delivery/trips/{id}/cancel; the dedicated endpoint remains backward compatible.

  • Every current member must still be pending, assigned, or en_route_to_pickup. A picked-up, in-transit, delivered, cancelled, returned, exception, or otherwise non-cancellable member blocks the whole trip cancellation.

  • Cancellation releases active assignments and driver capacity, returns every member to pending, and clears trip_id and trip_sequence.

  • Repeating cancellation is idempotent and creates no duplicate notification. A real cancellation sends the assigned driver one critical modal requiring acknowledgement plus push notification.

Completion response data contains id, previous_status, authoritative status, changed, timestamps, available_actions, order-wise results, total_success, total_changed, and total_failed. Cancellation returns the existing cancellation result including release counts and authoritative status.

Important errors

  • HTTP 400 code=validation_error for a missing/invalid status or invalid trip UUID. Missing completion or cancellation reason returns HTTP 400 error=reason_required and field=reason.

  • HTTP 403 code=insufficient_permissions.

  • HTTP 404 code=trip_not_found.

  • HTTP 409 code=invalid_trip_status_transition, trip_has_no_deliveries, trip_deliveries_not_ready_to_start, trip_deliveries_not_completed, or trip_deliveries_not_releasable.

Frontend integration contract

  • Start sends { "status": "in_progress" }.

  • Complete sends { "status": "completed", "reason": "...", "notes": "..." }.

  • Cancel may now send { "status": "cancelled", "reason": "..." } to this PATCH endpoint. The dedicated POST cancel endpoint remains supported.

  • Never send planned to this endpoint.

  • On HTTP 200, use the authoritative data.status and timestamps. Completion also returns data.results.

  • On completion HTTP 409, show each failed data.results[] entry's localized message. Branch on machine codes, never message text.

  • No navigation action is returned.

Path parameters

ParameterTypeDescription
trip_idRequiredstring

Variable used inside the request path.

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Content-Typestring

Example: application/json

Body parameters

ParameterTypeDescription
statusRequiredstring

in_progress, completed, or cancelled.

reasonstring

(required for completed and cancelled).

notesOptionalstring

is optional. Successful status changes persist reason in the Trip's append-only status_updates timeline. Completion also applies them to each delivery status update.

Staging
Request
curl
1curl --globoff --request PATCH "https://staging-api.ondi.io/functions/v1/delivery/trips/${TRIP_ID}/status" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}" \3  --header "Content-Type: application/json" \4  --data-raw '{5  "status": "cancelled",6  "reason": "Trip cancelled by dispatcher",7  "notes": "Vehicle issue reported before pickup"8}'

Response examples

200OK
Response body
json
1{2  "success": true,3  "message": "Trip status updated successfully",4  "data": {5    "id": "trip-uuid",6    "previous_status": "planned",7    "status": "in_progress",8    "changed": true,9    "started_at": "2026-07-29T12:30:00Z",10    "completed_at": null,11    "released_delivery_count": 0,12    "released_assignment_count": 0,13    "available_actions": [14      "cancel"15    ]16  }17}

List Eligible Deliveries for Trip​

Delivery & Last-Mile / Trips
GET/delivery/trips/eligible-deliveries

Returns operational deliveries that can be added to a trip.

Eligibility rules

  • Status is pending or assigned

  • Carrier is internal

  • Not deleted

  • Not linked to a Waybill, route, route session, or trip

  • Not a P&D parent order

  • For staged P&D, only the currently active child leg is returned: the pending/assigned inbound leg before staging, or the pending/assigned outbound leg after staging. Draft future legs are excluded.

Supports indexed search by delivery code, barcode, or external_reference_id and indexed newest-first pagination. The same rules are revalidated by POST; the picker response is not trusted as authorization.

Query parameters

ParameterTypeDescription
pagestring

Page number

Example: 1

limitstring

Items per page (max 100)

Example: 20

searchstring

Search by barcode or external_reference_id

tenant_idstring

Tenant ID

languagestring

Language

Example: en

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Staging
Request
curl
1curl --globoff --request GET "https://staging-api.ondi.io/functions/v1/delivery/trips/eligible-deliveries?page=1&limit=20&search=&tenant_id=&language=en" \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    {6      "id": "delivery-uuid-1",7      "delivery_code": 1001,8      "barcode": "BC001",9      "status": "pending",10      "external_reference_id": null,11      "payment_method": "cod",12      "cod_amount": 15000,13      "unit_size": 1,14      "pickup_location": {15        "latitude": 33.3,16        "longitude": 44.417      },18      "delivery_location": {19        "latitude": 33.5,20        "longitude": 44.621      },22      "pickup_warehouse": {23        "id": "wh-uuid",24        "name": "Main WH",25        "code": "WH01"26      },27      "dropoff_warehouse": null,28      "pickup_zone": {29        "id": "zone-uuid",30        "name": "Zone A",31        "code": "ZA"32      },33      "dropoff_zone": null34    }35  ],36  "total": 5,37  "page": 1,38  "limit": 2039}

Validate Scanned Trip Delivery​

Delivery & Last-Mile / Trips
POST/delivery/trips/deliveries/scan-validate

Read-only pre-create validation for trip scan mode. Use this when a hardware scanner or camera returns the printed shipment-label value such as DEL2993.

  • barcode is required. The backend trims and uppercases it, then resolves by exact stored barcode or by delivery_code after stripping the printed DEL prefix.

Business rules

  • This endpoint performs no writes and does not reserve capacity.

  • Driver, delivery, staged-child, and active-assignment context is resolved in one database call per scan.

  • The requested driver must exist for the tenant and be active.

  • If the scanned label resolves to a staged/P&D parent order, the backend resolves that parent to the currently active child delivery leg and validates the child. The response data.delivery is always the delivery row the trip should add.

  • Valid means the resolved delivery currently passes trip membership rules and any active assignment already belongs to the requested driver.

  • It rejects deleted deliveries, deliveries attached to a Waybill, route, route session, or another trip, non-internal carriers, non-pending/non-assigned statuses, active assignments for a different driver, and staged parents with no active child leg.

  • The final POST /delivery/trips request remains authoritative and repeats validation before creating the trip and assigning deliveries.

Frontend integration contract

  • Call this endpoint for each scan after a driver is selected.

  • On data.valid=true, always add/check duplicates by data.delivery.id. If the scanned parent was resolved, data.resolved_from_parent identifies the scanned parent and child, but the frontend must submit the returned child data.delivery.id.

  • If data.delivery.id is already in the local unsaved trip list, show the duplicate warning and do not add another row. Otherwise add it to the same local delivery list used by searched rows and display data.message.

  • On data.valid=false, display data.message and branch on data.code only if UI behavior needs a machine reason.

  • Submit searched and scanned rows together to POST /delivery/trips as deliveries: [{ delivery_id, sequence }]. No navigation action is returned.

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Content-Typestring

Example: application/json

Body parameters

ParameterTypeDescription
driver_idRequiredstring

is required. It is the intended trip driver UUID.

barcodestring—
Staging
Request
curl
1curl --globoff --request POST "https://staging-api.ondi.io/functions/v1/delivery/trips/deliveries/scan-validate" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}" \3  --header "Content-Type: application/json" \4  --data-raw '{5  "driver_id": "driver-uuid",6  "barcode": "DEL2993"7}'

Response examples

200OK
Response body
json
1{2  "success": true,3  "message": "Scanned delivery validated",4  "data": {5    "scanned_code": "DEL4108",6    "normalized_delivery_code": "4108",7    "valid": true,8    "assignment_action": "assign",9    "message": "Delivery is ready to add to this trip",10    "resolved_from_parent": {11      "parent_delivery_id": "parent-delivery-uuid",12      "parent_delivery_code": "4108",13      "delivery_id": "child-delivery-uuid",14      "delivery_code": "4108B"15    },16    "delivery": {17      "id": "child-delivery-uuid",18      "delivery_code": "4108B",19      "barcode": "DEL4108B",20      "external_reference_id": "ORDER-4108",21      "status": "pending",22      "unit_size": 1,23      "payment_method": "cod",24      "cod_amount": 15000,25      "pickup_location": {26        "latitude": 33.3,27        "longitude": 44.428      },29      "dropoff_location": {30        "latitude": 33.5,31        "longitude": 44.632      }33    }34  }35}

Validate Trip Deliveries​

Delivery & Last-Mile / Trips
POST/delivery/trips/{{trip_id}}/deliveries/validate

Read-only preflight for adding deliveries to a planned trip. The trip must have a driver. Results are returned in the exact submitted order and contain delivery_id, delivery_code, valid, and either assignment_action or localized error_key/error_message.

Assignment actions

  • assign: pending delivery has no active assignment and will be assigned to the trip driver during sync.

  • keep: active assignment already belongs to the trip driver.

  • An active assignment for another driver returns delivery_assigned_to_different_driver; the backend never reassigns it automatically.

This endpoint performs no writes. The final create/sync endpoint repeats the same validation. Frontend should show each error beside the matching delivery and submit only when every selected delivery is valid.

Path parameters

ParameterTypeDescription
trip_idRequiredstring

Variable used inside the request path.

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Content-Typestring

Example: application/json

Body parameters

ParameterTypeDescription
deliveriesarray<object>—
deliveries.delivery_idstring—
deliveries.sequencenumber—
Staging
Request
curl
1curl --globoff --request POST "https://staging-api.ondi.io/functions/v1/delivery/trips/${TRIP_ID}/deliveries/validate" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}" \3  --header "Content-Type: application/json" \4  --data-raw '{5  "deliveries": [6    {7      "delivery_id": "delivery-uuid-1",8      "sequence": 19    },10    {11      "delivery_id": "delivery-uuid-2",12      "sequence": 213    }14  ]15}'

Response examples

200OK
Response body
json
1{2  "success": true,3  "message": "Trip delivery validation completed",4  "data": {5    "results": [6      {7        "delivery_id": "delivery-uuid-1",8        "delivery_code": 1001,9        "valid": true,10        "assignment_action": "assign"11      },12      {13        "delivery_id": "delivery-uuid-2",14        "delivery_code": 1002,15        "valid": false,16        "error_key": "delivery_assigned_to_different_driver",17        "error_message": "Delivery is already assigned to a different driver"18      }19    ],20    "total_valid": 1,21    "total_failed": 122  }23}

Sync Trip Deliveries​

Delivery & Last-Mile / Trips
POST/delivery/trips/{{trip_id}}/deliveries

Realtime mobile contract

each newly attached unassigned delivery is immediately assigned to the Trip driver. The existing realtime assignment notification emits assignment_status: "assigned" and requires_driver_response: false; it is informational and never asks the driver to accept an already assigned Trip delivery. Use POST /delivery/trips/{trip_id}/remove-deliveries instead when removal must preserve order status and driver assignment.

Sync the full delivery membership of a trip. Only allowed while trip is planned.

This is a replace operation — the array sent becomes the exact set of deliveries on the trip. Delivery attachment, sequence changes, removals, assignment release, capacity release, and the batched deliveries_added, deliveries_removed, or deliveries_reordered trip events commit atomically. sequence is optional; if omitted, position index (1-based) is used. Send an empty array [] to remove all deliveries.

Assignment behavior

  • The trip must have a driver for a non-empty membership.

  • All incoming deliveries are validated before assignment or membership writes; failures are returned in request order.

  • Pending deliveries without active assignments are assigned through one shared preflight and the atomic batch-assignment transaction; existing validation results are reused instead of querying every assignment twice.

  • Newly assigned rows use status=assigned; route calculation is scheduled asynchronously after commit. One bulk realtime summary and one bulk driver notification represent the newly assigned rows, with assignment_status=assigned and requires_driver_response=false.

  • An existing active assignment is accepted only when it belongs to the same driver. A different-driver assignment is rejected and never automatically reassigned.

  • Removed members are unassigned, capacity is released, status returns to pending, and trip fields are cleared.

Eligibility rules for incoming deliveries

  • Internal delivery with status pending or assigned

  • Not deleted and not linked to a Waybill, route, route session, or another trip

  • Not a P&D parent; only the currently active internal child leg may be attached

Frontend integration contract

  • Use this sync endpoint for add/remove membership; send the complete desired replacement array.

  • Call the validate endpoint to display ordered per-delivery results before sync.

  • Ensure the trip has driver_id before submitting a non-empty array.

  • Replace local trip data with data from a successful response.

  • On HTTP 409 with code=trip_delivery_validation_failed, keep previous membership and show each data.results[].error_message against its delivery_id.

Path parameters

ParameterTypeDescription
trip_idRequiredstring

Variable used inside the request path.

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Content-Typestring

Example: application/json

Body parameters

ParameterTypeDescription
deliveriesarray<object>—
deliveries.delivery_idstring—
deliveries.sequencenumber—
Staging
Request
curl
1curl --globoff --request POST "https://staging-api.ondi.io/functions/v1/delivery/trips/${TRIP_ID}/deliveries" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}" \3  --header "Content-Type: application/json" \4  --data-raw '{5  "deliveries": [6    {7      "delivery_id": "delivery-uuid-1",8      "sequence": 19    },10    {11      "delivery_id": "delivery-uuid-3",12      "sequence": 213    },14    {15      "delivery_id": "delivery-uuid-4",16      "sequence": 317    }18  ]19}'

Response examples

200OK
Response body
json
1{2  "success": true,3  "message": "Trip deliveries synced successfully",4  "data": {5    "id": "trip-uuid",6    "summary": {7      "total": 38    }9  }10}

Remove Orders from Trip​

Delivery & Last-Mile / Trips
POST/delivery/trips/{{trip_id}}/remove-deliveries

Atomically removes one or more displayed orders from a trip. Requires bearer authentication plus manage:operations:tenant, manage:module:delivery, or system-admin access. Tenant scope comes from the authenticated request; Accept-Language controls the localized message.

  • Send one ID for single removal or multiple IDs for bulk removal.

  • Duplicate IDs are accepted once; the first occurrence defines response order.

Business rules

  • Every selected ID and its displayed membership in the source trip are validated before any write. A missing or stale member rolls back the whole request.

  • Every trip status and every order status are allowed.

  • For active, completed, or planned live membership, only trip_id and trip_sequence are cleared. Order status, active assignment, assigned driver, capacity, and order history remain unchanged, so the driver continues to receive the order through normal driver-order APIs.

  • The trip status and all unselected orders remain unchanged, including when the trip becomes empty. Maintained counts update transactionally; trip detail derives its displayed order list and stops from the remaining members.

  • For cancelled trips, only matching entries in historical_deliveries are removed. Current order membership and any newer trip or driver assignment are never changed. Existing audit history is preserved and one deliveries_removed trip event records this action.

Success returns HTTP 200 only after the RPC transaction commits. data.removed_delivery_ids contains the requested unique IDs in first-seen order, removed_count is its length, and summary.total is the remaining displayed order count.

Important errors all use { success:false, code, message, data:null }:

  • HTTP 400 delivery_ids_required or invalid_uuid.

  • HTTP 401/403 for bearer authentication or trip-management permission failure.

  • HTTP 404 trip_not_found or delivery_not_found.

  • HTTP 409 delivery_not_in_trip when membership changed.

  • HTTP 500 request_processing_failed after the database operation rolls back.

Frontend integration contract

call POST /functions/v1/delivery/trips/{trip_id}/remove-deliveries with the literal { "delivery_ids": [...] } body. Treat the response fields as required. Branch on code, display message, and make no follow-up mutation to order status or driver assignment. No navigation action is returned.

Path parameters

ParameterTypeDescription
trip_idRequiredstring

Variable used inside the request path.

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Content-Typestring

Example: application/json

Accept-Languagestring

Example: en

Body parameters

ParameterTypeDescription
delivery_idsRequiredarray<string>

is a required non-empty array of UUIDs.

Staging
Request
curl
1curl --globoff --request POST "https://staging-api.ondi.io/functions/v1/delivery/trips/${TRIP_ID}/remove-deliveries" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}" \3  --header "Content-Type: application/json" \4  --header "Accept-Language: en" \5  --data-raw '{6  "delivery_ids": [7    "11111111-1111-4111-8111-111111111111",8    "22222222-2222-4222-8222-222222222222"9  ]10}'

Response examples

200OK
Response body
json
1{2  "success": true,3  "message": "Orders removed from the trip successfully.",4  "data": {5    "id": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",6    "removed_delivery_ids": [7      "11111111-1111-4111-8111-111111111111"8    ],9    "removed_count": 1,10    "summary": {11      "total": 712    }13  }14}

Move Orders Between Trips​

Delivery & Last-Mile / Trips
POST/delivery/trips/{{source_trip_id}}/move-deliveries

Atomically moves one or more orders from a source trip to an existing destination trip and that trip's actual driver. Requires bearer authentication plus manage:operations:tenant, manage:module:delivery, or system-admin access. Tenant scope comes only from the authenticated request; Accept-Language selects the localized display message.

Business rules

  • One RPC locks both trip rows in stable order, then locks every selected order, P&D preparation resource, assignment, and affected driver capacity row. Every ID, current source membership, order status, destination state, and destination driver is rechecked before the first write. Any failure rolls back the whole selection.

  • delivered and cancelled orders are blocked. Other order statuses and the source trip's status are allowed. The destination must be a different planned or in_progress trip with an assigned driver.

  • The locked destination trip's actual driver must equal target_driver_id. A mismatch returns HTTP 409 target_trip_changed and changes nothing. No new driver/trip lookup API is required; use the existing GET /delivery/drivers and GET /delivery/trips selectors.

  • Orders retain their delivery status and prior history. Existing source and destination sequences are unchanged; moved orders are appended after the destination's current maximum sequence in their source order.

  • When drivers differ, the current assignment, custody, capacity, P&D handoff projection, and assignment-backed driver list move to the receiving driver. A different destination trip with the same driver changes only trip membership.

  • Both trip lifecycle statuses remain unchanged, including an empty source. Maintained counts update inside the transaction, and trip details derive their order lists and stops from the committed membership.

  • For a cancelled source, displayed membership comes from its historical snapshot. Selected snapshot entries are removed only if each current order remains outside any newer trip, route, route session, or live Waybill; a newer live assignment is never silently detached.

  • Both trips receive append-only move events containing actor, timestamp, trimmed reason, source/target trip IDs, immutable source/target trip-number snapshots, old/new driver IDs, order IDs, and source/target sequence values.

Success returns HTTP 200 only after commit. Every response field is required. moved_delivery_ids contains exactly the requested unique IDs in first-seen order; counts are the final displayed source and destination totals.

Important errors all use { success:false, code, message, data:null }:

  • HTTP 400 for missing fields, malformed UUIDs, an empty selection, or a reason outside 1–500 characters.

  • HTTP 401/403 for bearer authentication or trip-management permission failure.

  • HTTP 404 for a missing source trip, destination trip, order, or required driver record.

  • HTTP 409 for stale source membership, blocked order status, an unavailable/same destination, missing destination driver/vehicle assignment, capacity conflict, missing current order assignment, or target_trip_changed.

  • HTTP 500 request_processing_failed for a rolled-back unexpected server error.

Frontend integration contract

call POST /functions/v1/delivery/trips/{source_trip_id}/move-deliveries with the literal four-field body above. Treat every success field as required. Branch on stable code, display the localized message, and refresh selectors before reconfirming target_trip_changed. New move-history entries are available through trip detail with source_trip_number and target_trip_number; fall back to the corresponding IDs for older events. No navigation action is returned.

Path parameters

ParameterTypeDescription
source_trip_idRequiredstring

Variable used inside the request path.

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Content-Typestring

Example: application/json

Accept-Languagestring

Example: en

Body parameters

ParameterTypeDescription
delivery_idsRequiredarray<string>

non-empty UUID array. Single and bulk moves use the same endpoint. Duplicate IDs are accepted once and returned in first-seen request order.

target_trip_idRequiredstring

destination trip UUID; it must differ from the source.

target_driver_idRequiredstring

the destination driver UUID shown in the frontend confirmation.

reasonRequiredstring

trimmed, 1–500 characters.

Staging
Request
curl
1curl --globoff --request POST "https://staging-api.ondi.io/functions/v1/delivery/trips/${SOURCE_TRIP_ID}/move-deliveries" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}" \3  --header "Content-Type: application/json" \4  --header "Accept-Language: en" \5  --data-raw '{6  "delivery_ids": [7    "11111111-1111-4111-8111-111111111111",8    "22222222-2222-4222-8222-222222222222"9  ],10  "target_trip_id": "bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb",11  "target_driver_id": "dddddddd-dddd-4ddd-8ddd-dddddddddddd",12  "reason": "Correct trip assignment"13}'

Response examples

200OK
Response body
json
1{2  "success": true,3  "message": "Orders moved successfully.",4  "data": {5    "source_trip_id": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",6    "target_trip_id": "bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb",7    "moved_delivery_ids": [8      "11111111-1111-4111-8111-111111111111"9    ],10    "moved_count": 1,11    "source_summary": {12      "total": 713    },14    "target_summary": {15      "total": 316    }17  }18}

Remove Delivery from Trip​

Delivery & Last-Mile / Trips
DELETE/delivery/trips/{{trip_id}}/deliveries/{{delivery_id}}

Compatibility endpoint for removing one delivery from a trip. Requires bearer authentication plus manage:operations:tenant, manage:module:delivery, or system-admin access. It now uses the same atomic preservation workflow as POST /delivery/trips/{trip_id}/remove-deliveries: every trip and order status is allowed, only trip_id and trip_sequence are cleared, and the trip status, order status, active assignment, assigned driver, driver capacity, and histories remain unchanged. The response retains the existing trip-detail shape for compatibility. New dispatcher and tenant-admin flows should use the POST endpoint for its single/bulk contract. This endpoint does not delete the delivery.

Path parameters

ParameterTypeDescription
trip_idRequiredstring

Variable used inside the request path.

delivery_idRequiredstring

Variable used inside the request path.

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

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

Response examples

200OK
Response body
json
1{2  "success": true,3  "message": "Delivery removed from trip successfully",4  "data": {5    "id": "trip-uuid",6    "summary": {7      "total": 18    }9  }10}

Reorder Trip Deliveries​

Delivery & Last-Mile / Trips
PATCH/delivery/trips/{{trip_id}}/reorder

Reorder deliveries in the trip by providing all delivery IDs in the desired order.

  • Must include all current trip deliveries — no missing, no extra

  • trip_sequence will be reassigned as 1, 2, 3...

  • Only allowed while trip is planned

  • The returned driver_execution.stop_list uses the requested delivery order as a preference, but moves a grouped drop-off after every pickup required by the parcels inside it

  • Stop grouping uses only exact valid latitude/longitude coordinates; address IDs and text are ignored

Frontend integration contract

  • Render the returned data.driver_execution.stop_list, current_stop, and next_stop directly.

  • Do not assume each delivery row produces adjacent pickup and drop-off stops.

  • No navigation behavior changes.

Path parameters

ParameterTypeDescription
trip_idRequiredstring

Variable used inside the request path.

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Content-Typestring

Example: application/json

Body parameters

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

Response examples

200OK
Response body
json
1{2  "success": true,3  "message": "Trip stop order updated successfully",4  "data": {5    "id": "trip-uuid",6    "status": "planned"7  }8}

Save Trip Stop Order​

Delivery & Last-Mile / Trips
PATCH/delivery/trips/{{trip_id}}/stops/order

Saves the dispatcher-defined order of whole stops for a planned trip. Requires bearer authentication plus manage:operations:tenant, manage:module:delivery, or system-admin access. Send every stop_key from the latest trip detail exactly once and send that detail response's unchanged updated_at.

The database locks the trip, rechecks tenant ownership, planned status, timestamp, and the complete current stop set, then saves in one transaction. Every delivery's pickup must precede its drop-off. A pickup shared by every current order must be first; a drop-off shared by every current order must be last. Independent pickup/drop-off pairs may interleave. Invalid input is rejected without partial writes or silent rearrangement. A successful save appends one stops_reordered audit event and changes only stop_order, trip_events, and updated_at; trip status, delivery status, driver assignment, delivery membership, delivery sequence, and stop membership remain unchanged.

Trip detail for both dispatchers and drivers uses the saved order. Starting the trip preserves it. When membership or copied delivery addresses change, the trip timestamp advances; effective reads ignore obsolete keys, preserve surviving priorities, and insert new stops in a deterministic dependency-safe position. The older delivery reorder endpoint clears the custom stop order.

Important errors

  • HTTP 400 invalid_request: malformed trip ID/body/timestamp, duplicate or unknown keys, or an incomplete stop set.

  • HTTP 403 insufficient_permissions: caller cannot manage trips.

  • HTTP 404 trip_not_found: the tenant-scoped trip does not exist.

  • HTTP 409 trip_changed: expected_updated_at is stale.

  • HTTP 409 trip_not_reorderable: the trip is no longer planned.

  • HTTP 422 invalid_stop_order: pickup/drop-off dependency or shared-boundary rule failed.

  • HTTP 500 save_failed: the transaction rolled back because of an unexpected server error.

Frontend integration contract

drag locally and call this endpoint only on Apply. On HTTP 200, replace the Stops tab with data.driver_execution and store data.updated_at. On trip_changed, keep the draft but require Reset to latest before another Apply. For all failures, branch on code and display the localized message. No navigation action is returned.

Path parameters

ParameterTypeDescription
trip_idRequiredstring

Variable used inside the request path.

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Content-Typestring

Example: application/json

Body parameters

ParameterTypeDescription
ordered_stop_keysarray<string>—
expected_updated_atstring—
Staging
Request
curl
1curl --globoff --request PATCH "https://staging-api.ondi.io/functions/v1/delivery/trips/${TRIP_ID}/stops/order" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}" \3  --header "Content-Type: application/json" \4  --data-raw '{5  "ordered_stop_keys": [6    "pickup:36.19,44.01",7    "dropoff:36.2,44.02",8    "pickup:36.21,44.03",9    "dropoff:36.22,44.04"10  ],11  "expected_updated_at": "2026-09-10T10:00:00.000Z"12}'

Response examples

200OK
Response body
json
1{2  "success": true,3  "message": "Stop order updated.",4  "data": {5    "trip_id": "source-trip-uuid",6    "status": "planned",7    "updated_at": "2026-09-10T10:05:00.000Z",8    "driver_execution": {9      "stop_list": [10        {11          "stop_key": "pickup:36.19,44.01",12          "stop_type": "pickup",13          "location_label": null,14          "location": {15            "latitude": 36.19,16            "longitude": 44.0117          },18          "deliveries": [19            {20              "delivery_id": "order-uuid-1",21              "delivery_code": "3341A",22              "barcode": "DEL3341A",23              "status": "assigned",24              "trip_sequence": 1,25              "assignment_status": "assigned",26              "next_action": "start_delivery",27              "bulk_eligible": true28            }29          ],30          "is_complete": false31        }32      ],33      "current_stop": {34        "stop_key": "pickup:36.19,44.01",35        "stop_type": "pickup",36        "location_label": null,37        "location": {38          "latitude": 36.19,39          "longitude": 44.0140        },41        "deliveries": [42          {43            "delivery_id": "order-uuid-1",44            "delivery_code": "3341A",45            "barcode": "DEL3341A",46            "status": "assigned",47            "trip_sequence": 1,48            "assignment_status": "assigned",49            "next_action": "start_delivery",50            "bulk_eligible": true51          }52        ],53        "is_complete": false54      },55      "next_stop": null56    }57  }58}

Cancel Trip​

Delivery & Last-Mile / Trips
POST/delivery/trips/{{trip_id}}/cancel

Cancels a planned or in_progress trip only when every current delivery is still pending, assigned, or en_route_to_pickup. Any picked-up, in-transit, delivered, cancelled, returned, exception, or otherwise non-cancellable member blocks the whole action. Accepts optional reason and optional notes (each max 2000 characters) and requires trip-management permission.

The operation is atomic

the trip becomes cancelled; status history is appended; a trip_cancelled actor event is appended; the ordered delivery snapshot is stored on the trip; active assignments become unassigned; driver capacity is released; deliveries return to pending; and live trip fields/stops are cleared. Repeating cancellation is idempotent and does not append another event or notification. On a real cancellation, the assigned driver receives one critical in-app modal requiring acknowledgement plus push notification; the notification tells the driver not to continue and links to the trip.

Frontend integration contract

after success, load GET /delivery/trips/{id}. Render the cancellation story from data.trip_events; render data.deliveries as read-only when data.deliveries_mode=historical. current_stop, next_stop, and stop_list remain empty.

Path parameters

ParameterTypeDescription
trip_idRequiredstring

Variable used inside the request path.

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Content-Typestring

Example: application/json

Body parameters

ParameterTypeDescription
reasonstring—
notesstring—
Staging
Request
curl
1curl --globoff --request POST "https://staging-api.ondi.io/functions/v1/delivery/trips/${TRIP_ID}/cancel" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}" \3  --header "Content-Type: application/json" \4  --data-raw '{5  "reason": "Trip plan cancelled before pickup",6  "notes": "Vehicle issue reported before pickup"7}'

Response examples

200OK
Response body
json
1{2  "success": true,3  "message": "Trip cancelled successfully",4  "data": {5    "id": "trip-uuid",6    "previous_status": "in_progress",7    "status": "cancelled",8    "changed": true,9    "started_at": "2026-07-29T12:30:00Z",10    "completed_at": null,11    "released_delivery_count": 3,12    "released_assignment_count": 3,13    "available_actions": []14  }15}

Driver: List My Trips​

Delivery & Last-Mile / Trips
GET/delivery/driver-trips

Driver-facing

list trips assigned to the authenticated driver.

Driver sees trip_number, status, item_count, and timestamps. They can then call GET /delivery/trips/:id to get the full stop list for execution. The response is bounded to the requested page (maximum 100); item_count and the all-status summary are database-maintained/aggregated and do not load every matching row into the API runtime.

Query parameters

ParameterTypeDescription
pagestring

Page number

Example: 1

limitstring

Items per page (max 100)

Example: 20

statusstring

Filter by status

Example: in_progress

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

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

Response examples

200OK
Response body
json
1{2  "success": true,3  "message": "Trips retrieved successfully",4  "data": {5    "trips": [6      {7        "id": "trip-uuid",8        "trip_number": "TRIP-00001",9        "status": "in_progress",10        "trip_type": "multi_pickup",11        "item_count": 3,12        "started_at": "2026-04-11T08:00:00Z",13        "completed_at": null14      }15    ],16    "total": 1,17    "page": 1,18    "limit": 2019  }20}

Get Trip Partial Returns​

Delivery & Last-Mile / Trips
GET/delivery/trips/{{trip_id}}/returns

Convenience endpoint — returns only the deliveries in this trip that have one or more return requests.

Useful for a focused 'returns' tab on the trip detail screen without loading the full trip payload.

Each item includes

  • delivery_id, delivery_code, barcode, delivery_status, return_status, trip_sequence

  • return_requests array with id, status, return_reason, return_delivery_id, requested_at, notes

Note

return requests are created via the existing POST /delivery/returns endpoint — no new endpoint needed for creation.

Path parameters

ParameterTypeDescription
trip_idRequiredstring

Variable used inside the request path.

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

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

Response examples

200OK
Response body
json
1{2  "success": true,3  "message": "Trip returns retrieved successfully",4  "data": [5    {6      "delivery_id": "uuid-d2",7      "delivery_code": 1002,8      "barcode": "BC002",9      "delivery_status": "returned",10      "return_status": "return_approved",11      "trip_sequence": 2,12      "return_requests": [13        {14          "id": "rr-uuid",15          "status": "approved",16          "return_reason": "Customer refused delivery",17          "return_delivery_id": "uuid-return-delivery",18          "requested_at": "2026-04-11T10:30:00Z",19          "notes": null20        }21      ]22    },23    {24      "delivery_id": "uuid-d5",25      "delivery_code": 1005,26      "barcode": "BC005",27      "delivery_status": "returned",28      "return_status": "return_requested",29      "trip_sequence": 5,30      "return_requests": [31        {32          "id": "rr-uuid-2",33          "status": "pending",34          "return_reason": "Wrong address",35          "return_delivery_id": null,36          "requested_at": "2026-04-11T11:00:00Z",37          "notes": null38        }39      ]40    }41  ]42}

Driver: Execute Trip Stop Bulk Action​

Delivery & Last-Mile / Trips
POST/delivery/trips/{{trip_id}}/stops/bulk-action

Driver-facing group action for selected deliveries at one derived trip stop. The authenticated user must have the driver role, must be online/active/available, and must be the trip's assigned driver. Send the exact stop_key returned by GET trip detail plus 1 to 200 unique delivery UUIDs. Every ID is revalidated against tenant, trip, stop, driver assignment, current action, exceptions, returns, terminal state, carrier, and active Waybill rules; client selection is never trusted as authorization.

Actions

  • start_delivery: pickup stop; assigned or accepted assignment becomes en_route_to_pickup, using the same canonical transition and side effects as the individual sheet.

  • pickup: pickup stop; proofless single-parcel deliveries become picked_up. A delivery needing pickup proof or parcel review returns outcome=individual_action_required without mutation.

  • in_transit: drop-off stop; picked_up becomes assignment en_route_to_delivery and delivery in_transit.

  • complete: drop-off stop; every valid delivery returns outcome=individual_action_required without mutation so the app opens that order's existing completion/proof screen. No proof fields are accepted by this endpoint and proof is never shared.

The valid request returns HTTP 200 even when individual orders fail. data.results remains in trip sequence order and each row contains delivery ID/code, outcome (applied, individual_action_required, or failed), localized message, machine code when relevant, and authoritative status fields. Successful orders are not rolled back when another order fails. Fixed validation reads are batched. Eligible pickups are sent through one service-role database RPC; that RPC invokes the canonical guarded transition in request order and isolates expected per-delivery business failures. Unexpected database faults abort the outer transaction. Already-paid deliveries cause no payment write, while unpaid zero-fee deliveries use one set-based status update; positive sender-cash fees keep the existing idempotent COD/payment owner. Other actions retain bounded concurrency 10. Trip navigation does not fan out external route-calculation requests per order.

Important request errors

  • HTTP 400: missing stop key, invalid action, invalid/duplicate IDs, more than 200 IDs, or invalid UUID.

  • HTTP 403: not a driver, trip belongs to another driver, or driver is offline/inactive.

  • HTTP 404: trip or driver not found.

  • HTTP 409: trip is no longer planned/in progress.

Frontend integration contract

  • Use data.driver_execution.stop_list[].stop_key. Show selection only for deliveries whose literal next_action matches the button action.

  • Send one common action and selected delivery IDs. The button text includes the selection count, for example Start 7 deliveries.

  • Apply outcome=applied, display every failed delivery by delivery_code, and continue processing the remaining rows.

  • For individual_action_required, open each delivery's existing pickup or completion screen sequentially in result order. Refresh the trip once after the group/queue finishes.

  • Keep individual-card actions and per-order exception actions available. Branch on codes, never localized message text. No navigation action is returned by the backend.

Event actor contract

new driver-performed status events use the validated assigned drivers.id as actor_id, including grouped pickup. The authenticated user ID remains available to backend authorization and custody logic; clients do not send an actor ID.

Path parameters

ParameterTypeDescription
trip_idRequiredstring

Variable used inside the request path.

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Content-Typestring

Example: application/json

Body parameters

ParameterTypeDescription
stop_keystring—
actionstring—
delivery_idsarray<string>—
Staging
Request
curl
1curl --globoff --request POST "https://staging-api.ondi.io/functions/v1/delivery/trips/${TRIP_ID}/stops/bulk-action" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}" \3  --header "Content-Type: application/json" \4  --data-raw '{5  "stop_key": "pickup:33.3,44.4",6  "action": "start_delivery",7  "delivery_ids": [8    "00000000-0000-4000-8000-000000000001",9    "00000000-0000-4000-8000-000000000002"10  ]11}'

Response examples

200OK
Response body
json
1{2  "success": true,3  "message": "Trip stop action completed",4  "data": {5    "trip_id": "trip-uuid",6    "stop_key": "dropoff:33.3,44.4",7    "action": "complete",8    "results": [9      {10        "delivery_id": "00000000-0000-4000-8000-000000000001",11        "delivery_code": "ORD-1",12        "outcome": "individual_action_required",13        "success": true,14        "previous_assignment_status": "en_route_to_delivery",15        "assignment_status": "en_route_to_delivery",16        "delivery_status": "in_transit",17        "code": "trip_stop_individual_action_required",18        "message": "Continue with this delivery individually to review parcels or add proof"19      },20      {21        "delivery_id": "00000000-0000-4000-8000-000000000002",22        "delivery_code": "ORD-2",23        "outcome": "failed",24        "success": false,25        "previous_assignment_status": "en_route_to_delivery",26        "assignment_status": "en_route_to_delivery",27        "delivery_status": "in_transit",28        "code": "delivery_has_open_exception",29        "message": "Resolve the active delivery exception before continuing"30      }31    ],32    "requested_count": 2,33    "applied_count": 0,34    "individual_action_required_count": 1,35    "failed_count": 136  }37}

Driver: Trip Status Summary​

Delivery & Last-Mile / Trips
GET/delivery/driver-trips/status-summary

Driver-facing

returns database-aggregated counts per trip status for the authenticated driver without loading individual trip rows into the API runtime.

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

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

Response examples

200OK
Response body
json
1{2  "success": true,3  "message": "Trip status summary retrieved successfully",4  "data": {5    "planned": 2,6    "in_progress": 1,7    "completed": 15,8    "cancelled": 09  }10}