Skip to main content

Warehouse Exceptions

6 API calls in this section.

Warehouse item exceptions for receiving, picking, inventory audits, and inventory handling. Source of truth is warehouse_exceptions. An item may have multiple open exceptions. Tenant-defined types may set write_off_stock=true; the exception flow is the sole owner of that stock write-off. Audit-created exceptions expose inventory_audit_id; their stock transaction references the audit directly and does not rewrite historical receiving quantities. An open damaged exception with an exact package mapping and current available warehouse stock exposes available_actions[].key=return_to_origin so only that box can be sent back while the parent order continues. Current stock is matched by tenant, inventory item, and warehouse when the exception predates stock creation and therefore has no inventory_stock_id; an explicit stock ID remains exact. receiving_order_id is optional audit provenance and is inferred from inventory custody metadata when available.

List Warehouse Exceptions​

Warehousing / Warehouse Exceptions
GET/warehousing/exceptions

Lists warehouse item exceptions. Authentication requires the normal warehousing view access. Use filters for item, receiving order, picking order, warehouse, status, and type. Omit status to return both open and resolved history, including resolution details, resolver, and resolution time. Open damaged exceptions return the exact package_item_id and available_actions only when the item has current available unreserved stock in the exception warehouse. This includes stock created after the exception when inventory_stock_id is null; an explicit stock ID must still match exactly. The action endpoint revalidates stock and delivery state atomically. Receiving provenance does not control action visibility.

Query parameters

ParameterTypeDescription
tenant_idOptionalstring

UUID of the tenant. Optional when available from auth context.

Example: {{tenant_id}}

statusOptionalstring

Optional: open | resolved

Example: open

inventory_item_idOptionalstring

Optional item filter

receiving_order_idOptionalstring

Optional receiving order filter

picking_order_idOptionalstring

Optional picking order filter

warehouse_idOptionalstring

Optional warehouse filter

exception_typeOptionalstring

Optional exception type code filter

pagestring

Example: 1

limitstring

Example: 20

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Content-Typestring

Example: application/json

Staging
Request
curl
1curl --globoff --request GET "https://staging-api.ondi.io/functions/v1/warehousing/exceptions?tenant_id=${TENANT_ID}&status=open&page=1&limit=20" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}" \3  --header "Content-Type: application/json"

Response examples

200OK
Response body
json
1{2  "success": true,3  "exceptions": [4    {5      "id": "open-exception-uuid",6      "inventory_item_id": "item-uuid",7      "receiving_order_id": "receiving-order-uuid",8      "exception_type": "damaged",9      "status": "open",10      "package_item_id": "package-item-uuid",11      "available_actions": [12        {13          "key": "return_to_origin",14          "exception_id": "open-exception-uuid",15          "package_item_id": "package-item-uuid"16        }17      ],18      "notes": "Box crushed on arrival",19      "reported_by_user": {20        "id": "reporter-uuid",21        "full_name": "Warehouse Receiver"22      },23      "resolved_by_user": null,24      "affected_quantity": 1,25      "exception_details": {26        "damage_area": "outer_box"27      },28      "item": {29        "id": "item-uuid",30        "name": "Product Name",31        "sku": "SKU-123"32      },33      "warehouse": {34        "id": "warehouse-uuid",35        "name": "Main Warehouse",36        "code": "WH-001"37      }38    },39    {40      "id": "resolved-exception-uuid",41      "inventory_item_id": "item-uuid",42      "receiving_order_id": "receiving-order-uuid",43      "exception_type": "wrong_item",44      "status": "resolved",45      "package_item_id": "package-item-uuid",46      "available_actions": [],47      "resolution_details": {48        "note": "Correct item confirmed"49      },50      "resolved_by": "resolver-uuid",51      "resolved_at": "2026-08-22T07:45:00Z",52      "resolved_by_user": {53        "id": "resolver-uuid",54        "full_name": "Warehouse Supervisor"55      },56      "affected_quantity": 1,57      "exception_details": {},58      "item": {59        "id": "item-uuid",60        "name": "Product Name",61        "sku": "SKU-123"62      },63      "warehouse": {64        "id": "warehouse-uuid",65        "name": "Main Warehouse",66        "code": "WH-001"67      }68    }69  ],70  "stats": {71    "totalExceptions": 2,72    "openCount": 1,73    "resolvedCount": 174  },75  "page": 1,76  "limit": 20,77  "total": 278}

Available Warehouse Exception Types​

Warehousing / Warehouse Exceptions
GET/warehousing/exceptions/available

