Skip to main content

Deliveries

This section contains 1 subgroups.

Create Delivery​

External & Carrier Integrations / Deliveries
POST/external/deliveries

Create a normal domestic delivery using pickup and delivery address objects. The endpoint accepts coordinates and address text in the request. On success, it reloads the created row using the same delivery_list_view-backed detail shape used by external get-delivery, then returns it under delivery.

Success response

  • 201 with { success, error, message, currency_code, delivery }.

  • delivery is detail-shaped like GET /external/deliveries/{id}.

  • delivery includes pickup_location, delivery_location, pickup_warehouse_id, dropoff_warehouse_id, pickup_warehouse, dropoff_warehouse, delivery_services, package_details, unit_size, cod_amount, notes, and special_instructions when available.

  • delivery.id remains delivery_code for external create compatibility.

Important errors

  • 400 validation failure, delivery_service_not_found, external_reference_id_already_exists, unit_size_or_dimensions_required, price/zone errors.

  • 500 internal_server_error.

External delivery response enrichment

  • Top-level response includes currency_code. Each delivery object also includes currency_code.

  • delivery.track_order is the public tracking page URL built as {track_url}/tracking?code={delivery_code}; track_url resolves from tenant track_orders.track_url, then the WEB_URL secret, then system external_links.delivery_customer_portal_url, then https://staging-app.ondi.io.

  • delivery.customer_receipt_url is a signed receipt URL when a successful delivery payment receipt exists; otherwise null.

  • delivery.status_updated_at is derived from the latest status_updates[].timestamp, falling back to updated_at.

  • delivery.delivery_services includes description and image_url when available.

  • delivery.proofs is grouped as { pickup: [], delivery: [] } using OnDi proof records.

  • delivery.proof_of_pickup and delivery.proof_of_delivery are aggregate proof summaries when captured, shaped as { signature, photos }.

  • delivery.failure_reason is populated from delivery_failure_reason when present.

  • Top-level delivery_type is not returned; use delivery.delivery_services.delivery_type.

  • Response payment_method is documented as wallet | cash.

Headers

ParameterTypeDescription
X-API-Keystring

Example: {{api_key}}

Content-Typestring

Example: application/json

Body parameters

ParameterTypeDescription
pickup_addressRequiredobject

Pickup address payload.

pickup_address.latRequirednumber

Pickup latitude.

pickup_address.lngRequirednumber

Pickup longitude.

pickup_address.full_addressRequiredstring

Full pickup address text.

pickup_address.cityOptionalstring

City name.

pickup_address.stateOptionalstring

State or province.

pickup_address.countryOptionalstring

Country name or code.

pickup_address.postal_codeOptionalstring

Postal or ZIP code.

pickup_address.address_line1Optionalstring

Address line 1.

pickup_address.address_line2Optionalstring

Address line 2.

pickup_address.contact_infoOptionalobject

Pickup contact details.

pickup_address.contact_info.full_nameOptionalstring

Contact full name.

Show 40 more body fields

Additional body parameters

ParameterTypeDescription
pickup_address.contact_info.phoneOptionalstring

Contact phone number.

pickup_address.contact_info.emailOptionalstring

Contact email address.

delivery_addressRequiredobject

Delivery address payload.

delivery_address.latRequirednumber

Delivery latitude.

delivery_address.lngRequirednumber

Delivery longitude.

delivery_address.full_addressRequiredstring

Full delivery address text.

delivery_address.cityOptionalstring

City name.

delivery_address.stateOptionalstring

State or province.

delivery_address.countryOptionalstring

Country name or code.

delivery_address.postal_codeOptionalstring

Postal or ZIP code.

delivery_address.address_line1Optionalstring

Address line 1.

delivery_address.address_line2Optionalstring

Address line 2.

delivery_address.contact_infoOptionalobject

Receiver contact details.

delivery_address.contact_info.full_nameOptionalstring

Contact full name.

delivery_address.contact_info.phoneOptionalstring

Contact phone number.

delivery_address.contact_info.emailOptionalstring

Contact email address.

customer_idstring

Required for non-customer callers. Auto-filled from auth for customer/no-role callers.

delivery_service_idRequiredstring

Delivery service UUID.

payer_typeRequiredstring

Supported values: sender, receiver.

payment_methodRequiredstring

Accepted values: wallet, cash, cod. cod is normalized to cash.

external_reference_idRequiredstring

External reference. When delivery_external_reference.unique_per_customer=true (default), it must be unique among root orders for the same tenant customer; when false, duplicates are allowed.

cod_amountOptionalnumber

Minimum 0.

notesOptionalstring

General delivery notes.

special_instructionsOptionalstring

Top-level delivery handling instructions.

scheduled_pickup_timeOptionalstring

ISO 8601 datetime for planned pickup.

scheduled_delivery_timeOptionalstring

ISO 8601 datetime for planned delivery.

receiver_user_idOptionalstring

Receiver user UUID.

unit_sizeOptionalnumber

Positive unit size. Measured package items take priority; when neither is available, general.unit_size.settings.missing_unit_size_fallback is applied.

package_detailsOptionalobject

Used to compute unit size from measured items when supplied.

package_details.special_instructionsOptionalstring

Parcel-specific handling notes.

package_details.itemsOptionalarray<object>

Measured parcel items used for unit-size calculation.

package_details.items[].nameOptionalstring

Item name.

package_details.items[].descriptionOptionalstring

Item description.

package_details.items[].quantityOptionalnumber

Quantity for unit-size calculation. Defaults to 1 when omitted.

package_details.items[].weightOptionalnumber

Item weight.

package_details.items[].lengthOptionalnumber

Item length.

package_details.items[].widthOptionalnumber

Item width.

package_details.items[].heightOptionalnumber

Item height.

tracking_providerOptionalstring

Tracking provider identifier.

is_draftOptionalboolean

When true, delivery is created with status draft; otherwise it follows normal creation flow.

