G2G Transfer Manifest Items — Bulk Scan
6 API calls in this section.
Dispatcher-panel bulk workflow for processing G2G manifest items at the processing point.
Typical flow
GET /search— autocomplete as the dispatcher scans / types barcodes.POST /bulk/validate— pre-flight check for a batch of identifiers (UUIDs or barcode strings).POST /bulk/set-destination— in one frontend action, start receiving when needed, receive the selected items, complete receiving when every manifest item is accounted for, then set destination and create delivery orders.POST /bulk/resolve-exceptions— resolve selected exception queue items, or all exception items under one manifest, asreprocessorvoid.
Driver arrival is never skipped. A manifest still in draft, picked_up, or in_transit is rejected with its manifest number and current status, leaving the driver arrival/photo/signature step available. awaiting_receipt starts receiving inside the same call; receiving continues receiving; received, received_with_exceptions, and processing are receipt-safe retry states.
Barcode strings passed to /bulk/validate must be valid 14-character G2G postal barcodes with a matching MOD 11 check digit. A malformed or checksum-invalid input returns a localized per-item invalid_barcode result without a database barcode lookup.
All endpoints are tenant-scoped via the Authorization bearer token. Search exposes unpromoted picked_up, in_transit, and received items. Validation inspects found items regardless of lifecycle status so rejected rows can name their manifest and current state.
Feature gate
these endpoints require tenant setting transfer_manifest.enabled to be true. When disabled, no validation, search, update, exception resolution, or order creation runs; the API returns HTTP 403 with { "success": false, "code": "transfer_manifest_disabled", "message": "Transfer manifest is disabled for this tenant" }.
Frontend contract
keep one action/button and call bulk/set-destination once. Do not add a separate receiving action. Handle the stable error codes and display their localized messages.
Search Manifest Items
/delivery/transfer-manifest-items/searchTenant-wide manifest item search for autocomplete / barcode scanner input.
Returns only unpromoted post-pickup items with status picked_up, in_transit, or received and no existing delivery_id. Excludes scanned, processing, order_created, and void items. Returns enriched rows including manifest context (manifest_number, manifest_status), institution user, and source branch.
Query parameters
| Parameter | Type | Description |
|---|---|---|
q | string | Search term. If a valid UUID, performs an exact id match. Otherwise, performs a case-insensitive partial match on Example: |
limit | string | Max results to return. Range 1–100. Default 20. Example: |
Headers
| Parameter | Type | Description |
|---|---|---|
Authorization | string | Example: |
1curl --globoff --request GET "https://staging-api.ondi.io/functions/v1/delivery/transfer-manifest-items/search?q=BC-001&limit=20" \2 --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}"Response examples
1{2 "success": true,3 "message": "Data retrieved successfully",4 "data": [5 {6 "manifest_item_id": "item-uuid",7 "barcode_value": "BC-001",8 "status": "received",9 "manifest_id": "manifest-uuid",10 "manifest_number": "MF-2026-0042",11 "manifest_status": "received",12 "institution": {13 "id": "user-uuid",14 "full_name": "City Hospital",15 "phone": "+9647001234567"16 },17 "source_branch": {18 "id": "branch-uuid",19 "name": "Main Branch",20 "code": "MB-01"21 },22 "destination_branch_id": null,23 "destination_address_id": null,24 "delivery_id": null,25 "already_has_order": false26 }27 ]28}Bulk Validate — using object identifiers
/delivery/transfer-manifest-items/bulk/validateSame as the plain-string variant but uses the { type, value } object shape. The type field is ignored server-side; routing is determined by the value format.
A found item is valid when it can safely enter the one-call flow
awaiting_receipt or receiving with recorded driver arrival and required delivery proof, or receipt-safe retry status received, received_with_exceptions, or processing with complete receipt fields and an item still in received. Pre-arrival, terminal, inconsistent, or already-promoted rows return valid: false with a stable error_key, localized error_message, manifest number, manifest status, and current item status. The overall batch remains HTTP 200 so valid and invalid identifiers can be returned together.
Frontend integration contract
use each result's valid boolean. Display error_message; use error_key only for branching. Keep one Execute action.
Headers
| Parameter | Type | Description |
|---|---|---|
Authorization | string | Example: |
Content-Type | string | Example: |
Body parameters
| Parameter | Type | Description |
|---|---|---|
identifiers | array<object> | — |
identifiers.type | string | — |
identifiers.value | string | — |
1curl --globoff --request POST "https://staging-api.ondi.io/functions/v1/delivery/transfer-manifest-items/bulk/validate" \2 --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}" \3 --header "Content-Type: application/json" \4 --data-raw '{5 "identifiers": [6 {7 "type": "barcode",8 "value": "GE000010000017"9 },10 {11 "type": "id",12 "value": "item-uuid-003"13 }14 ]15}'Response examples
1{2 "success": true,3 "message": "Manifest item validation completed",4 "data": {5 "results": [6 {7 "input": "GE000090000020",8 "valid": false,9 "error_key": "manifest_bulk_order_creation_not_allowed",10 "error_message": "Manifest TM-MREX5VCN-ZQD2 is picked_up. Bulk order creation is not allowed in this status.",11 "manifest_item_id": "item-uuid",12 "barcode_value": "GE000090000020",13 "manifest_id": "manifest-uuid",14 "manifest_number": "TM-MREX5VCN-ZQD2",15 "manifest_status": "picked_up",16 "current_status": "picked_up",17 "allowed_manifest_statuses": [18 "awaiting_receipt",19 "receiving",20 "received",21 "received_with_exceptions",22 "processing"23 ]24 }25 ],26 "total_valid": 0,27 "total_failed": 128 }29}Bulk Set Destination & Create Orders
/delivery/transfer-manifest-items/bulk/set-destinationTenant-wide one-call receiving and order-creation endpoint. It accepts manifest item IDs from any manifest under the tenant and creates delivery orders with institution wallet payment and payer_type=sender. COD is not used.
One of
destination_branch_idordestination_address_id.
State behavior
draft,picked_up,in_transit,completed, andcancelledare rejected before any receipt/order write. The localized message names the manifest and current status.awaiting_receipt: verifiesarrived_atand required delivery photo/signature, recordsreceiving_started, and fillsreceived_by.receiving: does not start receiving again; it requires the existing receiver record.Selected
picked_up/in_transititems are atomically changed toreceived, receivereceived_at, update their barcode, and get item-levelitem_receivedevents.If any manifest item remains unaccounted for, that manifest stays
receiving; its selected items remain received but no order is created yet. The response usesmanifest_receiving_incomplete.When every item is accounted for, receiving is completed first (
received_at,received_by, durable received count,receiving_completed), then orders are created.received,received_with_exceptions, andprocessingallow retry only for items still inreceivedwith complete receipt fields.Every promotion is protected again in the shared service and database, so
picked_up → order_createdis impossible.
Ordering contract
receipt.received_item_ids, receipt.ready_item_ids, per-manifest item_ids, created, and failed follow the first-seen item_ids order, independent of database row order or parallel completion timing. Before bounded parallel creation starts, the backend atomically reserves parent delivery codes in that same order. Invalid or ineligible items are skipped without changing the relative order of the remaining items. Passing the returned created delivery codes to Generate Shipment Labels in response order therefore produces page 1 for the first successfully scanned item, page 2 for the next, and so on. Retries reuse an existing delivery or its durable reservation.
Existing promotion contract preserved
every created order stores the immutable source snapshot with snapshot_version=manifest_origin_v1. The parent delivery stores the manifest and item IDs; A/B legs store the manifest ID and the same snapshot, but not the item ID. Canonical manifest orders use the dedicated manifest_shipment_label assignment; normal deliveries keep the existing shipment_label path. When package measurements and an explicit unit size are both absent, the shared resolver uses the configured missing_unit_size_fallback.
Processing and performance
receipt is one tenant-scoped database RPC with deterministic row locks and batch writes. Receipt-safe items/manifests are then loaded with focused batch queries. One short reservation RPC allocates the full contiguous code block before creation. Up to 20 ready items use the existing bounded creation path directly. A larger request is split into ordered parts of at most 20 and sent through at most two fresh Edge invocations at once; each part limits delivery creation concurrency to two, keeping total database concurrency equivalent to the previous path while resetting the 2-second CPU budget per part. Retryable 502/503/504/546 part failures are retried up to three times. If a child committed but its response was lost, the coordinator reconciles only items durably finalized as order_created. The public request and response shapes do not change. There is no per-item reservation or receipt/event query. Existing origin snapshot, label, pricing-origin, delivery-policy, warehouse-staging, and missing-unit fallback behavior remains unchanged.
Response shape
{ created_count, failed_count, created, failed, receipt }. receipt contains requested_count, received_count, received_item_ids, ready_item_ids, and per-manifest status/results.
Frontend integration contract
keep the existing request and one Execute action. Preserve the returned created order when passing delivery codes to Generate Shipment Labels. Do not call Start Receiving or Receive Items separately from this screen. Display localized message/failed[].error_message; branch on stable codes such as invalid_uuid, manifest_bulk_order_creation_not_allowed, manifest_arrival_required, manifest_delivery_proof_required, manifest_bulk_receipt_incomplete, and manifest_receiving_incomplete. On an incomplete manifest, keep the user in the workflow because selected items were received but orders were intentionally not created.
Headers
| Parameter | Type | Description |
|---|---|---|
Authorization | string | Example: |
Content-Type | string | Example: |
Body parameters
| Parameter | Type | Description |
|---|---|---|
item_idsRequired | array<string> | required array of manifest item UUID strings. At least 1. Duplicates are deduplicated; the first occurrence defines the item's execution position. |
destination_address_id | string | — |
delivery_service_idOptional | string | optional when G2G settings has |
destination_branch_id | string | — |
1curl --globoff --request POST "https://staging-api.ondi.io/functions/v1/delivery/transfer-manifest-items/bulk/set-destination" \2 --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}" \3 --header "Content-Type: application/json" \4 --data-raw '{5 "item_ids": [6 "{{item_id}}"7 ],8 "destination_address_id": "{{destination_address_id}}",9 "delivery_service_id": "{{delivery_service_id}}",10 "destination_branch_id": "{{destination_branch_id}}"11}'Replace {{placeholders}} with your own values before sending.
Response examples
1{2 "success": true,3 "message": "Orders created successfully",4 "data": {5 "created_count": 2,6 "failed_count": 0,7 "created": [8 {9 "item_id": "item-uuid-1",10 "delivery": {11 "id": "delivery-uuid",12 "delivery_code": "2501",13 "status": "pending"14 }15 },16 {17 "item_id": "item-uuid-2",18 "delivery": {19 "id": "delivery-uuid-2",20 "delivery_code": "2502",21 "status": "pending"22 }23 }24 ],25 "failed": [],26 "receipt": {27 "requested_count": 2,28 "received_count": 2,29 "received_item_ids": [30 "item-uuid-1",31 "item-uuid-2"32 ],33 "ready_item_ids": [34 "item-uuid-1",35 "item-uuid-2"36 ],37 "manifests": [38 {39 "manifest_id": "manifest-uuid",40 "manifest_number": "TM-001",41 "status": "received",42 "remaining_count": 0,43 "ready": true44 }45 ]46 }47 }48}Bulk Set Destination — with address ID directly
/delivery/transfer-manifest-items/bulk/set-destinationSame one-call receipt-safe endpoint, passing destination_address_id directly instead of destination_branch_id. delivery_service_id can be omitted when the G2G default is configured. Driver arrival remains mandatory; the backend starts/continues receiving, records item-level receipt, completes a fully accounted manifest, and only then creates orders. Receipt-safe retries from received, received_with_exceptions, or processing do not duplicate receiving events. First-seen item_ids order controls execution results and atomically reserved delivery codes even though order creation remains parallel; invalid or ineligible items are skipped without reordering the survivors. Existing institution-wallet, manifest-origin snapshot, label, pricing-origin, delivery-policy, warehouse-staging, and missing-unit fallback behavior remains unchanged. The immutable source snapshot uses snapshot_version=manifest_origin_v1; A/B legs store the manifest ID and the same snapshot, but not the item ID. Canonical manifest orders use the dedicated manifest_shipment_label assignment, while normal deliveries keep the existing shipment_label path. The shared resolver uses the configured missing_unit_size_fallback when package measurements and an explicit unit size are absent.
Frontend integration contract
keep the same request and one Execute action, preserve the returned created order when requesting labels, and use the localized receipt/order errors described on the primary example.
Headers
| Parameter | Type | Description |
|---|---|---|
Authorization | string | Example: |
Content-Type | string | Example: |
Body parameters
| Parameter | Type | Description |
|---|---|---|
item_ids | array<string> | — |
destination_address_id | string | — |
1curl --globoff --request POST "https://staging-api.ondi.io/functions/v1/delivery/transfer-manifest-items/bulk/set-destination" \2 --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}" \3 --header "Content-Type: application/json" \4 --data-raw '{5 "item_ids": [6 "item-uuid-1",7 "item-uuid-2"8 ],9 "destination_address_id": "address-uuid"10}'Response examples
No saved response example. Use Try it to inspect a staging response.
Bulk Resolve Exceptions
/delivery/transfer-manifest-items/bulk/resolve-exceptionsBulk resolution endpoint for dispatcher exception queues. The user can resolve selected exception item IDs from GET /delivery/transfer-manifest-exceptions, or resolve all exception items under one manifest.
Selected items body
item_ids— array of transfer manifest item UUIDs.resolution— optional,reprocessorvoid. Defaults toreprocess.notes— optional note stored on the resolved items.
All exceptions under manifest body
manifest_id— transfer manifest UUID.all_exceptions— must betrueto explicitly resolve every current exception under the manifest.resolution— optional,reprocessorvoid. Defaults toreprocess.notes— optional note stored on the resolved items.
Behavior
Only items currently in
exceptionstatus are resolved.reprocessmoves item and barcode back toreceived, clearsexception_reason, and makes the item eligible for bulk destination/order creation.voidmoves item and barcode tovoid, terminally excluding it from order creation.When the last exception under a manifest is resolved, a manifest in
received_with_exceptionsreturns toreceivedif non-terminal items remain. If every item isorder_createdorvoid, the manifest becomescompleted.Non-exception selected items are returned in
skipped; missing selected IDs are returned innot_found.
Events
an exception_resolved event is recorded per resolved item with payload.barcode_value, payload.resolution, and payload.notes; automatic manifest correction is recorded as manifest_status_changed.
Headers
| Parameter | Type | Description |
|---|---|---|
Authorization | string | Example: |
Content-Type | string | Example: |
Body parameters
| Parameter | Type | Description |
|---|---|---|
item_ids | array<string> | — |
resolution | string | — |
notes | string | — |
1curl --globoff --request POST "https://staging-api.ondi.io/functions/v1/delivery/transfer-manifest-items/bulk/resolve-exceptions" \2 --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}" \3 --header "Content-Type: application/json" \4 --data-raw '{5 "item_ids": [6 "{{exception_item_id}}"7 ],8 "resolution": "reprocess",9 "notes": "Resolved from dispatcher exception queue"10}'Replace {{placeholders}} with your own values before sending.
Response examples
1{2 "success": true,3 "message": "Updated successfully",4 "data": {5 "resolved_count": 2,6 "resolved": [7 {8 "item_id": "item-uuid-1",9 "from_status": "exception",10 "to_status": "received"11 },12 {13 "item_id": "item-uuid-2",14 "from_status": "exception",15 "to_status": "received"16 }17 ],18 "skipped": [],19 "not_found": []20 }21}Bulk Resolve All Exceptions Under Manifest
/delivery/transfer-manifest-items/bulk/resolve-exceptionsResolves every current exception item under one transfer manifest. Requires all_exceptions: true so the client cannot accidentally resolve a whole manifest when it meant to send selected item IDs. When this resolves the last exception, a manifest in received_with_exceptions returns to received if non-terminal items remain, or becomes completed when every item is order_created or void.
Events
an exception_resolved event is recorded per resolved item with payload.barcode_value, payload.resolution, and payload.notes; automatic manifest correction is recorded as manifest_status_changed.
Headers
| Parameter | Type | Description |
|---|---|---|
Authorization | string | Example: |
Content-Type | string | Example: |
Body parameters
| Parameter | Type | Description |
|---|---|---|
manifest_id | string | — |
all_exceptions | boolean | — |
resolution | string | — |
notes | string | — |
1curl --globoff --request POST "https://staging-api.ondi.io/functions/v1/delivery/transfer-manifest-items/bulk/resolve-exceptions" \2 --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}" \3 --header "Content-Type: application/json" \4 --data-raw '{5 "manifest_id": "{{manifest_id}}",6 "all_exceptions": true,7 "resolution": "void",8 "notes": "Void all unresolved exceptions under this manifest"9}'Replace {{placeholders}} with your own values before sending.
Response examples
No saved response example. Use Try it to inspect a staging response.