Returns exception type options available for a specific item/context. Type configuration comes from tenant override tenant_settings.warehouse_exception_types or system setting warehouse_exception_types. Each type returns write_off_stock; this behavior is configured by the tenant and must not be inferred from its code or label.

Query parameters

ParameterTypeDescription
tenant_idstring

Example: {{tenant_id}}

inventory_item_idRequiredstring

Required inventory item id

Example: {{inventory_item_id}}

inventory_stock_idOptionalstring—
receiving_order_idOptionalstring

Optional receiving context

Example: {{receiving_order_id}}

picking_order_idOptionalstring—

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Content-Typestring

Example: application/json

Staging
Request
curl
1curl --globoff --request GET "https://staging-api.ondi.io/functions/v1/warehousing/exceptions/available?tenant_id=${TENANT_ID}&inventory_item_id=${INVENTORY_ITEM_ID}&receiving_order_id=${RECEIVING_ORDER_ID}" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}" \3  --header "Content-Type: application/json"

Response examples

200OK
Response body
json
1{2  "success": true,3  "exception_types": [4    {5      "code": "missing",6      "label": "Missing",7      "description": "Expected item is missing during receiving or handling.",8      "write_off_stock": true9    }10  ],11  "all_exception_types": []12}

Create Warehouse Exception​

Warehousing / Warehouse Exceptions
POST/warehousing/exceptions

Creates one open warehouse exception for a specific inventory item. Mutation access requires manage:operations:tenant, manage:module:warehouse, system admin, or a warehouse role.

Business rules

  • inventory_item_id and exception_type are required.

  • exception_type must exist in warehouse_exception_types.

  • Context can be receiving (receiving_order_id), picking (picking_order_id), stock (inventory_stock_id), or general inventory. receiving_order_id is optional; when omitted, a valid receipt recorded in the inventory item's custody metadata is captured as audit provenance.

  • An item may have multiple independent open exceptions in the same context.

  • If one pending delivery already owns one completed picking order containing the item, creation captures that picking_order_id as preparation context; ambiguous candidates remain recordable and are rejected safely only if resolution would need to restore preparation.

  • If the selected tenant-defined type has write_off_stock=true and the linked receipt item has positive received_quantity, affected_quantity defaults to 1 and must be positive. The exception insert atomically deducts available stock, releases any reservation that can no longer be satisfied, changes a zero-quantity stock row to depleted, reduces the receipt's received_quantity, records the authenticated exception actor as received_by when the receipt had no actor, and writes a stock_adjustment inventory transaction. A fully received order changes to received_with_exceptions; a partially received order remains partially_received so outstanding unreceived lines are not completed. This is the normal Lost/received-stock path.

  • If the linked receipt item has received_quantity=0, or omits received_quantity because receiving has not started, the item has not entered warehouse stock. The exception is still created, no stock or receipt row is changed by the write-off trigger, unrelated stock for the same inventory item is not consumed, and the response returns stock_written_off=false with null write-off audit fields.

  • The exception response records stock_written_off, written_off_quantity, written_off_by, written_off_at, and written_off_by_user.

  • If positive received quantity exists but sufficient available stock cannot be written off, HTTP 409 returns code warehouse_write_off_stock_not_available.

  • Resolving the exception later follows the existing resolution behavior.

Query parameters

ParameterTypeDescription
tenant_idstring

Example: {{tenant_id}}

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Content-Typestring

Example: application/json

Body parameters

ParameterTypeDescription
inventory_item_idstring—
receiving_order_idstring—
warehouse_idstring—
location_idstring—
exception_typestring—
affected_quantitynumber—
exception_detailsobject—
notesstring—
Staging
Request
curl
1curl --globoff --request POST "https://staging-api.ondi.io/functions/v1/warehousing/exceptions?tenant_id=${TENANT_ID}" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}" \3  --header "Content-Type: application/json" \4  --data-raw '{5  "inventory_item_id": "{{inventory_item_id}}",6  "receiving_order_id": "{{receiving_order_id}}",7  "warehouse_id": "{{warehouse_id}}",8  "location_id": "{{bin_location_id}}",9  "exception_type": "missing",10  "affected_quantity": 1,11  "exception_details": {},12  "notes": "Box could not be located"13}'

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

Response examples