Staging
Request
curl
1curl --globoff --request POST "https://staging-api.ondi.io/functions/v1/external/deliveries" \2  --header "X-API-Key: ${ONDI_TENANT_API_KEY}" \3  --header "Content-Type: application/json" \4  --data-raw '{5  "pickup_address": {6    "lat": {{pickup_lat}}, // required7    "lng": {{pickup_lng}},8    "full_address": "{{pickup_full_address}}",9    "city": "{{pickup_city}}",10    "state": "{{pickup_state}}",11    "country": "{{pickup_country}}",12    "postal_code": "{{pickup_postal_code}}",13    "contact_info": {14      "full_name": "{{sender_name}}",15      "phone": "{{sender_phone}}",16      "email": "{{sender_email}}"17    }18  },19  "delivery_address": {20    "lat": {{delivery_lat}},21    "lng": {{delivery_lng}},22    "full_address": "{{delivery_full_address}}",23    "city": "{{delivery_city}}",24    "state": "{{delivery_state}}",25    "country": "{{delivery_country}}",26    "postal_code": "{{delivery_postal_code}}",27    "contact_info": {28      "full_name": "{{receiver_name}}",29      "phone": "{{receiver_phone}}",30      "email": "{{receiver_email}}"31    }32  },33  "customer_id": "{{customer_id}}",34  "delivery_service_id": "{{delivery_service_id}}",35  "payer_type": "sender",36  "payment_method": "wallet",37  "cod_amount": 0,38  "notes": "Package delivery",39  "external_reference_id": "EXT-REF-123",40  "is_draft": true,41  "scheduled_pickup_time": "2024-01-15T10:00:00Z",42  "scheduled_delivery_time": "2024-01-15T14:00:00Z",43  "receiver_user_id": "{{receiver_user_id}}",44  "unit_size": 1,45  "tracking_provider": "internal",46  "package_details": {47    "special_instructions": "Handle with care",48    "items": [49      {50        "name": "{{item_name}}",51        "description": "{{item_description}}",52        "quantity": 1,53        "weight": 2.5,54        "length": 30,55        "width": 20,56        "height": 1557      }58    ]59  },60  "special_instructions": "Handle with care"61}'

Replace {{placeholders}} with your own values before sending.

Response examples

201Created
Response body
json
1{2  "success": true,3  "error": null,4  "message": "Delivery created successfully",5  "delivery": {6    "id": "DEL-000001",7    "tenant_id": "tenant-uuid",8    "external_reference_id": "EXT-REF-123",9    "parent_delivery_id": null,10    "assignment_id": null,11    "requires_warehouse_staging": false,12    "staging_warehouse_id": null,13    "staging_warehouse": null,14    "pickup_location": {15      "lat": 40.7128,16      "lng": -74.006,17      "latitude": 40.7128,18      "longitude": -74.006,19      "full_address": "123 Main St, New York, NY 10001",20      "city": "New York",21      "state": "NY",22      "country": "US",23      "postal_code": "10001",24      "zone_id": "pickup-zone-uuid",25      "zone_name": "Downtown Zone",26      "zone_code": "DT",27      "contact_info": {28        "full_name": "John Sender",29        "phone": "+1-555-0100",30        "email": "[email protected]",31        "zone_name": "Downtown Zone",32        "zone_code": "DT"33      }34    },35    "delivery_location": {36      "lat": 40.7589,37      "lng": -73.9851,38      "latitude": 40.7589,39      "longitude": -73.9851,40      "full_address": "456 Broadway, New York, NY 10013",41      "city": "New York",42      "state": "NY",43      "country": "US",44      "postal_code": "10013",45      "zone_id": "delivery-zone-uuid",46      "zone_name": "Midtown Zone",47      "zone_code": "MT",48      "contact_info": {49        "full_name": "Jane Receiver",50        "phone": "+1-555-0200",51        "email": "[email protected]",52        "zone_name": "Midtown Zone",53        "zone_code": "MT"54      }55    },56    "pickup_warehouse_id": null,57    "dropoff_warehouse_id": null,58    "pickup_warehouse": null,59    "dropoff_warehouse": null,60    "assignment": null,61    "cod": null,62    "delivery_services": {63      "id": "service-uuid",64      "name": "Standard Delivery",65      "description": "Standard delivery",66      "image_url": "https://cdn.example.com/services/standard.png",67      "delivery_type": {68        "id": "delivery-type-uuid",69        "name": "Delivery"70      }71    },72    "customer": {73      "id": "customer-uuid",74      "full_name": "Customer Name",75      "avatar_url": null,76      "phone": "+1-555-0000"77    },78    "driver": {79      "id": null,80      "full_name": null,81      "avatar_url": null,82      "phone": null,83      "latitude": null,84      "longitude": null85    },86    "vehicle": null,87    "payment_status": "pending",88    "payer_type": "sender",89    "cod_amount": 0,90    "delivery_fee": 15.5,91    "payment_method": "wallet",92    "status": "draft",93    "open_exception": null,94    "has_open_exception": false,95    "sequence_plan": null,96    "carrier_type": "internal",97    "is_parent": false,98    "direction": null,99    "pickup_zone_id": "pickup-zone-uuid",100    "dropoff_zone_id": "delivery-zone-uuid",101    "unit_size": 1,102    "notes": "Package delivery",103    "special_instructions": "Handle with care",104    "package_details": {105      "special_instructions": "Handle with care",106      "items": [107        {108          "name": "Item 1",109          "description": "Widget",110          "quantity": 1,111          "weight": 2.5,112          "length": 30,113          "width": 20,114          "height": 15115        }116      ]117    },118    "quantity": 1,119    "delivery_code": "DEL-000001",120    "barcode": "DEL-000001",121    "track_order": "https://staging-app.ondi.io/tracking?code=DEL-000001",122    "customer_receipt_url": null,123    "status_updated_at": "2024-01-15T08:30:00Z",124    "proofs": {125      "pickup": [],126      "delivery": []127    },128    "shipment_label_url": null,129    "shipment_label_status": null,130    "status_updates": null,131    "created_at": "2024-01-15T08:00:00Z",132    "updated_at": "2024-01-15T08:00:00Z",133    "delivery_flow": null134  }135}

