Docs & Guides
Multi-stage Packing
14 min
paccurate’s packing algorithm can be used in any stage of the fulfillment process putting items into cartons, placing cartons onto pallets, and even pallets onto trailers in the past, we have encouraged customers to chain api requests together using the boxes from a cartonization response as items in a palletization or trailer filling second request we often advised a similar approach when certain items had to packed together before other items could be added to the cartons after the high priority items were initially packed — essentially as a more aggressive sequencesort when the use case arose the multi request pack solution is not always feasible given technical constraints, and we decided to find a way to achieve staged pack functionality within one request to pacapi the result of this effort is a new rule, called pack sequence the pack sequence rule takes precedence over any sequence property to define the steps of a pack, such as inners to cases and then cases to pallets the options for pack sequence are the following (also available in our api https //api paccurate io/docs ) index (required) an integer specifying which stage of the pack the operation happens 0 is first, 1 is second, etc key (optional) key for separating item groups which must be sequenced at the same step, so their results are combined and processed together e g , two item groups are packed separately and in parallel, like (index 0, key "refrigerated") and (index 0, key "room temperature"), which then both combine on a mixed pallet (index 1) when reduce is set to 'boxes to items' reduce the action to take between steps options are default (normal packing, done in index order), pack as is (boxes are locked after the sequence grouping has completed), and boxes to items (boxes are remade as subspaces and then packed as items — such as kitting, inner/outer packing, or case → pallet planning) reducedrefid (optional) the new refid for the reduced boxes critical for creating rules for generated items on each step for example, items may not be able to be packed directly on a pallet, but items that have already been put in a carton would be permitted to pack at a later stage of the pack carton to pallet a common use case for this api feature is to generate a pallet (or pallets) based on items in an order in this example, we’ll be configuring our request to take a mixed set of items that will all wind up on a pallet some will need to be put in a carton before being placed on the pallet, while others can ship in their own packaging, so they do not need to be over boxed before being palletized we’ll start by setting up a couple of box options; a carton and a pallet to keep things simple { 	"boxtypes" \[ 	 { 	 "refid" 200, 	 "weightmax" 1500, 	 "name" "pallet", 	 "dimensions" { 	 "x" 72, 	 "y" 40, 	 "z" 48 	 } 	 }, 	 { 	 "refid" 100, 	 "weightmax" 150, 	 "name" "box", 	 "dimensions" { 	 "x" 14, 	 "y" 16, 	 "z" 18 	 } 	 } 	 ] } it’s important that the boxes have their own refids — we will be using them to build our exclude rules later (though we can use targetboxmatch if ids need to be shared!) next, lets add some items to be packed { 	"itemsets" \[ 	 { 	 "refid" 1, 	 "quantity" 5, 	 "dimensions" { 	 "z" 10 5, 	 "y" 4 25, 	 "x" 6 	 }, 	 "weight" 3, 	 "name" "parts a", 	 "color" "#1488f5", 	 "sequence" "" 	 }, 	 { 	 "refid" 2, 	 "quantity" 2, 	 "dimensions" { 	 "x" 27, 	 "y" 14 5, 	 "z" 42 	 }, 	 "weight" 24, 	 "name" "bike frame", 	 "color" "#3b6667", 	 "sequence" "sioc" 	 }, 	 { 	 "refid" 3, 	 "quantity" 5, 	 "dimensions" { 	 "x" 11 25, 	 "y" 8, 	 "z" 9 25 	 }, 	 "weight" 0 25, 	 "name" "parts b", 	 "color" "#ffae00" 	 } 	 ] } here we have 3 items 2 are generic parts, the third a large bike frame we’ve added “sioc” to the item’s sequence attribute to denote that it can be shipped in its own container — or in this case, go directly on the pallet time to set up some pack sequence rules! { 	"rules" \[ { "operation" "pack sequence", "itemmatch" { "property" "sequence", "expression" "sioc", "negate" true }, "options" { "index" 0, "comment" "non sioc overboxing", "reduce" "box to item", "reducedrefid" 1000 } } ] } breaking down the rule above we’re defining the operation (rule type) “pack sequence” then defining the items that it applies to using itemmatch , we’re looking at the sequence value, and targeting items that are not “sioc” (aka items that have to be placed on a carton) with the items targeted, we then set the options on the rule "index" 0 this is the first step in the pack sequence "reduce" "box to item" the packed boxes become items in the next pack sequence "reducedrefid" 1000 the new refid for the packed boxes "comment" "non sioc overboxing" adding context for visibility before stitching the items, boxes, and rules together, there is one more important piece we need to consider, which is making sure that in our first pack sequence step, no items are placed on the pallet it’s for cartons only! so how do we create that rule? using the same itemmatch we leveraged for pack sequence , we will also create an exclude rule that looks like this { "operation" "exclude", "itemmatch" { "property" "sequence", "negate" true, "expression" "sioc" }, "targetboxrefids" \[ 200 ], "comment" "only sioc/pre boxed items on pallets" "ignoresubspaces" true } we’ve assigned the exclude operation to the same set of items non sioc then, in the targetboxrefids array, we place the refid for the “pallet” box type and finally, the ignoresubspaces value tells the request to say this exclusion rule doesn’t apply to “subspace” items — in our case, the items that have already been cartonized with the "pack sequence" rule putting all of the pieces together, the request looks like this { "orderid" "staged packing 1", "rules" \[ { "operation" "exclude", "itemmatch" { "property" "sequence", "expression" "sioc", "negate" true }, "targetboxrefids" \[ 200 ], "ignoresubspaces" true }, { "operation" "pack sequence", "itemmatch" { "property" "sequence", "expression" "sioc", "negate" true }, "options" { "index" 0, "comment" "non sioc overboxing", "reduce" "box to item", "reducedrefid" 1000 } } ], "itemsets" \[ { "refid" 1, "quantity" 5, "dimensions" { "z" 10 5, "y" 4 25, "x" 6 }, "weight" 3, "name" "parts a", "color" "#1488f5", "sequence" "" }, { "refid" 2, "quantity" 2, "dimensions" { "x" 27, "y" 14 5, "z" 42 }, "weight" 24, "name" "bike frame", "color" "#3b6667", "sequence" "sioc" }, { "refid" 3, "quantity" 5, "dimensions" { "x" 11 25, "y" 8, "z" 9 25 }, "weight" 0 25, "name" "parts b", "color" "#ffae00" } ], "boxtypes" \[ { "refid" 200, "weightmax" 1500, "name" "pallet", "dimensions" { "x" 72, "y" 40, "z" 48 } }, { "refid" 100, "weightmax" 150, "name" "box", "dimensions" { "x" 14, "y" 16, "z" 18 } } ] } the output packs all 12 items into one outer container, the pallet boxtype pacapi responds with information about the boxes as well, including which items went into each box before the boxes were then placed onto the pallet inner pack / gift boxing / kitting another frequent use case that inspired the pack sequence rule is inner packing, oftentimes presented as gift boxing, kitting, or item bundling the required behavior is make sure qualified items are packaged together first (as a kit or gift box), then pack the kit in a carton that can be shipped the solution to this use case was developed along with our partners at packsize — they had a customer that needed right sized kit boxing that would then be placed into a right sized shipping box this added an additional requirement to consider the outer dimensions of the generated inner kit boxes, in order to make the properly sized outer when the kit is placed with other items let’s construct this example we’ll use an electronic device and its power supply that need to be packed together as a kit item, and then ship the kit along with another item in a shippable box we will use our boxtypegenerator to create the right sized kit, and then use a standard rsc box for shipping let’s start by creating our items { 	"itemsets" \[ 	 { 	 "refid" 1, 	 "quantity" 1, 	 "dimensions" { 	 "z" 10 5, 	 "y" 4 25, 	 "x" 3 	 }, 	 "weight" 3, 	 "name" "device", 	 "color" "#1488f5", 	 "sequence" "kit bundle a" 	 }, 	 { 	 "refid" 2, 	 "quantity" 1, 	 "dimensions" { 	 "x" 2, 	 "y" 3, 	 "z" 4 	 }, 	 "weight" 0 3, 	 "name" "device power", 	 "color" "#3b6667", 	 "sequence" "kit bundle a" 	 }, 	 { 	 "refid" 3, 	 "quantity" 3, 	 "dimensions" { 	 "x" 3, 	 "y" 4, 	 "z" 7 	 }, 	 "weight" 0 25, 	 "name" "accessories", 	 "color" "#ffae00" 	 } 	 ] } in this example, we’re using the sequence attribute to hold our flag for kitting — set to "kit bundle a" this will be used when creating our exclude rules next we will set up our custom kit boxtypegenerator and add a shipping outer box { 	"boxtypegenerators" \[ 	 { 	 "boxtypedefaults" { 	 "name" "promo kit 2026", 	 "refid" 2000, 	 "weightmax" 10, 	 "outer" { 	 "dimensionchange" { 	 "x" 0 33, 	 "y" 0 33, 	 "z" 0 33 	 } 	 } 	 }, 	 "operation" "cartesian", "options" { "xrange" { "min" 2, "max" 10 }, "ylist" \[4, 6, 8, 10], "zlist" \[8, 10, 12] }	 	 } 	], 	"boxtypes" \[ 	 { "refid" 11, "name" "medium box", "weightmax" 50, "dimensions" { "x" 8, "y" 10, "z" 13 } } 	] } worth noting here in the boxtypedefaults for the kit box, we’ve defined an outer behavior that will add 0 33 to the generated dimensions in order to accommodate for the width the cardboard when the kit needs to be packed we’re also using a mix of xrange along with the ylist and zlist to control the footprint of the kit box a bit more now its time to set up the rules for pack sequence and routing the items correctly { "rules" \[ { "operation" "exclude", "itemmatch" { "property" "sequence", "expression" "kit bundle" }, "targetboxmatch" { "property" "name", "expression" "promo kit", "negate"\ true }, "ignoresubspaces" true "comment" "ensure kit items cannot be placed in anything aside from kit box" }, { "operation" "exclude", "itemmatch" { "property" "sequence", "expression" "kit bundle", "negate"\ true }, "targetboxmatch" { "property" "name", "expression" "promo kit" }, "comment" "non kit items are excluded from kit box" }, { "operation" "pack sequence", "itemmatch" { "property" "sequence", "expression" "kit bundle" }, "options" { "index" 0, "comment" "kit items first", "reduce" "box to item", "reducedrefid" 1000 } } ] } there are 3 rules above, and they go as follows exclude the first exclude rule is assigned to items that are kittable (sequence property “kit bundle”) and excludes them from being packed into boxes that are not the “promo kit” box, unless they are part of a subspace (the ignoresubspaces option being set to true means that once kitted, these items can be packed into other boxes) exclude the second exclude rule is set to prevent non kittable items (without “kit bundle” in their sequence) from using the “promo kit” boxtypegenerator pack sequence assigned to items with “kit bundle” in the sequence, this rule will convert the kit box to an item (the "reduce" "box to item" setting) with the refid of 1000 — setting "reducedrefid" 1000 makes this happen view the combined payload of items, boxes, rules, and boxtypegenerators below { "orderid" "staged packing 2", "itemsets" \[ { "refid" 1, "quantity" 1, "dimensions" { "z" 10 5, "y" 4 25, "x" 3 }, "weight" 3, "name" "device", "color" "#1488f5", "sequence" "kit bundle a" }, { "refid" 2, "quantity" 1, "dimensions" { "x" 2, "y" 3, "z" 4 }, "weight" 0 3, "name" "device power", "color" "#3b6667", "sequence" "kit bundle a" }, { "refid" 3, "quantity" 3, "dimensions" { "x" 3, "y" 4, "z" 7 }, "weight" 0 25, "name" "accessories", "color" "#ffae00" } ], "boxtypegenerators" \[ { "boxtypedefaults" { "name" "promo kit 2026", "refid" 2000, "weightmax" 10, "outer" { "dimensionchange" { "x" 0 33, "y" 0 33, "z" 0 33 } } }, "operation" "cartesian", "options" { "xrange" { "min" 2, "max" 10 }, "ylist" \[ 4, 6, 8, 10 ], "zlist" \[ 8, 10, 12 ] } } ], "boxtypes" \[ { "refid" 11, "name" "medium box", "weightmax" 50, "dimensions" { "x" 8, "y" 10, "z" 13 } } ], "rules" \[ { "operation" "exclude", "itemmatch" { "property" "sequence", "expression" "kit bundle" }, "targetboxmatch" { "property" "name", "expression" "promo kit", "negate" true }, "ignoresubspaces" true, "comment" "ensure kit items cannot be placed in anything aside from kit box" }, { "operation" "exclude", "itemmatch" { "property" "sequence", "expression" "kit bundle", "negate" true }, "targetboxmatch" { "property" "name", "expression" "promo kit" }, "comment" "non kit items are excluded from kit box" }, { "operation" "pack sequence", "itemmatch" { "property" "sequence", "expression" "kit bundle" }, "options" { "index" 0, "comment" "kit items first", "reduce" "box to item", "reducedrefid" 1000 } } ] } using our inspector tool, we can see that everything packs into one outer box here is the full example request and response, embedded our api response includes two boxes the promo kit 2026 box as well as the medium box this is important for any automation that will need to generate the inner box, as well as for packers who need to see the contents of the kit box in the response, we can see that the promo kit 2026 box has inner dimensions of 12, 8, and 3 taking a look at the contents of the medium box — we can see that the dimensions of the kit item have in fact used our outer dimension adjustments; as an item its dimensions are 12 33, 8 33, and 3 33 items to box to pallet to truck a more advanced application of pack sequence is an extension of our first example — once items are placed into boxes, and those boxes placed onto pallets, we can take it one step further and place those pallets onto a truck "itemsets" \[ { "refid" 1, "quantity" 600, "dimensions" { "z" 10 5, "y" 4 25, "x" 6 }, "weight" 2 97, "name" "parts", "color" "#1488f5", "sequence" "un" }, { "refid" 2, "quantity" 500, "dimensions" { "x" 17 75, "y" 5, "z" 7 75 }, "weight" 20 5, "name" "more parts", "color" "#27f40b", "sequence" "un" }, { "refid" 3, "quantity" 400, "dimensions" { "x" 0 25, "y" 14 5, "z" 6 5 }, "weight" 14 25, "name" "extra parts", "color" "#f0149f", "sequence" "un" }, { "refid" 99, "quantity" 1, "dimensions" { "x" 0, "y" 0, "z" 0 }, "weight" 0, "name" "step force 1", "color" "#000000" } ] here we have 3 types of parts, along with a virtual item with no dimensions or weight we can set up our rules as we usually do; exclusions for specific box types, plus the pack sequence rules for the parts one additional step in the packing sequence is required to turn the pallets into items to then be placed on a truck, and that is where the virtual item is used { "rules" \[ { "operation" "exclude", "itemmatch" { "property" "sequence", "expression" "un" }, "targetboxrefids" \[ 200, 300 ], "ignoresubspaces" true }, { "operation" "exclude", "itemrefid" 100, "targetboxrefids" \[ 100, 300 ], "ignoresubspaces" true }, { "operation" "exclude", "itemrefid" 200, "targetboxrefids" \[ 100, 200 ], "ignoresubspaces" true }, { "operation" "pack sequence", "itemmatch" { "property" "sequence", "expression" "un" }, "options" { "index" 0, "comment" "box to pallet", "reduce" "box to item", "reducedrefid" 100 } }, { "operation" "pack sequence", "itemmatch" { "property" "name", "expression" "step force 1" }, "options" { "index" 1, "comment" "pallet to truck", "reduce" "box to item", "reducedrefid" 200 } } ] } the two "pack sequence" rules are setup like so the parts ( ”sequence” ”un” ) put the generated boxes onto pallets, much like our first example with the pallets generated, we need to turn them into items so they can be put onto our truck boxtype the “step force 1” virtual item tells the api that we now have another pack sequence at "index" 1 which looks at the pallets output from "index" 0 , and has to pack them somewhere else along with the virtual item these are the boxtypes and generators we use — the additional “exclude” rules in the snippet above are used to route items to their proper box type — parts into boxes, generated boxes onto pallets, then those pallets into the trucks {"boxtypes" \[ { "refid" 100, "weightmax" 150, "name" "box", "dimensions" { "x" 14, "y" 16, "z" 18 } }, { "refid" 300, "weightmax" 44000, "name" "truck", "dimensions" { "x" 102, "y" 102, "z" 630 } } ], "boxtypegenerators" \[ { "boxtypedefaults" { "name" "pallet", "refid" 200, "weightmax" 4000, "weighttare" 35 }, "operation" "cartesian", "options" { "xrange" { "min" 10, "max" 91 5 }, "ylist" \[ 42 ], "zlist" \[ 50 ] } } ] } putting the entire payload together looks like this { "orderid" "staged packing 3", "rules" \[ { "operation" "exclude", "itemmatch" { "property" "sequence", "expression" "un" }, "targetboxrefids" \[ 200, 300 ], "ignoresubspaces" true }, { "operation" "exclude", "itemrefid" 100, "targetboxrefids" \[ 100, 300 ], "ignoresubspaces" true }, { "operation" "exclude", "itemrefid" 200, "targetboxrefids" \[ 100, 200 ], "ignoresubspaces" true }, { "operation" "pack sequence", "itemmatch" { "property" "sequence", "expression" "un" }, "options" { "index" 0, "comment" "box to pallet", "reduce" "box to item", "reducedrefid" 100 } }, { "operation" "pack sequence", "itemmatch" { "property" "name", "expression" "step force 1" }, "options" { "index" 1, "comment" "pallet to truck", "reduce" "box to item", "reducedrefid" 200 } } ], "boxtypegenerators" \[ { "boxtypedefaults" { "name" "pallet", "refid" 200, "weightmax" 4000, "weighttare" 35 }, "operation" "cartesian", "options" { "xrange" { "min" 10, "max" 91 5 }, "ylist" \[ 42 ], "zlist" \[ 50 ] } } ], "itemsets" \[ { "refid" 1, "quantity" 600, "dimensions" { "z" 10 5, "y" 4 25, "x" 6 }, "weight" 2 97, "name" "parts", "color" "#1488f5", "sequence" "un" }, { "refid" 2, "quantity" 500, "dimensions" { "x" 17 75, "y" 5, "z" 7 75 }, "weight" 20 5, "name" "more parts", "color" "#27f40b", "sequence" "un" }, { "refid" 3, "quantity" 400, "dimensions" { "x" 0 25, "y" 14 5, "z" 6 5 }, "weight" 14 25, "name" "extra parts", "color" "#f0149f", "sequence" "un" }, { "refid" 99, "quantity" 1, "dimensions" { "x" 0, "y" 0, "z" 0 }, "weight" 0, "name" "step force 1", "color" "#000000" } ], "boxtypes" \[ { "refid" 100, "weightmax" 150, "name" "box", "dimensions" { "x" 14, "y" 16, "z" 18 } }, { "refid" 300, "weightmax" 44000, "name" "truck", "dimensions" { "x" 102, "y" 102, "z" 630 } } ] } all 1501 items are packed onto a single truck our inspector tool returns the pack as below conclusion pack sequence enables staged packing in a single pacapi request , removing the need to chain multiple requests for inner → outer workflows the rule’s index , key , and reduce options define how stages run and how outputs become inputs , including powerful patterns like boxes to items (kitting, inner/outer) and preserving identities via reducedrefid real world patterns are supported end to end carton → pallet with sioc routing, inner kit boxing with btg and outer dimension adjustments, and multi hop flows like items → box → pallet → truck using a forced step item