201Created
Response body
json
1{2  "success": true,3  "exception": {4    "id": "exception-uuid",5    "exception_type": "missing",6    "status": "open",7    "stock_written_off": true,8    "written_off_quantity": 1,9    "written_off_by": "user-uuid",10    "written_off_at": "2026-08-22T10:15:00Z",11    "written_off_by_user": {12      "id": "user-uuid",13      "full_name": "Warehouse Receiver"14    },15    "affected_quantity": 1,16    "exception_details": {},17    "notes": "Box could not be located",18    "item": {19      "id": "item-uuid",20      "name": "Product Name",21      "sku": "SKU-123"22    },23    "warehouse": {24      "id": "warehouse-uuid",25      "name": "Main Warehouse",26      "code": "WH-001"27    }28  },29  "open_exception": {30    "id": "exception-uuid",31    "exception_type": "missing",32    "exception_label": "Missing",33    "status": "open",34    "affected_quantity": 1,35    "stock_written_off": true,36    "written_off_quantity": 1,37    "written_off_by": "user-uuid",38    "written_off_at": "2026-08-22T10:15:00Z",39    "exception_details": {},40    "notes": "Box could not be located"41  }42}

Get Warehouse Exception​

Warehousing / Warehouse Exceptions
GET/warehousing/exceptions/{{warehouse_exception_id}}

Fetches one warehouse exception with item, stock, warehouse, location, optional receiving-order provenance, picking order, optional inventory_audit_id, reporter, resolver, and write-off audit context. For an open damaged stock-backed exception, package_item_id identifies the exact order box and available_actions contains return_to_origin when the mapping and available stock exist.

Path parameters

ParameterTypeDescription
warehouse_exception_idRequiredstring

Variable used inside the request path.

Query parameters

ParameterTypeDescription
tenant_idstring

Example: {{tenant_id}}

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Content-Typestring

Example: application/json

Staging
Request
curl
1curl --globoff --request GET "https://staging-api.ondi.io/functions/v1/warehousing/exceptions/${WAREHOUSE_EXCEPTION_ID}?tenant_id=${TENANT_ID}" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}" \3  --header "Content-Type: application/json"

Response examples

200OK
Response body
json
1{2  "success": true,3  "exception": {4    "id": "exception-uuid",5    "exception_type": "damaged",6    "status": "open",7    "package_item_id": "package-item-uuid",8    "available_actions": [9      {10        "key": "return_to_origin",11        "exception_id": "exception-uuid",12        "package_item_id": "package-item-uuid"13      }14    ]15  }16}

Return Damaged Package to Origin​

Warehousing / Warehouse Exceptions
POST/warehousing/exceptions/{{warehouse_exception_id}}/return-to-origin

Creates and prepares one zero-fee internal reverse delivery from the current warehouse to the original sender for the exact damaged package linked to this exception. Requires warehouse mutation access. Tenant and system administrators may act without a warehouse assignment; other warehouse actors require an active staff assignment at the warehouse holding the package. notes is optional.

Business rules

  • The exception must be open and damaged; the exact inventory/package mapping and positive unreserved warehouse stock are the custody authority. A receiving order is optional audit provenance, not a prerequisite.

  • The inventory item must have an exact delivery_package_item_id; ambiguous legacy mappings are rejected.

  • The customer-bound staged outbound delivery must still be draft and unassigned.

  • At least one other eligible package must remain for the customer; otherwise use the existing whole-order return flow.

  • The reverse delivery uses return_requests.default_delivery_service_id, delivery_flow=operational_return, delivery_fee=0, cod_amount=0, and payment_status=paid. It creates no customer charge or refund.

  • The source package leaves customer dispatch immediately through return_status=return_approved, but its primary status remains pending until the reverse delivery reaches the sender. Only then does the source package become returned.

  • The source package is excluded from the customer-bound driver detail package list and its package/actionable counts while the return is approved, in transit, or completed. The separate reverse delivery keeps its own actionable package.

  • Each P&D pickup completes its existing assignment-owned handoff once; the legacy handoff fallback does not create a second handoff. Staged parent status updates do not create physical handoffs; the child legs own pickup. Customer-bound pickup and completion affect only customer-bound inventory, preserving the separate return package's stock, reservation, and custody.

  • Sender delivery completion sets the exact inventory item linked by delivery_package_item_returns to returned_to_sender custody. It does not select inventory through the original outbound link or change sibling inventory.

  • The parent/root and customer-bound outbound delivery statuses are never changed by this action. Other packages continue normally.

  • The exception is resolved with the actor and timestamp. The action is idempotent for the same exception and returns HTTP 200 with idempotent_replay=true on replay.

  • Package-return reverse deliveries can be retried or reassigned after an exception but cannot use generic cancellation, because cancelling would strand the required physical return.

Frontend integration contract

  • Show this action only when the exception/item response contains available_actions[].key=return_to_origin.

  • Call this endpoint with the exception_id; do not call the whole-order return endpoint and do not send a package status.

  • On success, use data.return_delivery_id to open the reverse delivery and data.package_return_status for its package-return state.

  • HTTP 409 means the action is stale or ineligible; use the stable code for branching and show the localized message.