List Deliveries​

External & Carrier Integrations / Deliveries
GET/external/deliveries

List normal deliveries visible to the authenticated external caller. External customers are always restricted to deliveries whose customer ID matches the authenticated user; a supplied customer_id cannot override this ownership scope. The default list includes draft and other active deliveries. Delivered deliveries remain excluded by default unless requested with status=delivered or is_completed=true.

Tenant scope comes from the authenticated context, with tenant_id query fallback where supported.

Success response

{ success, error, message, currency_code, deliveries, page, limit, total }. Each delivery object also includes currency_code for money fields such as delivery_fee and cod_amount. Deliveries are loaded from delivery_list_view, then enriched for the external contract.

Frontend integration contract

read snake_case response fields only. Use delivery.delivery_services.delivery_type for delivery type, delivery.track_order for the public tracking page, delivery.customer_receipt_url when non-null, and delivery.status_updated_at for the last status change timestamp.

External delivery response enrichment

  • Top-level response includes currency_code. Each delivery object also includes currency_code.

  • delivery.track_order is the public tracking page URL built as {track_url}/tracking?code={delivery_code}; track_url resolves from tenant track_orders.track_url, then WEB_URL secret, then system external_links.delivery_customer_portal_url, then https://staging-app.ondi.io.

  • delivery.customer_receipt_url is a signed receipt URL when a successful delivery payment receipt exists; otherwise null.

  • delivery.status_updated_at is derived from the latest status_updates[].timestamp, falling back to updated_at.

  • delivery.delivery_services includes description and image_url when available.

  • delivery.proofs is grouped as { pickup: [], delivery: [] } using OnDi proof records.

  • delivery.proof_of_pickup and delivery.proof_of_delivery are aggregate proof summaries when captured, shaped as { signature, photos }.

  • delivery.failure_reason is populated from delivery_failure_reason when present.

  • Top-level delivery_type is not returned; use delivery.delivery_services.delivery_type.

  • payment_method is documented as wallet|cash.

Query parameters

ParameterTypeDescription
pageOptionalstring

Page number for pagination (Optional, default: 1)

Example: 1

limitOptionalstring

Number of deliveries per page (Optional, default: 10)

Example: 10

statusOptionalstring

Filter by delivery status, including draft, pending, assigned, picked_up, delivered, or cancelled (Optional)

customer_idOptionalstring

Filter by customer ID (Optional)

searchOptionalstring

Search in delivery details (Optional)

driver_idOptionalstring

Filter by driver ID (Optional)

zone_idsOptionalstring

Filter by zone IDs (comma-separated) (Optional)

payer_typeOptionalstring

Filter by payer type (sender/receiver) (Optional)

is_completedOptionalstring

Filter by completion status (true/false) (Optional)

payment_methodOptionalstring

Filter by payment method (wallet/cod/card) (Optional)

payment_statusOptionalstring

Filter by payment status (Optional)

delivery_service_idOptionalstring

Filter by delivery service ID (Optional)

start_dateOptionalstring

Filter by start date (ISO format) (Optional)

end_dateOptionalstring

Filter by end date (ISO format) (Optional)

assignment_statusOptionalstring

Filter by assignment status (Optional)

Headers

ParameterTypeDescription
X-API-Keystring

Example: {{api_key}}

Staging
Request
curl
1curl --globoff --request GET "https://staging-api.ondi.io/functions/v1/external/deliveries?page=1&limit=10" \2  --header "X-API-Key: ${ONDI_TENANT_API_KEY}"

Response examples

200OK
Response body
json
1{2  "success": true,3  "error": null,4  "message": "Deliveries retrieved successfully",5  "currency_code": "IQD",6  "deliveries": [7    {8      "id": "DEL-000001",9      "status": "pending",10      "tenant_id": "tenant-uuid",11      "external_reference_id": "EXT-REF-123",12      "customer_id": "customer-uuid",13      "delivery_service_id": "service-uuid",14      "payer_type": "sender",15      "payment_method": "wallet",16      "delivery_fee": 15.5,17      "cod_amount": 0,18      "currency_code": "IQD",19      "notes": "Package delivery",20      "unit_size": 1,21      "tracking_provider": "internal",22      "carrier_type": "internal",23      "pickup_warehouse_id": null,24      "dropoff_warehouse_id": null,25      "pickup_location_id": "address-uuid-1",26      "delivery_location_id": "address-uuid-2",27      "receiver_user_id": null,28      "assigned_carrier_id": null,29      "assigned_to_user_id": null,30      "package_details": {31        "special_instructions": "Handle with care",32        "items": [33          {34            "name": "Item 1",35            "description": "Widget",36            "quantity": 1,37            "weight": 2.538          }39        ]40      },41      "scheduled_pickup_time": "2024-01-15T10:00:00Z",42      "scheduled_delivery_time": "2024-01-15T14:00:00Z",43      "started_at": null,44      "picked_up_at": null,45      "delivered_at": null,46      "cancelled_at": null,47      "delivery_failure_reason": null,48      "created_at": "2024-01-15T08:00:00Z",49      "updated_at": "2024-01-15T08:00:00Z",50      "barcode": "DEL-000001",51      "pickup_location": {52        "id": "address-uuid-1",53        "full_address": "123 Main St, New York, NY 10001",54        "latitude": 40.7128,55        "longitude": -74.006,56        "city": "New York",57        "state": "NY",58        "country": "US",59        "postal_code": "10001",60        "contact_info": {61          "full_name": "John Sender",62          "phone": "+1-555-0100",63          "email": "[email protected]"64        }65      },66      "delivery_location": {67        "id": "address-uuid-2",68        "full_address": "456 Broadway, New York, NY 10013",69        "latitude": 40.7589,70        "longitude": -73.9851,71        "city": "New York",72        "state": "NY",73        "country": "US",74        "postal_code": "10013",75        "contact_info": {76          "full_name": "Jane Receiver",77          "phone": "+1-555-0200",78          "email": "[email protected]"79        }80      },81      "delivery_code": "DEL-000001"82    }83  ],84  "page": 1,85  "limit": 10,86  "total": 2587}

