Product fields & validation reference
This is a lookup reference for the fields on a product record: the identity fields that name and match it, the validation rules and numeric bounds enforced on create and edit, the behavior flags (sellability, lot-tracking, expiry), the shipping-dimension fields, and the endpoint that supplies the form dropdown values.
For the task of creating or editing a product, see Create a product and Edit a product's details. For what each product type means, see Product types explained.
Before you begin
- Creating a product requires the products.create permission; editing requires products.update. Both also require the products read/write scope. See Product permissions reference.
- Inventory figures (On Hand, Available, FIFO cost) aren't product fields — they're computed from real-time stock. See the Inventory guide for how those are derived.
Identity fields
These fields name a product and let SKU.io and your channels match it.
| Field | Payload key | Stored as | Unique? | Notes |
|---|---|---|---|---|
| SKU | sku | sku | Yes | The canonical reference for the product and its unique key. Required on create. |
| Barcode | barcode | barcode | No | UPC/EAN/GTIN. Free text — duplicates are allowed. |
| MPN | mpn | mpn | No | Manufacturer part number. Free text — duplicates are allowed. |
| Name | name | name | No | Display name. Optional. |
| Brand | brand_name | brand_id | — | Supply the brand name; SKU.io matches an existing brand or creates one, then stores its id. See Manage brands. |
Key points about identity:
- SKU is the identity of the product. It's the value returned as the product's reference and the field the uniqueness guard is built on. Once movements exist, changing a SKU can detach external links (for example some 3PL stock feeds match on the SKU string), so treat it as stable.
- Barcode and MPN aren't unique-constrained. Two products can share a barcode or MPN. Use SKU when you need a guaranteed-unique key.
- Brand is set by name, not id. Passing
brand_nameon create or edit finds-or-creates the brand, so a typo silently creates a new brand rather than erroring.
How search and typeahead rank these fields
Product search (Typesense via Scout) queries SKU, name, barcode, and MPN together, weighted so that SKU matches rank highest by a wide margin, then MPN, with name and barcode tied lowest. Barcode and MPN are only added to the search index when they have a value, so a blank barcode or MPN never dilutes a match. Typeahead pickers additionally push an exact SKU match to the top, then SKU prefix matches, ahead of everything else. Full column and lookup detail is in Products list columns, search & filters reference.
Create & edit validation rules
The create form (POST) and edit form (PUT) share one rule set, with the differences below. Fields not listed are optional and unvalidated beyond their type.
Required fields & uniqueness
| Field | On create (POST) | On edit (PUT) |
|---|---|---|
sku | Required, max:255, must be unique across all products | max:255, unique except this product (so you can save without changing it) |
type | Required, one of the six product types | Optional — omit to keep the existing type |
barcode | Optional, max:255 | Same |
mpn | Optional, max:255 | Same |
name | Optional, max:255 | Same |
brand_name | Optional, max:255 | Same |
The six accepted type values are standard, bundle, kit, matrix, blemished, and manufactured. See Product types explained. Blemished products are created through a dedicated flow — see Create a blemished product.
Numeric bounds
All numeric fields are optional; when supplied they must fall within these bounds. lt means strictly less than.
| Field | Rule |
|---|---|
weight, length, width, height | numeric, lt:1000000 |
case_quantity | numeric, lt:10000 |
case_length, case_width, case_height, case_weight | numeric, lt:10000 |
expiration_offset_months | integer, 0–120 |
target_stock_days | integer, 1–365 |
min_stock_level | integer, min:0 |
unit_cost | numeric, 0 to < 100000 |
default_price | numeric, 0 to < 100000 |
default_supplier_price | numeric, 0 to < 100000 |
pricing.*.price (tier prices) | numeric, < 100000 |
suppliers.*.pricing.*.price | numeric, 0 to < 100000 |
proforma_shipping_cost, proforma_landed_cost_percentage | numeric (no bound) |
Relationship & existence checks
| Field | Rule |
|---|---|
weight_unit, case_weight_unit | one of lb, g, kg, oz |
dimension_unit, case_dimension_unit | one of in, cm, mm |
categories.* | Category must exist and be a leaf — attaching a non-leaf category fails with a non-leaf error. See How product taxonomy works. |
suppliers.*.supplier_sku | Must be unique per supplier — the same supplier SKU can't map to two products for one supplier. See Manage a product's suppliers. |
sales_nominal_code_id, cogs_nominal_code_id | Must exist in nominal codes. See Set a product's accounting. |
default_tax_rate_id | Must exist in tax rates |
attributes.*.id / .name | Attribute must exist (by id) or is matched/created by name |
stock_uom_id | Must exist in units of measure. See Units of measure reference. |
parent_id | Must exist and can't be the product itself; a variation can't itself have a parent (no two-level nesting) |
Bundle & kit component rules
Bundles and kits must always resolve to at least one component:
- Creating a
bundleorkitrequires a non-emptycomponentsarray. - Editing a product's type into a bundle or kit (when it has no existing components) also requires components.
- Any save that submits
components: []for a bundle or kit is rejected — it would wipe the configuration. - Each component needs a valid product
idand aquantityof at least1.
Editing the components of a kit that already has inventory movements is refused (the request returns a 422 validation-style error, not a server error), because the kit's assembled stock is already committed. See Kit & bundle component reference and Build bundles & kits in the Workshop.
Unit defaulting on create
When you omit a unit field on create, it falls back to your account's default weight unit and dimension unit (from General Settings). Units you do submit are respected as-is — a product saved as kg stays kg. Edit behaves the same way: units you send are kept, omitted units are left unchanged.
Sellability & behavior flags
These booleans govern how a product behaves across selling, tax, and POS surfaces. All default appropriately for a normal sellable product unless you change them.
| Flag | Payload key | Effect |
|---|---|---|
| Sellable | is_sellable | When off, the product is treated as non-sellable — for example decoration consumables like thread or ink. Non-sellable products are hidden from channel listing pickers (the integration product-mapping modals request sellable products only). |
| Dropshippable | is_dropshippable | Marks the product as eligible for dropship fulfillment flows. |
| Decoration consumable | is_decoration_consumable | Marks a consumable used in decoration/production. A decoration consumable is the one product kind that's not decorable (it can't itself carry decoration). |
| Taxable | is_taxable + default_tax_rate_id | When taxable, default_tax_rate_id sets the default tax rate applied. |
| Show in register | show_in_register | Controls POS register visibility. On a new product this defaults from the auto-show new products in register product setting (on by default) unless you set it explicitly on the form or an import. |
is_sellable on the product is separate from the Non-Sellable on-hand quantity, which comes from stock held in non-sellable warehouse locations. Product On Hand includes Non-Sellable stock. See the Inventory guide for the stock buckets.
Listing a sellable product to a sales channel is out of scope here — that's covered in the Listings guide. See Products, channels & reporting (seams) for where product data hands off to channels.
Lot-tracking & expiry flags
These flags turn on lot/expiry handling and set its thresholds. The deep behavior — how lots are created, picked, and expired — lives in the Inventory area; this page only documents the fields on the product.
| Flag / field | Payload key | Notes |
|---|---|---|
| Lot tracked | is_lot_tracked | Turns on lot tracking for the product. |
| Lot tracking method | lot_tracking_method | Required when is_lot_tracked is on. One of fifo (first-in, first-out) or fefo (first-expired, first-out). |
| Shelf life (days) | shelf_life_days | Integer, min:1. |
| Near-expiry threshold (days) | near_expiry_threshold_days | Integer, min:1. |
| Min remaining shelf life (days) | min_remaining_shelf_life_days | Integer, min:0. |
| Expiry-aware availability | expiry_aware_availability | When on, availability accounts for expiry. |
| Catch weight enabled | catch_weight_enabled | Variable-weight handling flag (set outside the main form). |
| Manufacturing enabled | manufacturing_enabled | Manufacturing/assembly flag (set outside the main form). |
For lot creation, FEFO/FIFO picking, and expiry availability behavior, see the Inventory guide.
Shipping fields
The product detail Shipping tab holds the physical dimensions used for FBA prep, case packing, and shipping calculations. Editing the tab requires the products.update permission and is subject to the product edit-lock — if another user is editing the product, the Edit button is disabled and names who holds the lock.
Product Dimensions
| Field | Payload key | Unit field |
|---|---|---|
| Weight | weight | weight_unit (lb, g, kg, oz) |
| Length | length | dimension_unit (in, cm, mm) |
| Width | width | dimension_unit |
| Height | height | dimension_unit |
Case Shipping Details
These define how individual units pack into a shipping case, used for bulk and FBA inbound shipments.
| Field | Payload key | Unit field |
|---|---|---|
| Case quantity | case_quantity | Units per case (shown in the product's stock unit of measure) |
| Case weight | case_weight | case_weight_unit (lb, g, kg, oz) |
| Case length / width / height | case_length, case_width, case_height | case_dimension_unit (in, cm, mm) |
| Expiration offset (months) | expiration_offset_months | 0–120; blank falls back to the app default of 10 months, used to pre-fill FBA inbound expiration dates |
| FBA prep instructions | fba_prep_instructions | Free text, max:255 (for example, polybagging, labeling) |
The stock unit of measure that labels Case quantity is configured separately — see Configure a product's units of measure.
Form constants endpoint
The product form loads its dropdown options from a single endpoint rather than hardcoding them, so the values always match the back end enums.
GET /products/constants returns:
| Key | Contents |
|---|---|
product_types | standard, bundle, kit, matrix, blemished, manufactured |
weight_units | lb, g, kg, oz |
dimension_units | in, cm, mm |
attribute_types | Supported attribute value types — see Attribute fields & types reference |
warehouse_types | Warehouse type enum |
inventory_movement_type | Inventory movement type enum |
inventory_movement_status | Inventory movement status enum |
The endpoint requires the products read/write scope. It's read-only and carries no per-field permission of its own.
Next steps
- Create a product — the create task, end to end.
- Edit a product's details — editing fields and tabs.
- Product detail page reference — every tab and stat on the detail page.
- Product permissions reference — who can create, edit, and delete.
- Importable product columns reference — the same fields as spreadsheet columns.