Path parameters

ParameterTypeDescription
warehouse_exception_idRequiredstring

Variable used inside the request path.

Query parameters

ParameterTypeDescription
tenant_idstring

Example: {{tenant_id}}

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Content-Typestring

Example: application/json

Body parameters

ParameterTypeDescription
notesstring—
Staging
Request
curl
1curl --globoff --request POST "https://staging-api.ondi.io/functions/v1/warehousing/exceptions/${WAREHOUSE_EXCEPTION_ID}/return-to-origin?tenant_id=${TENANT_ID}" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}" \3  --header "Content-Type: application/json" \4  --data-raw '{5  "notes": "Box crushed during inbound handling"6}'

Response examples

201Created
Response body
json
1{2  "success": true,3  "message": "The damaged package was prepared for return to the sender",4  "data": {5    "success": true,6    "action_key": "return_to_origin",7    "exception_id": "exception-uuid",8    "exception_status": "resolved",9    "resolved_by": "user-uuid",10    "resolved_at": "2026-09-11T14:30:00Z",11    "package_item_id": "package-item-uuid",12    "package_item_status": "pending",13    "package_return_id": "package-return-uuid",14    "package_return_status": "return_approved",15    "return_delivery_id": "return-delivery-uuid",16    "return_delivery_code": 1050,17    "return_delivery_status": "pending",18    "picking_order_id": "picking-order-uuid",19    "root_delivery_id": "root-delivery-uuid",20    "root_delivery_status": "pending",21    "outbound_delivery_id": "outbound-delivery-uuid",22    "outbound_delivery_status": "draft",23    "handoff_id": null,24    "idempotent_replay": false25  }26}

Update Warehouse Exception​

Warehousing / Warehouse Exceptions
PUT/warehousing/exceptions/{{warehouse_exception_id}}

Updates or resolves a warehouse exception. Allowed statuses are open and resolved. Change a wrong reason with exception_type; if an open exception has not written off stock before and the corrected tenant-defined type has write_off_stock=true, the same atomic write-off is applied once. When that correction completes an in-progress receipt, the authenticated updater becomes received_by, the receipt becomes received_with_exceptions, and an item corrected to received_quantity=0 creates no stock and requires no bin. Changing away from that type or changing it again never restores or repeats the write-off. Resolve either a completed issue or a mistaken flag with status=resolved and an optional short note in resolution_details.note; resolution records resolved_by and resolved_at and cannot be reopened. A prior configured write-off is reversed exactly once. If this is the item's final open marker and one pending delivery still owns one valid completed picking order, the same stock is reserved back to that pick and item metadata records reservation_state=reserved; delivery, assignment, picking-order, and handoff statuses do not advance. If no valid preparation remains, restored stock stays available. Ambiguous or quantitatively inconsistent active preparation returns HTTP 409 warehouse_preparation_inconsistent and leaves the exception open.

Frontend integration contract

  • Continue sending the same resolution payload; no new request field is required.

  • On success, replace the local exception with the returned exception.

  • A missing unit with no physical receipt is a valid success and must not prompt for a bin.

  • Handle HTTP 409 warehouse_write_off_stock_not_available for real stock conflicts and warehouse_preparation_inconsistent when existing picking/stock records require correction before resolution can finish.

Path parameters

ParameterTypeDescription
warehouse_exception_idRequiredstring

Variable used inside the request path.

Query parameters

ParameterTypeDescription
tenant_idstring

Example: {{tenant_id}}

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Content-Typestring

Example: application/json

Body parameters

ParameterTypeDescription
statusstring—
resolution_detailsobject—
resolution_details.notestring—
Staging
Request
curl
1curl --globoff --request PUT "https://staging-api.ondi.io/functions/v1/warehousing/exceptions/${WAREHOUSE_EXCEPTION_ID}?tenant_id=${TENANT_ID}" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}" \3  --header "Content-Type: application/json" \4  --data-raw '{5  "status": "resolved",6  "resolution_details": {7    "note": "Issue sorted out"8  }9}'

Response examples

200OK
Response body
json
1{2  "success": true,3  "exception": {4    "id": "exception-uuid",5    "status": "resolved",6    "picking_order_id": "picking-order-uuid",7    "resolution_details": {8      "note": "Issue sorted out"9    },10    "resolved_by": "user-uuid",11    "resolved_at": "2026-08-22T07:45:00Z",12    "resolved_by_user": {13      "id": "user-uuid",14      "full_name": "Warehouse Supervisor"15    }16  }17}