Get Delivery​

External & Carrier Integrations / Deliveries
GET/external/deliveries/{{delivery_code}}

Retrieve one normal delivery by public delivery code, UUID, or barcode. External customers can retrieve only their own delivery; another customer’s delivery returns 404.

  • The path value is the public delivery code by default. UUIDs still work for legacy callers.

Tenant scope comes from the authenticated context, with tenant_id query fallback where supported.

Success response

{ success, error, message, currency_code, delivery }. The delivery object also includes currency_code for money fields such as delivery_fee and cod_amount. Delivery is loaded from delivery_list_view, then enriched for the external contract.

Responses

200 success, 404 delivery_not_found, 500 internal_server_error for a database/query failure.

Frontend integration contract

read snake_case response fields only. Use delivery.delivery_services.delivery_type for delivery type, delivery.track_order for the public tracking page, delivery.customer_receipt_url when non-null, and delivery.status_updated_at for the last status change timestamp.

External delivery response enrichment

  • Top-level response includes currency_code. Each delivery object also includes currency_code.

  • delivery.id is the public delivery code and matches delivery.delivery_code.

  • delivery.track_order is the public tracking page URL built as {track_url}/tracking?code={delivery_code}; track_url resolves from tenant track_orders.track_url, then WEB_URL secret, then system external_links.delivery_customer_portal_url, then https://staging-app.ondi.io.

  • delivery.customer_receipt_url is a signed receipt URL when a successful delivery payment receipt exists; otherwise null.

  • delivery.status_updated_at is derived from the latest status_updates[].timestamp, falling back to updated_at.

  • delivery.delivery_services includes description and image_url when available.

  • delivery.proofs is grouped as { pickup: [], delivery: [] } using OnDi proof records.

  • delivery.proof_of_pickup and delivery.proof_of_delivery are aggregate proof summaries when captured, shaped as { signature, photos }.

  • delivery.failure_reason is populated from delivery_failure_reason when present.

  • Top-level delivery_type is not returned; use delivery.delivery_services.delivery_type.

  • payment_method is documented as wallet|cash.

Path parameters

ParameterTypeDescription
delivery_codeRequiredstring

Variable used inside the request path.

Query parameters

ParameterTypeDescription
filter_typeOptionalstring

Optional override: id, delivery_code, or barcode. Without it, UUID paths use id and all other paths use delivery_code. id, delivery_code, or barcode. Defaults to delivery_code for non-UUID paths and id for UUID paths.

Example: {{filter_type}}

Headers

ParameterTypeDescription
X-API-Keystring

Example: {{api_key}}

Staging
Request
curl
1curl --globoff --request GET "https://staging-api.ondi.io/functions/v1/external/deliveries/${DELIVERY_CODE}" \2  --header "X-API-Key: ${ONDI_TENANT_API_KEY}"

Response examples

200OK
Response body
json
1{2  "success": true,3  "error": null,4  "message": "Delivery retrieved successfully",5  "currency_code": "IQD",6  "delivery": {7    "id": "DEL-000001",8    "status": "pending",9    "tenant_id": "tenant-uuid",10    "external_reference_id": "EXT-REF-123",11    "customer_id": "customer-uuid",12    "delivery_service_id": "service-uuid",13    "payer_type": "sender",14    "payment_method": "wallet",15    "delivery_fee": 15.5,16    "cod_amount": 0,17    "currency_code": "IQD",18    "notes": "Package delivery",19    "unit_size": 1,20    "tracking_provider": "internal",21    "carrier_type": "internal",22    "pickup_warehouse_id": null,23    "dropoff_warehouse_id": null,24    "pickup_location_id": "address-uuid-1",25    "delivery_location_id": "address-uuid-2",26    "receiver_user_id": null,27    "assigned_carrier_id": null,28    "assigned_to_user_id": null,29    "package_details": {30      "special_instructions": "Handle with care",31      "items": [32        {33          "name": "Item 1",34          "description": "Widget",35          "quantity": 1,36          "weight": 2.537        }38      ]39    },40    "scheduled_pickup_time": "2024-01-15T10:00:00Z",41    "scheduled_delivery_time": "2024-01-15T14:00:00Z",42    "started_at": null,43    "picked_up_at": null,44    "delivered_at": null,45    "cancelled_at": null,46    "delivery_failure_reason": null,47    "created_at": "2024-01-15T08:00:00Z",48    "updated_at": "2024-01-15T08:00:00Z",49    "barcode": "DEL-000001",50    "pickup_location": {51      "id": "address-uuid-1",52      "full_address": "123 Main St, New York, NY 10001",53      "latitude": 40.7128,54      "longitude": -74.006,55      "city": "New York",56      "state": "NY",57      "country": "US",58      "postal_code": "10001",59      "contact_info": {60        "full_name": "John Sender",61        "phone": "+1-555-0100",62        "email": "[email protected]"63      }64    },65    "delivery_location": {66      "id": "address-uuid-2",67      "full_address": "456 Broadway, New York, NY 10013",68      "latitude": 40.7589,69      "longitude": -73.9851,70      "city": "New York",71      "state": "NY",72      "country": "US",73      "postal_code": "10013",74      "contact_info": {75        "full_name": "Jane Receiver",76        "phone": "+1-555-0200",77        "email": "[email protected]"78      }79    },80    "delivery_code": "DEL-000001"81  }82}

Update Delivery​

External & Carrier Integrations / Deliveries
PUT/external/deliveries/{{delivery_code}}

Update an external customer's owned delivery order.

Access and immutable fields

  • An external customer can update only an order whose customer_id matches the authenticated customer. A different customer's identifier returns HTTP 404.

  • Tenant staff using this endpoint require manage:operations:tenant or manage:module:delivery.

  • customer_id, delivery_service_id, status, and internal delivery_flow are immutable.

  • Use the public boolean is_replacement to enable or disable replacement handling.

  • Status is not accepted here; use the existing external status/confirmation endpoints.

