Product

Upsert a product

Creates or updates a single product catalog entry through one endpoint. Omit id to create a new product (Distru assigns and returns its ID); pass an existing product's id to update it. An id that isn't valid, or one that doesn't belong to your company, returns a not-found error. On create you must supply at minimum name, sku, category_id, vendor_id, unit_type_id, unit_price, and inventory_tracking_method; your company settings can make more fields mandatory (for example a required description or a required product photo). name and sku must each be unique within your company.

Updates are sparse: only the fields you send are changed, and any field you omit keeps its stored value. The list- and map-valued fields replace wholesale when you send them, but omitting the field leaves the current value untouched. tags replaces the product's entire tag set when sent — any tag you leave out of the list is removed, and an empty array clears them all; omit tags to leave the current tags unchanged. custom_data likewise replaces the entire custom-field map when sent, and is left untouched when omitted. Menu placement is driven by menu_visibility: omit it to leave the product's current menu placement untouched; send DO_NOT_INCLUDE to remove it from all menus, INCLUDE_IN_ALL to place it on every menu, or INCLUDE_IN_SELECT with menus to set the exact set (that list replaces the product's full menu set — omit a menu to remove it). menus may only be sent alongside menu_visibility: it is required with INCLUDE_IN_SELECT and ignored with the other two modes, so sending menus without menu_visibility, or INCLUDE_IN_SELECT without menus, is rejected with a 400.

Some attributes are locked once established. inventory_tracking_method cannot be changed after it is first set — the only allowed transition is PRODUCT to BATCH. unit_type_id cannot be changed once the product has any inventory. Attempting either returns a validation error.

This endpoint writes only the product's catalog record. It does NOT create, move, reserve, or release inventory (add packages or batches separately), and it does NOT itself push the product to Metrc or BioTrack — products are local catalog data and only their inventory syncs to state traceability. For BioTrack companies the category_id you choose must be compatible with inventory_tracking_method (a PACKAGE-tracked product needs a category tied to a BioTrack inventory type; a non-package one needs a category that is not). Changing menu_visibility or menus changes which DistruCommerce menus and the Order Tracker surface the product. If the product is linked to a connected sales channel (LeafLink, or a POS such as Blaze/Dutchie/Treez), catalog and price edits made here propagate to that channel. The product's bill_of_materials is readable in the response but is NOT settable here — manage it through its own endpoints.

Required permission: products_permissions_create to create a new product, or products_permissions_edit (plus access to the product under team restrictions) to update an existing one.

post/public/v1/products

Request body

idstring

ID for this product. Omit it to create a new product — Distru assigns the ID. Provide an existing product's ID to update that product; an ID that doesn't exist returns a not-found error.

inventory_tracking_method'PACKAGE' | 'BATCH' | 'PRODUCT'

How this product's inventory is tracked. Required on create. Once set it cannot be changed, with the single exception that a PRODUCT-tracked product may later be switched to BATCH. SCREAMING_CASE, one of:• PACKAGE: Inventory is defined by individual packages (the tracking method used for state-compliance/Metrc packages).• PRODUCT: Not grouped in any manner. Inventory is a simple running quantity you add to or remove from as you transact.• BATCH: Grouped into batches that share common traits such as expiration dates and test results.

skustring

Stock Keeping Unit (SKU). Required on create and must be unique within your company.

namestring

Product name. Required on create and must be unique within your company. Cannot contain the characters :, [, or ], or other Metrc-disallowed special characters.

vendor_idstring

ID of the company relationship for the vendor company that supplies this product. This is the company-relationship ID, not the raw company ID. Required on create.

category_idstring

ID of the product's category. Required on create. For BioTrack companies this must be compatible with inventory_tracking_method: a PACKAGE-tracked product needs a category tied to a BioTrack inventory type, a non-package one needs a category that is not.

external_namestring

Customer-facing name shown on DistruCommerce menus and the Order Tracker. Falls back to name when left blank or omitted, so the response's external_name is never null.

unit_type_idstring

ID of the product's unit type. Required on create. Cannot be changed once the product has any inventory.

unit_pricenumber

Sale price of the product per unit, as a decimal. Required on create. Must be non-negative (>= 0).

descriptionstring

Plain-text description of the product. If you send description without description_markdown, the markdown description is overwritten with this plain text. If you send description_markdown without description, the request is rejected — the two must be provided together (or neither). Some companies require a description; those companies reject a create/update that omits both.

description_markdownstring

Markdown-formatted description. Must be provided together with description. Only italic, bold, strikethrough, and links are supported by the display — other markdown may render unpredictably.

upcstring

Universal Product Code (UPC) for this product

subcategory_idstring

ID of the product's subcategory. When provided it must be a child of the category given in category_id.

group_idstring

ID of the product's group.

brand_idstring

ID of the company relationship for the brand company associated with this product. This is the company-relationship ID, not the raw company ID.

quantity_available_threshold_minnumber

Minimum quantity you want to keep in stock. When the product's available quantity dips below this number it is automatically included in scheduled Low Inventory Reports. Optional; omit to set no low threshold. When both thresholds are set, quantity_available_threshold_max must be greater than this value.

quantity_available_threshold_maxnumber

Maximum quantity you want to keep in stock. When the product's available quantity exceeds this number it is automatically included in scheduled Inventory Reports. Optional; omit to set no high threshold. When both thresholds are set, this must be greater than quantity_available_threshold_min.

units_per_casenumber

Number of units in a case of the product. When provided must be greater than 0.

unit_costnumber

Cost of the product per unit, as a decimal. When provided must be non-negative (>= 0).

msrpnumber

The Manufacturer's Suggested Retail Price (MSRP) of the product per unit. If you have POS integrations enabled in Distru, this may be synced to your POS

wholesale_unit_pricenumber

The wholesale price of the product per unit. When provided must be non-negative (>= 0).

treez_wholesale_pricenumber

The Treez wholesale price of the product per unit, as a decimal. When your company has the Treez wholesale price setting enabled, this value is used as the default price on order items for this product. Omit to leave an existing value unchanged; send null to clear it.

is_featuredboolean

Whether the product is featured. Featured products are shown at the top of menus. Defaults to false when omitted on create.

strain_idstring

ID of the strain associated with the product.

owner_idstring

ID of the user considered the owner of the product. Must be a user the authenticated caller is allowed to assign as owner under their team restrictions.

leaflink_product_idinteger

The LeafLink product ID this product is linked to, used to match LeafLink orders to this product. Must be unique within your account — sending a value already linked to another product is rejected. Omit to leave an existing link unchanged; send null to unlink.

is_inactiveboolean

Whether the product is inactive (hidden from normal use). Defaults to false when omitted on create. Can be flipped back to active at any time.

total_cannabinoid_unitstring

Unit for this product's THC/CBD content. One of PERCENT or MG (SCREAMING_CASE). Required whenever total_thc or total_cbd is provided.

total_thcstring

THC content of the product, expressed in the unit given by total_cannabinoid_unit (which must be provided alongside it). Must be non-negative (>= 0); when total_cannabinoid_unit is PERCENT it cannot exceed 100.

total_cbdstring

CBD content of the product, expressed in the unit given by total_cannabinoid_unit (which must be provided alongside it). Must be non-negative (>= 0); when total_cannabinoid_unit is PERCENT it cannot exceed 100.

menu_visibility'DO_NOT_INCLUDE' | 'INCLUDE_IN_ALL' | 'INCLUDE_IN_SELECT'

Controls which menus (if any) the product appears on. Defaults to DO_NOT_INCLUDE when omitted on create; on update, omit it to leave the product's current menu placement untouched. SCREAMING_CASE, one of:• DO_NOT_INCLUDE: The product appears on no menus; any menus you send are ignored.• INCLUDE_IN_ALL: The product appears on every menu in your company; menus is ignored.• INCLUDE_IN_SELECT: The product appears only on the menus listed in menus, which fully replaces its current menu set. menus is required in this mode. Required whenever menus is provided — sending menus without menu_visibility is rejected.

unit_net_weightnumber

Net weight/volume of the product per unit. When provided must be greater than 0 and requires unit_net_weight_and_serving_size_unit_type_id to be set.

unit_serving_sizenumber

Serving size of the product per unit. When provided must be greater than 0, cannot exceed unit_net_weight, and requires unit_net_weight_and_serving_size_unit_type_id to be set.

unit_net_weight_and_serving_size_unit_type_idstring

ID of the unit type that unit_net_weight and unit_serving_size are measured in. Required whenever either of those is set. Applies only to count-based products; leave null otherwise. Once set, changing the product away from a count/'Unit' category is rejected.

gross_weightnumber

Gross weight of the product. When provided must be greater than 0 and must be set together with gross_weight_unit_type_id (both present or both absent).

gross_weight_unit_type_idstring

ID of the weight unit type gross_weight is measured in. Must be a weight-based unit type that Metrc supports, and must be set together with gross_weight (both present or both absent).

tagsstring[]

Tag IDs to associate with the product. When sent, the list replaces the product's complete tag set — any tag left out is removed, and an empty array clears all tags. Omit tags on update to leave the current tags unchanged.

menusstring[]

Menu IDs the product should appear on. Only used when menu_visibility is INCLUDE_IN_SELECT — required in that mode, ignored with DO_NOT_INCLUDE and INCLUDE_IN_ALL. When applied, this list becomes the product's complete menu set, so omit a menu from the list to remove the product from it. Must be sent together with menu_visibility: sending menus without menu_visibility is rejected, as is INCLUDE_IN_SELECT without menus. Omitting menu_visibility entirely leaves the current menu placement untouched.

custom_dataobject

A map of custom field IDs to their values. Use GET /public/v1/custom-fields?parent_object=product to retrieve available custom fields, their IDs, and their types. The value format depends on the field's type: a text field takes a string, a date field takes a full ISO8601 datetime, and a checkbox field takes an array of its selected options. On update, omit custom_data to leave the stored map unchanged; when sent it replaces the entire map, so include every field you want to keep.

Response

A single product

Changes

No recorded changes to this endpoint across all 1 revision of this API.