Skip to main content

Create Product

POST 

/api/products

Create a new product.

Required scope: products:write

Grant this scope to your token under Settings → Developer → Personal Access Tokens.

Authentication: Requires Bearer token.

Required fields: sku (unique), type Product types: standard, bundle, kit, matrix Weight units: kg, lb, oz, g Dimension units: cm, in, mm, ft

For bundle/kit products, components[] is required and must contain at least one component (each entry has id and quantity). Saving a bundle or kit with an empty components array (or without providing one when creating, or when changing type to bundle/kit) returns a 422 validation error on the components field. For matrix products (parent), include variations[] with sku and attributes.

Optional field: mpn (string, max 255, nullable) — manufacturer part number.

Optional field: hts_code (string, max 255, nullable) — Harmonized Tariff Schedule code.

Optional field: product_type_id (integer, nullable) — the product's product type (Organization → Product Types); must be an active leaf type. It decides which channel category the product publishes into on each sales channel. Send null to clear it.

Returns 422 validation error if SKU already exists or other validation fails.

Note (pricing tiers): When sending suppliers[].pricing[] entries with operation: updateOrCreate (or omitted), price is required and must be a numeric value >= 0 and < 100000. To remove a tier, send operation: "delete". Sending price: null will return a 422 validation error on suppliers.{i}.pricing.{j}.price.

Lot tracking fields (optional):

  • is_lot_tracked (boolean, nullable) — enable batch/lot tracking for this product.
  • lot_tracking_method (string, nullable) — required when is_lot_tracked is true. Allowed values: fifo (First-In, First-Out), fefo (First-Expiry, First-Out).
  • When is_lot_tracked is omitted (or null), the product follows the tenant default from the Serial & Lot Tracking settings (lot_tracking_default_enabled / default_lot_tracking_method). An explicit true or false always wins.

Stock unit of measure (optional):

  • stock_uom_id (integer, nullable) — id of a row in units_of_measure. When omitted, the product is auto-assigned EACH as its stock UoM on create. Pick KG, L, etc. for bulk weight or volume products so quantities, purchase orders, sales orders, and stock takes use the correct unit from day one.

Requires permission: products.create

Also accepts expiration_offset_months (integer 0-120, nullable) — the product's default FBA-inbound shelf-life estimate in months; blank uses the app default (10). Responses include it too.

allow_negative_inventory (boolean|null, optional): whether this product may ship when it would take a warehouse below zero on hand. Deliberately one-way — false pins the product to refusing, whatever the warehouse or company setting says, and is the way to protect an item that must never oversell. true grants nothing on its own: a product cannot unlock permission the warehouse or company has not already given. Omit or send null to inherit.

Serial format. serial_format_override, when set, must include a sequence token, {seq} or {seq:N}, so every generated serial is different. A format without one fails with 422 on serial_format_override.

B2B Portal ordering rules (optional; honoured by the buyer portal's catalog, cart and inventory feed):

  • portal_visible (boolean) — show the product on the portal. Default true.
  • min_order_quantity (integer >= 1, nullable) — product-level MOQ. null = no minimum.
  • order_increment (integer >= 1, nullable) — buyers order in multiples of this. null falls back to case_quantity, else any quantity.
  • allow_backorder (boolean, nullable) — per-product backorder override. null inherits the account's, then the portal's default.
  • portal_lead_time_days (integer >= 0, nullable) — lead time shown to buyers when the product is out of stock.
  • portal_exclude_from_order_discount (boolean) — net price: the product still counts toward an account's order-value discount threshold ("spend $X, take Y% off") but never takes the discount. Default false.

Pricing tier must be one the supplier uses: each suppliers[].pricing[].supplier_pricing_tier_id must be a tier attached to that supplier (or, when the supplier has no tiers attached, the account's default tier). Otherwise the request fails with 422 on suppliers.{i}.pricing.{j}.supplier_pricing_tier_id, because the price would never show on the supplier. Attach the tier to the supplier first, or choose one of the supplier's tiers. Entries sent with operation: "delete" are always accepted, so a price saved on such a tier earlier can still be removed.

Request​

Responses​

Created

Response Headers
    Content-Type