Editable until final delivery

  • pickup_warehouse_id or dropoff_warehouse_id

  • pickup_location_id or pickup_location

  • delivery_location_id or delivery_location

  • external_reference_id (string or null; trimmed, and an empty string or null clears it)

  • cod_amount (minimum 0)

  • payer_type (sender | receiver)

  • payment_method (wallet | cash | cod; cod is stored as cash)

  • delivery_fee (minimum 0)

  • unit_size (integer, minimum 1)

  • is_replacement (boolean)

  • notes (string or null; send an empty string or null to clear it)

  • package_details, including package_details.special_instructions (string handling instructions) and replacement semantics for items[]; each item requires a name, a positive integer quantity, non-negative numeric dimensions, and a valid UUID when id is supplied

External references keep the tenant delivery_external_reference.unique_per_customer rule. When enabled (default), another root order for the same customer cannot use the same non-empty value; the API returns HTTP 400 with a localized duplicate-reference message and field=external_reference_id.

The external API is no longer draft-only. The same rule is used by internal and external updates: an inbound P&D leg delivered to the warehouse does not lock the order, but final parent/outbound delivered locks all editable fields.

A non-null warehouse ID takes priority over the matching address ID/JSON and resolves the tenant warehouse address. Send the warehouse field as null with a replacement customer address to switch back to a normal address. A missing, cross-tenant, or address-less warehouse fails before the atomic update. Saved non-warehouse address IDs must belong to the authenticated customer. Address and unit changes reuse the fixed service to validate countries/zones and recalculate the fee. An explicit delivery_fee wins. For P&D, endpoint warehouse/address changes propagate to the matching operational leg; external reference, notes, and special instructions are stored on the parent and both operational legs in the same atomic update. If an assigned driver cannot serve the new zone, or the larger unit size exceeds assigned vehicle capacity, the API returns HTTP 409 and nothing changes.

Unchanged endpoint submissions and contact-only location updates do not trigger automatic fee recalculation when the effective address and warehouse IDs and every normalized non-contact location field match the saved endpoint. Any extra submitted location field must also match its saved value. These saves preserve the saved non-contact snapshot, including omitted legacy fields, and still run the existing address resolution, coordinate/country validation, atomic assignment checks, and finance rules. Real endpoint or resolved unit-size changes still run service/zone pricing. An explicitly supplied delivery_fee remains supported under the existing rules.

Replacement behavior

  • is_replacement: true maps the logical order to delivery_flow = replacement_forward; false removes that classification. A child code updates its logical P&D parent.

  • The toggle is editable until final delivery. Final completion and return creation recheck the same state atomically.

  • Enabling replacement while the final payment method is cash remains subject to allow_cod_on_replacement; a forbidden change returns HTTP 400 cod_not_allowed_for_replacement_orders.

  • Disabling after the return exists returns HTTP 409 replacement_return_already_exists.

  • A replacement return cannot be converted; submitting is_replacement on it returns HTTP 400 replacement_return_type_not_editable.

  • A replacement forward follows normal direct/P&D update rules. Its latest addresses, package, unit size, and fixed service are used when the return is generated.

  • Return addresses, package details, notes, special instructions, unit size, payer type, and payment method remain editable until the return is delivered. Direct and staged return assignments receive normal zone and vehicle-capacity validation.

  • Every replacement return remains zero-fee and zero-COD. Non-zero values return HTTP 400 replacement_return_delivery_fee_must_be_zero or replacement_return_cod_must_be_zero.

Finance

  • Draft wallet orders resize or move their reservation atomically as fee, payer, or method changes. No wallet transaction or payment is created until confirmation; changing away from wallet releases the hold.

  • Non-draft sender-wallet updates refund the full previously settled fee first, then charge the full updated fee. These are two visible wallet transaction lines when both amounts are non-zero.

  • Pending sender cash to wallet charges the full current delivery fee.

  • Pending sender wallet to cash refunds the full settled wallet fee and leaves the fee pending for pickup collection.

  • Sender cash remains a pickup obligation before pickup; later corrections refund any full previous wallet settlement, then charge the full corrected fee through the sender wallet without rewriting historical physical cash.

  • Receiver cash or wallet refunds any settled sender fee and stays pending as a final-delivery obligation; no receiver collection/payment row is created by this update.

  • COD updates only change the future goods-collection amount; no COD collection, payment, or wallet entry is created before final delivery.

  • Successful delivery-fee payment rows are the settled-money source of truth; the delivery payment-status flag does not fabricate a wallet settlement.

  • Refund, replacement charge, payment audit, and delivery changes reconcile atomically.

  • Replacement-return payer or method changes move no money because its fee remains zero.

  • insufficient_available_balance or insufficient wallet balance rolls back the update. A missing customer wallet returns HTTP 500.

Webhook

  • External-reference-only, notes-only, package_details.special_instructions-only, and replacement-only updates do not emit order_update; existing tracked fields remain status, COD, price, payment side, and payment method.

  • One request emits at most one existing order_update webhook when tracked fields change; P&D child synchronization emits no duplicate request.

Integration contract

  • When a warehouse is selected, send its non-null pickup_warehouse_id or dropoff_warehouse_id; matching address input is ignored. To deselect it, send the warehouse field as null together with the replacement address.

  • Send only changed fields and use is_replacement for the toggle; never send delivery_flow. Send special instructions only as package_details.special_instructions; top-level special_instructions is rejected.

  • Use the returned canonical parent delivery; delivery_flow = replacement_forward means the toggle is on.

  • Show the toggle until final delivery, except for delivery_flow = replacement_return, where it is read-only. Keep replacement-return delivery_fee and cod_amount read-only at zero.

  • Handle HTTP 400 with field=external_reference_id as a duplicate external reference, plus invalid_delivery_update_fields, invalid_package_details, cod_not_allowed_for_replacement_orders, replacement_return_type_not_editable, and the zero-fee/zero-COD errors.

  • Handle HTTP 409 delivery_already_delivered, replacement_return_already_exists, driver zone/capacity errors, insufficient_available_balance, insufficient_balance, and delivery_update_conflict.

  • On a conflict, keep the previous local order; reload before retrying.

