Docs navigation
Home / Docs / Bundles

Bundles

Sell a set of SKUs as one — how bundles are defined, how many you can sell, how they reserve and ship their components, nested bundles, cost rollups, and CSV import/export.

A bundle (kit) is a product you sell as one line but stock as its parts. A “Van Camping Starter Kit” is a bundle of a stove, a fuel can, and a windscreen; the customer buys one SKU, your warehouse picks three.

Bundles are virtual. They never hold stock of their own — everything about them derives from their components.

Bundle or assembly?

BundleAssembly
StockNone — sellable count derives from components liveReal — built ahead via work orders, then stocked as a SKU
At saleComponents are reserved and shipped individuallyThe finished unit is reserved and shipped
Use whenParts ship as they are; nothing is fabricatedLabor or a build step turns parts into a new thing

If you put the parts in a box at pack time, it’s a bundle. If you build it on a bench beforehand, it’s an assembly. A product can be converted between the two from its page.

Defining a bundle

Create a product with type BUNDLE, then on its page use the Bundle Components card: Add Components picks variants from your catalog; each row has a Qty (per kit, minimum 1) you can edit inline; a component can carry a note. Bundle workspace opens a full-page view of the same thing.

A component can itself be a bundle — nesting to any depth. Foundry refuses a component that would create a loop.

How many can you sell?

Every bundle variant carries a buildable quantity: how many complete kits the components on hand support right now.

  • Flatten the bundle to its leaf SKUs, multiplying quantities down through any nested bundles. A leaf used in two branches is counted once with its total per-kit need.
  • For each leaf, floor((available − reserved) ÷ per-kit quantity).
  • The bundle’s buildable count is the smallest of those — the constraining component. The Bundle Components card bolds it.
  • An assembly used as a component contributes its stock plus what it could build.
  • A bundle with no components is sellable at 0.

What a channel receives as the bundle’s quantity is available + buildable − reserved; for a bundle, available is always 0, so that’s buildable minus open holds. The count refreshes whenever a component’s stock or the component list changes, and a nightly reconcile re-checks every kit.

Because a kit can’t be “counted”, the Available to Sell figure on the card is the truth — there is no on-hand number to look for.

Selling a bundle

Bundles list on channels exactly like any other variant: a channel SKU with Listed on and a price. On headless storefronts the storefront price is the channel SKU’s price, seeded from the variant’s price when you list it. Nothing on the channel side knows or cares that the SKU is a kit.

One thing to know: the first push after listing a bundle can send 0 as its quantity; the next inventory push sends the real buildable count.

What happens when a bundle sells

At order time, the order line for the kit places no reservation on the kit SKU. Instead Foundry flattens it to leaves and reserves each component — 1 kit of a bundle needing 2 stoves and 1 windscreen becomes a hold of 2 on the stove and 1 on the windscreen, attributed to that order line. Nested bundles flatten all the way down.

At fulfillment, when the kit line ships, each component hold is drawn down as a SALE movement on that component in the shipping warehouse (primary bin first). The kit’s holds are released only when the whole kit line ships — a partial shipment of other lines leaves the kit’s parts reserved. Dropship components release their hold without a stock movement.

On the pick list, the kit prints as a heading with its components listed underneath — see Fulfillment. The packing slip shows the kit, as the customer expects. Pushed to ShipStation, the kit line carries the price and its components appear as $0 lines “(in KIT-SKU)” that carry the weight.

Orders placed before component-level reservations existed may still hold the kit SKU itself; POST /orders/:id/reservations/expand-kits rewrites those into component holds.

Converting a stocked product into a bundle

If a SKU that used to be stocked becomes a bundle, any on-hand it still has is meaningless — the kit is virtual now. The conversion warns you (kit_stock_will_be_zeroed) and, on confirmation, writes an adjustment that retires that stock, per warehouse and bin, with the reason recorded in the movement history. Nothing is silently dropped, and the components’ stock is untouched.

The data health check on Products flags any bundle still holding warehouse stock, which can only happen for kits converted before this rule existed.

Cost

A bundle’s computed cost is the sum of component cost × quantity, using each component’s own cost — or, for a nested kit or assembly, its computed cost. It updates when a component or its cost changes and is re-checked nightly. When a bundle sells, its line’s cost of goods uses this figure.

CSV import and export

From the Products list menu, Export Bundles CSV and Import Bundles CSV. The file has one row per component:

ColumnMeaning
Bundle SKUThe kit — matched by SKU or Foundry SKU (SKU wins)
Bundle NameInformational; ignored on import
Component SKUThe part — matched the same way
Component NameInformational
QuantityPer-kit quantity, positive whole number
NoteOptional

Headers are auto-detected, so an export re-imports with no mapping. Import is all-or-nothing per kit: any bad row for a kit rejects that kit and leaves it as it was. onConflict chooses skip (leave kits that already have components alone) or replace (the file becomes the kit’s full component list). A kit with no components exports no rows — so a CSV can never empty a kit by omission.

API: GET /bundles/export?variantIds=…, POST /bundles/import, and GET/POST/PUT/DELETE /bundles/:bundleVariantId/components; GET /bundles/:bundleVariantId/availability returns the live buildable calculation with the constraining component.