Import Packing Layout
The wholesale_order_import_packing_layout mutation allows you to define the full packing structure of a wholesale order in a single API call, rather than building it container-by-container through the ShipHero UI.
Important
This mutation is currently intended for orders packed outside ShipHero. It creates the packing configuration directly from the provided layout without going through the standard warehouse picking and packing workflows. It does not validate against staged inventory, assign lots, or create LPNs. If your order is being packed through the ShipHero warehouse UI, use the standard packing flow instead.
Prerequisites
- The wholesale order must be in packing status.
- The order must not have an existing packing configuration. If one already exists (even partially), the import will be rejected. Reset the order’s packing state before re-importing.
Mutation
mutation {
wholesale_order_import_packing_layout(data: {
order_id: "12345"
packing_data: "{ ... JSON string ... }"
}) {
request_id
complexity
wholesale_order {
id
}
}
}The packing_data field accepts a JSON string describing the entire container hierarchy.
JSON Structure
The top-level object must contain a containers array. Each entry in the array is a top-level container.
{
"containers": [
{ "...container object..." },
{ "...container object..." }
]
}Container Types
Every container object must include a type field and a details field. The supported types are:
| Type | Description | Can be top-level? | Can be nested? |
|---|---|---|---|
Pallet | A regular pallet. Contains packages, case packs, and/or loose line items. | ✅ Yes | ❌ No |
UOM Pallet | A pallet that represents a single unit-of-measure product case (e.g., a full pallet of one SKU). | ✅ Yes | ❌ No |
Package | A box or carton. Can contain case packs, line items, and/or nested inner packages. | ✅ Yes | ✅ Yes |
CasePack | A sealed product case (e.g., a master carton of 12 units). Leaf node — cannot contain children. | ✅ Yes | ✅ Yes |
Shipping option constraints
- Freight orders: Only
PalletandUOM Palletare allowed at the top level. Packages and CasePacks must be nested inside a pallet. - Courier orders:
PalletandUOM Palletare not allowed at any level. UsePackageandCasePackas top-level containers.
Nesting rules
- Pallets and UOM Pallets can only appear at the top level. They cannot be placed inside another container.
- Packages can be nested inside other packages or inside pallets, up to a maximum depth:
- Freight: 3 levels (e.g., Pallet → Package → Package)
- Courier: 2 levels (e.g., Package → Package)
- CasePacks can appear inside a pallet, inside a package, or at the top level (courier orders). They are always leaf nodes and do not count toward the nesting depth.
Container Fields
Pallet
{
"type": "Pallet",
"details": {
"height": 48.0,
"length": 40.0,
"width": 48.0,
"weight_in_oz": 1600.0,
"sscc_barcode": "PALLET001",
"shipping_box_id": "optional-box-id",
"container_number": 1
},
"line_items": [ ],
"containers": [ ]
}| Field | Type | Required | Description |
|---|---|---|---|
details.height | float | Yes | Height in inches. Must be greater than 0. |
details.length | float | Yes | Length in inches. Must be greater than 0. |
details.width | float | Yes | Width in inches. Must be greater than 0. |
details.weight_in_oz | float | Yes | Tare weight in ounces (weight of the pallet itself, excluding contents). Must be greater than 0. |
details.sscc_barcode | string | No | SSCC or other barcode identifier for the pallet. Max 30 characters. |
details.shipping_box_id | string | No | Reference to a predefined pallet/shipping box type. Max 128 characters. |
details.container_number | int | Yes | Sequential number for this pallet (see Container Numbering) |
line_items | array | No | Loose items packed directly on the pallet (see Line Items) |
containers | array | No | Nested containers: Packages and/or CasePacks inside this pallet |
Package
{
"type": "Package",
"details": {
"height": 16.0,
"length": 12.0,
"width": 10.0,
"weight_in_oz": 320.0,
"sscc_barcode": "PKG001",
"shipping_box_id": "optional-box-id",
"container_number": 1
},
"line_items": [ ],
"containers": [ ]
}| Field | Type | Required | Description |
|---|---|---|---|
details.height | float | Yes | Height in inches. Must be greater than 0. |
details.length | float | Yes | Length in inches. Must be greater than 0. |
details.width | float | Yes | Width in inches. Must be greater than 0. |
details.weight_in_oz | float | Yes | Tare weight in ounces. Must be greater than 0. |
details.sscc_barcode | string | No | SSCC or other barcode identifier. Max 30 characters. |
details.shipping_box_id | string | No | Reference to a predefined shipping box type. Max 128 characters. |
details.container_number | int | Yes | Sequential number for this package (see Container Numbering) |
line_items | array | No | Items packed inside this box (see Line Items) |
containers | array | No | Nested containers: inner Packages and/or CasePacks |
CasePack
{
"type": "CasePack",
"details": {
"height": 12.0,
"length": 10.0,
"width": 8.0,
"weight_in_oz": 160.0,
"sscc_barcode": "CASE001",
"container_number": 1
},
"order_line_item_id": 98765,
"case_sku": "CASE-SKU-001",
"quantity": 1
}| Field | Type | Required | Description |
|---|---|---|---|
order_line_item_id | int | Yes | The order line item ID this case pack fulfills |
case_sku | string | Yes | The SKU of the product case definition. Must exist in your account’s product cases and must belong to the same product family as the order line item (same base product). |
quantity | int | Yes | Number of cases (must be ≥ 1). The total quantity must convert evenly to the order line item’s unit of measure. See CasePack Quantity Rules for when this can be greater than 1. |
details.height | float | Yes | Height in inches. Must be greater than 0. |
details.length | float | Yes | Length in inches. Must be greater than 0. |
details.width | float | Yes | Width in inches. Must be greater than 0. |
details.weight_in_oz | float | Yes | Weight in ounces. Must be greater than 0. |
details.sscc_barcode | string | No | SSCC or other barcode identifier. Max 30 characters. |
details.container_number | int | Yes | Sequential number for this case pack (see Container Numbering) |
Note
The product case definition linked to case_sku already stores canonical dimensions in ShipHero. The import requires dimensions in the payload and stores them as provided. For consistency with the UI packing flow (which always uses product case master data), we recommend passing values that match your product case definition.
UOM Pallet
{
"type": "UOM Pallet",
"details": {
"height": 60.0,
"length": 48.0,
"width": 40.0,
"weight_in_oz": 2400.0,
"sscc_barcode": "UOMPALLET001",
"container_number": 2
},
"order_line_item_id": 98766,
"case_sku": "PALLET-SKU-001"
}| Field | Type | Required | Description |
|---|---|---|---|
order_line_item_id | int | Yes | The order line item ID this UOM pallet fulfills |
case_sku | string | Yes | The SKU of the pallet-level product case definition. Must exist in your account, must have container type PALLET, and must belong to the same product family as the order line item. |
details.height | float | Yes | Height in inches. Must be greater than 0. |
details.length | float | Yes | Length in inches. Must be greater than 0. |
details.width | float | Yes | Width in inches. Must be greater than 0. |
details.weight_in_oz | float | Yes | Weight in ounces. Must be greater than 0. |
details.sscc_barcode | string | No | SSCC or other barcode identifier. Max 30 characters. |
details.container_number | int | Yes | Sequential number — shares the same sequence as regular Pallets (see Container Numbering) |
Note
UOM Pallets have a linked product case definition that stores canonical dimensions. In the current version the import uses the dimensions you provide in the payload. For consistency with the UI packing flow, we recommend passing values that match your product case definition. In a future iteration, the system may enforce or default to the product case master data dimensions.
Line Items
Line items represent individual product units (eaches) packed directly inside a Package or on a Pallet.
{
"order_line_item_id": 12345,
"quantity": 10
}| Field | Type | Required | Description |
|---|---|---|---|
order_line_item_id | int | Yes | The order line item ID being packed |
quantity | int | Yes | Number of units packed in this container (must be ≥ 1) |
Line items can only appear inside a Package or directly on a Pallet. They cannot appear inside a CasePack or UOM Pallet.
Important
Line items represent loose eaches only. If the order line item is in cases (e.g., six-packs), use CasePack containers instead.
Container Numbering
Container numbers are sequential identifiers used for label generation, SSCC-18 codes, packing lists, and ASN transmissions. There are three independent sequences:
| Sequence | Shared by |
|---|---|
| Pallet sequence | All Pallet and UOM Pallet entries (they share one counter) |
| Package sequence | All Package entries, regardless of nesting depth |
| CasePack sequence | All CasePack entries, regardless of parent container |
Rules:
- Each sequence must start at 1 and increment by 1 with no gaps or duplicates.
- Regular pallets and UOM pallets count together. If you have 2 pallets and 1 UOM pallet, their container numbers must be 1, 2, 3.
- All packages across the entire hierarchy share one sequence. A top-level package and a deeply nested inner box both draw from the same counter. If you have 3 packages total (at any depth), they must be numbered 1, 2, 3.
- All case packs share one sequence, regardless of which pallet or package they sit inside.
Example with correct numbering:
Pallet (container_number: 1) ← pallet sequence
├── CasePack (container_number: 1) ← case pack sequence
├── Package (container_number: 1) ← package sequence
│ ├── LineItem
│ └── Package (container_number: 2) ← package sequence (continues)
│ └── LineItem
UOM Pallet (container_number: 2) ← pallet sequence (continues)CasePack Quantity Rules
The quantity field on a CasePack entry behaves differently depending on whether the CasePack is nested inside a parent container or top-level (i.e., a standalone shippable carton).
Nested CasePacks (inside a Package or Pallet)
When a CasePack is inside a Package or Pallet, the parent container is the shippable unit that receives a shipping label. In this case, quantity can be greater than 1 — a single entry with quantity: 3 means 3 cases of the same product packed inside that parent container, all sharing the same container_number.
{
"type": "Package",
"details": { "..." },
"containers": [
{
"type": "CasePack",
"details": { "container_number": 1 },
"order_line_item_id": 1001,
"case_sku": "CASE-SKU-A",
"quantity": 3
}
]
}Top-level CasePacks (courier orders)
When a CasePack is at the top level of a courier order, it represents a standalone physical carton that will receive its own shipping label. In this case, quantity must be 1. Each physical carton needs its own entry with its own container_number.
To pack 3 top-level cases of the same product, create 3 separate entries:
{
"containers": [
{
"type": "CasePack",
"details": { "container_number": 1 },
"order_line_item_id": 1001,
"case_sku": "CASE-SKU-A",
"quantity": 1
},
{
"type": "CasePack",
"details": { "container_number": 2 },
"order_line_item_id": 1001,
"case_sku": "CASE-SKU-A",
"quantity": 1
},
{
"type": "CasePack",
"details": { "container_number": 3 },
"order_line_item_id": 1001,
"case_sku": "CASE-SKU-A",
"quantity": 1
}
]
}Note
Top-level CasePacks with quantity > 1 are rejected. Each physical carton shipped separately needs its own entry and shipping label.
Quantity Validation
Every order line item must be accounted for in the packing layout, and the total quantity packed for each must exactly match the ordered quantity. If a line item is omitted entirely or its packed total does not match, the import will be rejected.
The system validates this by summing:
quantityfrom allline_itemsentries referencing that line item ID.- For
CasePackentries:quantity × case_multiplier, where the case multiplier is the number of eaches (ordered units) contained in one case, resolved from the product case hierarchy. - For
UOM Palletentries:1 × case_multiplier. UOM Pallets always represent exactly one physical pallet.
All integer quantity fields (quantity on line items, CasePacks, and container_number) must be ≥ 1. Zero, negative, or non-numeric values will be rejected.
Packing rules by order UOM
| Order line item UOM | Allowed packing methods | Notes |
|---|---|---|
| Eaches | LineItem (loose eaches) and/or CasePack | Can mix eaches and cases in the same layout. |
| Cases (e.g., six-packs) | CasePack with the exact same case SKU as the order line item | Must use the same case definition. Different case sizes within the same product family are not supported. |
Warning
These packing rules are not fully enforced by the API in the current version. Following them is the caller’s responsibility — submitting a layout that violates these rules may result in incorrect packing data. A future update will add server-side enforcement with clear error messages.
Current Limitations
The following features are not yet supported in this first version of the import and will be added in future iterations:
Packed Outside ShipHero Only
This import is designed for orders packed outside ShipHero’s warehouse workflows. It does not validate against staged inventory (order bin allocations) and bypasses the standard picking flow. Do not use it for orders being packed through the ShipHero warehouse UI.
Lot Tracking
The import does not currently accept or assign lot information. If your account uses lot tracking, the imported packing layout will not have lots associated with line items or case packs. This means:
- Lot-tracked products will not show lot assignments on packed items.
- If lot tracking enforcement is required for your workflow, coordinate with ShipHero support before using this endpoint.
LPN (License Plate Number) Assignment
The import does not currently create or assign LPN identifiers to containers. LPNs are normally auto-generated during the UI packing flow and are required for:
- Scanning containers in the warehouse.
- Unpacking and repacking operations.
- Label generation and fulfillment workflows.
If your workflow requires LPN-based scanning after import, this limitation should be considered.
Weight Recalculation
Container weights are taken as-is from the payload. The system does not automatically recalculate container weights based on their contents (as the UI packing flow does). Ensure the weights you provide are accurate.
Partial Imports and Incremental Updates
The import creates the full packing configuration in a single call. You cannot add containers to an existing configuration or import a partial layout. To modify a previously imported layout, reset the order’s packing configuration and re-import the full layout.
Full Examples
Freight Order
A freight order with 4 line items packed across a regular pallet and a UOM pallet:
{
"containers": [
{
"type": "Pallet",
"details": {
"height": 48.0,
"length": 40.0,
"width": 48.0,
"weight_in_oz": 1600.0,
"sscc_barcode": "PALLET001",
"container_number": 1
},
"line_items": [
{ "order_line_item_id": 1001, "quantity": 10 }
],
"containers": [
{
"type": "CasePack",
"details": {
"sscc_barcode": "CASE001",
"container_number": 1
},
"order_line_item_id": 1002,
"case_sku": "CASE-SKU-A",
"quantity": 1
},
{
"type": "Package",
"details": {
"height": 16.0,
"length": 12.0,
"width": 10.0,
"weight_in_oz": 320.0,
"sscc_barcode": "PKG001",
"container_number": 1
},
"line_items": [
{ "order_line_item_id": 1002, "quantity": 2 }
],
"containers": [
{
"type": "Package",
"details": {
"height": 8.0,
"length": 6.0,
"width": 4.0,
"weight_in_oz": 80.0,
"sscc_barcode": "PKG002",
"container_number": 2
},
"line_items": [
{ "order_line_item_id": 1003, "quantity": 15 }
]
}
]
}
]
},
{
"type": "UOM Pallet",
"details": {
"height": 60.0,
"length": 48.0,
"width": 40.0,
"weight_in_oz": 2400.0,
"sscc_barcode": "UOMPALLET001",
"container_number": 2
},
"order_line_item_id": 1004,
"case_sku": "PALLET-SKU-B"
}
]
}Numbering breakdown
- Pallet sequence: Pallet → 1, UOM Pallet → 2
- Package sequence: PKG001 → 1, PKG002 → 2
- CasePack sequence: CASE001 → 1
Courier Order
A courier order with 2 line items — one packed as loose eaches in a box, and one as a standalone case pack. Both Package and CasePack are valid top-level containers for courier orders.
{
"containers": [
{
"type": "Package",
"details": {
"height": 16.0,
"length": 12.0,
"width": 10.0,
"weight_in_oz": 320.0,
"sscc_barcode": "PKG001",
"container_number": 1
},
"line_items": [
{ "order_line_item_id": 2001, "quantity": 5 }
]
},
{
"type": "CasePack",
"details": {
"height": 12.0,
"length": 10.0,
"width": 8.0,
"weight_in_oz": 100.0,
"sscc_barcode": "CASE001",
"container_number": 1
},
"order_line_item_id": 2002,
"case_sku": "CASE-SKU-X",
"quantity": 1
}
]
}Numbering breakdown
- Package sequence: PKG001 → 1
- CasePack sequence: CASE001 → 1
Note that the package and case pack sequences are independent — both start at 1.
Error Responses
The mutation will raise an error with a descriptive message in the following cases:
| Scenario | Error message |
|---|---|
| Order not in packing status | Wholesale order is not in packing status |
| Packing config already exists | Packing configuration already exists for this order... |
| Malformed JSON structure (missing fields, wrong types, extra fields, unknown container type, nested pallets, zero/negative quantities, zero/negative dimensions, oversized strings) | Invalid packing data: ... (includes specific validation details) |
| Freight order with non-pallet at top level | Freight orders must have only Pallets or UOM Pallets at the top level. Found invalid types: ... |
| Courier order with pallets | Courier orders cannot contain Pallets or UOM Pallets. Found at top level: ... |
| Top-level CasePack with quantity > 1 (courier) | Top-level CasePacks must have quantity 1. To pack multiple cases, create separate entries. |
| Nesting too deep (freight) | Maximum nesting depth of 3 exceeded at ... |
| Nesting too deep (courier) | Maximum nesting depth of 2 exceeded at ... |
| Line item not in order | Line item {id} does not belong to order {order_id} |
| Quantity mismatch or missing line item | Line item {sku} quantity mismatch. Expected: {n}, Got: {m} |
| Unknown case SKU | Case SKU {sku} not found |
| Case SKU belongs to different product family | Case SKU {sku} is not compatible with line item SKU {sku} |
| Quantity does not convert evenly between UOMs | Quantity {n} of {case_sku} does not convert evenly to {line_item_sku} |
| Product case not found during creation | Product case not found for SKU: {sku} |
| UOM Pallet with non-PALLET product case | UOM Pallet requires a PALLET product case, but {sku} has container type {type} |
| Line item not found | Line item not found: {id} |