Path parameters

ParameterTypeDescription
delivery_codeRequiredstring

Variable used inside the request path.

Headers

ParameterTypeDescription
X-API-Keystring

Example: {{api_key}}

Content-Typestring

Example: application/json

Body parameters

ParameterTypeDescription
pickup_warehouse_idstring—
pickup_locationobject—
pickup_location.full_addressstring—
pickup_location.latitudenumber—
pickup_location.longitudenumber—
pickup_location.citystring—
pickup_location.countrystring—
dropoff_warehouse_idnull—
delivery_location_idstring—
external_reference_idstring—
cod_amountnumber—
payer_typestring—
Show 15 more body fields

Additional body parameters

ParameterTypeDescription
payment_methodstring—
delivery_feenumber—
unit_sizenumber—
is_replacementboolean—
package_detailsobject—
package_details.special_instructionsstring—
package_details.itemsarray<object>—
package_details.items.namestring—
package_details.items.descriptionstring—
package_details.items.quantitynumber—
package_details.items.weightnumber—
package_details.items.lengthnumber—
package_details.items.widthnumber—
package_details.items.heightnumber—
notesstring—
Staging
Request
curl
1curl --globoff --request PUT "https://staging-api.ondi.io/functions/v1/external/deliveries/${DELIVERY_CODE}" \2  --header "X-API-Key: ${ONDI_TENANT_API_KEY}" \3  --header "Content-Type: application/json" \4  --data-raw '{5  "pickup_warehouse_id": "pickup-warehouse-uuid",6  "pickup_location": {7    "full_address": "Updated pickup address",8    "latitude": 36.191,9    "longitude": 44.01,10    "city": "Erbil",11    "country": "Iraq"12  },13  "dropoff_warehouse_id": null,14  "delivery_location_id": "dropoff-address-uuid",15  "external_reference_id": "CUSTOMER-ORDER-456",16  "cod_amount": 25,17  "payer_type": "receiver",18  "payment_method": "cod",19  "delivery_fee": 31,20  "unit_size": 2,21  "is_replacement": true,22  "package_details": {23    "special_instructions": "Keep upright and avoid heat",24    "items": [25      {26        "name": "Item 1",27        "description": "Updated item",28        "quantity": 1,29        "weight": 3.5,30        "length": 35,31        "width": 25,32        "height": 2033      }34    ]35  },36  "notes": "Call the receiver before delivery"37}'

Response examples

200OK
Response body
json
1{2  "success": true,3  "message": "Delivery updated successfully",4  "currency_code": "IQD",5  "delivery": {6    "id": "parent-delivery-uuid",7    "delivery_code": "2123",8    "status": "in_transit",9    "delivery_flow": "replacement_forward",10    "external_reference_id": "CUSTOMER-ORDER-456",11    "pickup_warehouse_id": "pickup-warehouse-uuid",12    "dropoff_warehouse_id": null,13    "customer_id": "customer-uuid",14    "delivery_service_id": "service-uuid",15    "pickup_location": {16      "full_address": "Updated pickup address",17      "latitude": 36.191,18      "longitude": 44.0119    },20    "delivery_location_id": "dropoff-address-uuid",21    "payer_type": "receiver",22    "payment_method": "cash",23    "delivery_fee": 31,24    "cod_amount": 25,25    "unit_size": 2,26    "package_details": {27      "items": [28        {29          "name": "Item 1",30          "quantity": 131        }32      ]33    },34    "updated_at": "2026-07-21T12:00:00Z",35    "notes": "Call the receiver before delivery"36  }37}

Update Delivery Status By Code​

External & Carrier Integrations / Deliveries
POST/external/deliveries/code/{{delivery_code}}/status

Update a normal delivery status using delivery_code instead of delivery ID. The authenticated external caller can update only deliveries they own.

Behavior

  • Uses the same status workflow as the internal delivery endpoint.

  • Lookup is by delivery_code, but status changes are applied only when the delivery belongs to the authenticated external caller.

  • On success, the response is { success, error, message, currency_code, delivery } and delivery is loaded from delivery_list_view, then enriched for the external contract.

Responses

200 success, 400 validation failure, 404 delivery_not_found, 409 invalid transition / business conflict.

External delivery response enrichment

  • Top-level response includes currency_code. Each delivery object also includes currency_code.

  • delivery.track_order is the public tracking page URL built as {track_url}/tracking?code={delivery_code}; track_url resolves from tenant track_orders.track_url, then the WEB_URL secret, then system external_links.delivery_customer_portal_url, then https://staging-app.ondi.io.

  • delivery.customer_receipt_url is a signed receipt URL when a successful delivery payment receipt exists; otherwise null.

  • delivery.status_updated_at is derived from the latest status_updates[].timestamp, falling back to updated_at.

  • delivery.delivery_services includes description and image_url when available.

  • delivery.proofs is grouped as { pickup: [], delivery: [] } using OnDi proof records.

  • delivery.proof_of_pickup and delivery.proof_of_delivery are aggregate proof summaries when captured, shaped as { signature, photos }.

  • delivery.failure_reason is populated from delivery_failure_reason when present.

  • Top-level delivery_type is not returned; use delivery.delivery_services.delivery_type.

  • Response payment_method is documented as wallet | cash.

Path parameters

ParameterTypeDescription
delivery_codeRequiredstring

Variable used inside the request path.

Headers

ParameterTypeDescription
X-API-Keystring

Example: {{api_key}}

Content-Typestring

Example: application/json

Body parameters

ParameterTypeDescription
target_delivery_statusstring

One of draft, pending, assigned, en_route_to_pickup, picked_up, in_transit, delivered, cancelled, returned.

reasonOptionalstring

Transition reason. If returned_items is sent as a string array, this reason is copied to each item; otherwise the default is returned.

driver_idstring

Required when the requested status needs an assignment, the delivery has no active assignment, and tenant settings do not define assignment_change_settings.default_driver_id.

notesOptionalstring

Timeline / workflow notes.

proof_of_deliveryOptionalobject

Proof payload for delivered flows. Only signature_url and photo_urls are accepted.

proof_of_delivery.signature_urlOptionalstring

Signature asset URL. Some delivered flows may require it based on tenant settings.

proof_of_delivery.photo_urlsOptionalarray<string>

Proof photo URLs. Some delivered flows may require at least one photo based on tenant settings.

bin_idOptionalstring

Internal bin identifier passed through when relevant to the status workflow.

returned_itemsOptionalstring

Used for return flows. (string form): Inventory item ID. The backend converts it to { id, reason } using top-level reason or returned.

returned_items[].idstring

Required when object form is used | string | Inventory item ID.

returned_items[].reasonstring

Required when object form is used | string | Return reason for that item.

Staging
Request
curl
1curl --globoff --request POST "https://staging-api.ondi.io/functions/v1/external/deliveries/code/${DELIVERY_CODE}/status" \2  --header "X-API-Key: ${ONDI_TENANT_API_KEY}" \3  --header "Content-Type: application/json" \4  --data-raw '{5  "target_delivery_status": "delivered",6  "reason": "Delivery completed successfully",7  "driver_id": "{{driver_id}}",8  "notes": "Delivered by external integration",9  "proof_of_delivery": {10    "signature_url": "{{signature_url}}",11    "photo_urls": [12      "{{proof_photo_url_1}}"13    ]14  }15}'

Replace {{placeholders}} with your own values before sending.

Response examples

200OK
Response body
json
1{2  "success": true,3  "error": null,4  "message": "Delivery status updated successfully",5  "delivery": {6    "id": "DEL-000001",7    "status": "delivered",8    "tenant_id": "tenant-uuid",9    "external_reference_id": "EXT-REF-123",10    "customer_id": "customer-uuid",11    "delivery_service_id": "service-uuid",12    "payer_type": "sender",13    "payment_method": "wallet",14    "delivery_fee": 15.5,15    "cod_amount": 0,16    "notes": "Package delivery",17    "unit_size": 1,18    "tracking_provider": "internal",19    "carrier_type": "internal",20    "pickup_warehouse_id": null,21    "dropoff_warehouse_id": null,22    "pickup_location_id": "address-uuid-1",23    "delivery_location_id": "address-uuid-2",24    "receiver_user_id": null,25    "assigned_carrier_id": null,26    "assigned_to_user_id": null,27    "package_details": {28      "special_instructions": "Handle with care"29    },30    "scheduled_pickup_time": "2024-01-15T10:00:00Z",31    "scheduled_delivery_time": "2024-01-15T14:00:00Z",32    "started_at": null,33    "picked_up_at": null,34    "delivered_at": null,35    "cancelled_at": null,36    "delivery_failure_reason": null,37    "created_at": "2024-01-15T08:00:00Z",38    "updated_at": "2024-01-15T08:30:00Z",39    "barcode": "DEL-000001",40    "delivery_code": "DEL-000001"41  }42}

Delete Delivery (Legacy Admin Cancellation)​

External & Carrier Integrations / Deliveries
DELETE/external/deliveries/{{delivery_code}}

Legacy tenant-admin cancellation endpoint. The delivery record is retained. This endpoint delegates to the canonical atomic logical-order cancellation flow, so a parent or operational child resolves to the root, the order family and active work are cancelled consistently, and paid family delivery fees or collected COD are refunded exactly once to the sender wallet. Separate replacement-return orders are excluded. Requires manage:operations:tenant or system-admin access; customer API keys must use POST /external/deliveries/{delivery_code}/cancel.

Responses

200 success, 403 unauthorized, 404 delivery not found, 409 terminal logical-order conflict. Admin callers may cancel assigned work.

Frontend integration contract

treat success as cancellation, not physical deletion. Keep the record and render it with cancelled status. Prefer the explicit POST cancellation endpoint for new integrations.

Path parameters

ParameterTypeDescription
delivery_codeRequiredstring

Variable used inside the request path.

Headers

ParameterTypeDescription
X-API-Keystring

Example: {{api_key}}

Staging
Request
curl
1curl --globoff --request DELETE "https://staging-api.ondi.io/functions/v1/external/deliveries/${DELIVERY_CODE}" \2  --header "X-API-Key: ${ONDI_TENANT_API_KEY}"

Response examples

200OK
Response body
json
1{2  "success": true,3  "message": "Delivery deleted successfully"4}

Cancel Delivery​

External & Carrier Integrations / Deliveries
POST/external/deliveries/{{delivery_code}}/cancel

Cancel a delivery owned by the authenticated external customer. Ownership is enforced before cancellation even if the auth context contains an accidental staff permission; another customer’s delivery returns 404. Cancellation is transactionally blocked with HTTP 409 cannot_cancel_delivery_after_driver_assignment once an assignment in the logical order is active, and with HTTP 409 cannot_cancel_delivery_in_terminal_status after final delivery or another terminal logical state.

  • Empty body: Allowed | N/A | This endpoint accepts no JSON body.

Staged/P&D behavior

the parent or any operational child identifier resolves to the canonical parent. A completed inbound warehouse leg does not block cancellation while final delivery remains incomplete. Parent, non-terminal operational children, assignments, receiving work, handoffs, and eligible picking work are cancelled atomically. Paid family delivery fees and collected COD are refunded once to the sender wallet. Separate replacement-return orders are excluded.

Responses

200 success, 400 validation failure, 403 unauthorized, 404 delivery_not_found, 409 assigned-work or terminal logical-order conflict.

External delivery response enrichment

  • Top-level response includes currency_code. Each delivery object also includes currency_code.

  • The returned delivery is the canonical logical root; delivery.id is its public delivery code and matches delivery.delivery_code.

  • delivery.track_order is the public tracking page URL built as {track_url}/tracking?code={delivery_code}; track_url resolves from tenant track_orders.track_url, then the WEB_URL secret, then system external_links.delivery_customer_portal_url, then https://staging-app.ondi.io.

  • delivery.customer_receipt_url is a signed receipt URL when a successful delivery payment receipt exists; otherwise null.

  • delivery.status_updated_at is derived from the latest status_updates[].timestamp, falling back to updated_at.

  • delivery.delivery_services includes description and image_url when available.

  • delivery.proofs is grouped as { pickup: [], delivery: [] } using OnDi proof records.

  • delivery.proof_of_pickup and delivery.proof_of_delivery are aggregate proof summaries when captured, shaped as { signature, photos }.

  • delivery.failure_reason is populated from delivery_failure_reason when present.

  • Top-level delivery_type is not returned; use delivery.delivery_services.delivery_type.

  • Response payment_method is documented as wallet | cash.

Path parameters

ParameterTypeDescription
delivery_codeRequiredstring

Variable used inside the request path.

Headers

ParameterTypeDescription
X-API-Keystring

Example: {{api_key}}

Body parameters

ParameterTypeDescription
failure_reasonOptionalstring

Reason stored in delivery_failure_reason and the status timeline.

notesOptionalstring

Additional timeline notes.

Staging
Request
curl
1curl --globoff --request POST "https://staging-api.ondi.io/functions/v1/external/deliveries/${DELIVERY_CODE}/cancel" \2  --header "X-API-Key: ${ONDI_TENANT_API_KEY}" \3  --header "Content-Type: application/json" \4  --data-raw '{5  "failure_reason": "Customer changed mind",6  "notes": "Order cancelled per customer request"7}'

Response examples

200OK
Response body
json
1{2  "success": true,3  "error": null,4  "message": "Delivery cancelled successfully",5  "delivery": {6    "id": "DEL-000001",7    "status": "cancelled",8    "tenant_id": "tenant-uuid",9    "external_reference_id": "EXT-REF-123",10    "customer_id": "customer-uuid",11    "delivery_service_id": "service-uuid",12    "payer_type": "sender",13    "payment_method": "wallet",14    "delivery_fee": 15.5,15    "cod_amount": 0,16    "notes": "Order cancelled per customer request",17    "unit_size": 1,18    "tracking_provider": "internal",19    "carrier_type": "internal",20    "pickup_warehouse_id": null,21    "dropoff_warehouse_id": null,22    "pickup_location_id": "address-uuid-1",23    "delivery_location_id": "address-uuid-2",24    "receiver_user_id": null,25    "assigned_carrier_id": null,26    "assigned_to_user_id": null,27    "package_details": {28      "special_instructions": "Handle with care"29    },30    "scheduled_pickup_time": "2024-01-15T10:00:00Z",31    "scheduled_delivery_time": "2024-01-15T14:00:00Z",32    "started_at": null,33    "picked_up_at": null,34    "delivered_at": null,35    "cancelled_at": "2024-01-15T09:00:00Z",36    "delivery_failure_reason": "Customer changed mind",37    "created_at": "2024-01-15T08:00:00Z",38    "updated_at": "2024-01-15T09:00:00Z",39    "barcode": "DEL-000001",40    "delivery_code": "DEL-000001"41  }42}

Confirm Delivery​

External & Carrier Integrations / Deliveries
POST/external/deliveries/{{delivery_code}}/confirm

Confirm (execute) a draft delivery by transitioning its status from 'draft' to 'pending'. The delivery becomes available for assignment and execution.

This endpoint has no request body; it is fully driven by the delivery's current status.

Business Rules

  • External customers can confirm only a delivery they own; another customer’s delivery returns 404

  • Delivery must have status = 'draft'

  • A positive wallet fee must already be reserved; confirmation captures it and creates the delivery-fee transaction/payment atomically

  • For staged parents, the outbound child must not be in 'draft' status (already executed)

Responses

200 success, 409 invalid_status_transition / insufficient_available_balance / wallet reservation conflict, 404 delivery_not_found. The reservation contract returns no currency; use tenant configuration.

External delivery response enrichment

  • Top-level response includes currency_code. Each delivery object also includes currency_code.

  • delivery.id is the public delivery code and matches delivery.delivery_code.

  • delivery.track_order is the public tracking page URL built as {track_url}/tracking?code={delivery_code}; track_url resolves from tenant track_orders.track_url, then WEB_URL secret, then system external_links.delivery_customer_portal_url, then https://staging-app.ondi.io.

  • delivery.customer_receipt_url is a signed receipt URL when a successful delivery payment receipt exists; otherwise null.

  • delivery.status_updated_at is derived from the latest status_updates[].timestamp, falling back to updated_at.

  • delivery.delivery_services includes description and image_url when available.

  • delivery.proofs is grouped as { pickup: [], delivery: [] } using OnDi proof records.

  • delivery.proof_of_pickup and delivery.proof_of_delivery are aggregate proof summaries when captured, shaped as { signature, photos }.

  • delivery.failure_reason is populated from delivery_failure_reason when present.

  • Top-level delivery_type is not returned; use delivery.delivery_services.delivery_type.

  • payment_method is documented as wallet|cash.

Path parameters

ParameterTypeDescription
delivery_codeRequiredstring

Variable used inside the request path.

Headers

ParameterTypeDescription
X-API-Keystring

Example: {{api_key}}

Staging
Request
curl
1curl --globoff --request POST "https://staging-api.ondi.io/functions/v1/external/deliveries/${DELIVERY_CODE}/confirm" \2  --header "X-API-Key: ${ONDI_TENANT_API_KEY}"

Response examples

200Success
Response body
json
1{2  "success": true,3  "error": null,4  "message": "Delivery status updated successfully",5  "delivery": {6    "id": "DEL-000001",7    "status": "pending"8  }9}