Language
Products are structured records made up of fields defined by a product type. Each product has one or more variants (SKUs) which inherit the product's field values and can override them.
LingoJS uses a query object to list and filter products. Build the query, then call fetch to retrieve a page of results, or fetchAll to page through every match.
Typescript
const { products, total } = await lingo
.products()
.ofType("<productTypeUuid>")
.includeVariants()
.sortBy("name")
.limit(100)
.fetch();
Query Options
ofType
string
Only products of the given product type uuid.
status
string
`active` (default) or `archived`.
includeVariants
boolean
Include each product's variants (default false).
sortBy
string, boolean
Sort by `name`, `product_type`, `date_added`, `date_updated`, `options`, `variants` or `markets`. Pass `true` as the second argument to reverse.
page
number
The page to fetch (default 1).
limit
number
Products per page, up to 200 (default 50).
nextPage
Advance to the next page before calling `fetch` again.
Typescript
const products = await lingo.products().ofType("<productTypeUuid>").fetchAll();
A utility to fetch every product matching a query. The API limits each page to 200 products; fetchAll pages through them and returns the full list.
A product matches when the product itself or any of its variants matches every filter. When name or field filters are used, each product gains a matched_variants list with the uuid and option_values of the variants that caused the match. Field filters take the field's uuid, which you can find on the product type's fields.
Typescript
const { products } = await lingo
.products()
.matchingName("jacket")
.inMarket("<marketUuid>")
.whereText("<colorFieldUuid>", "contains", "Forest")
.whereSelect("<sizeFieldUuid>", "in", ["<optionUuid>"])
.fetch();
for (const product of products) {
// The variants that caused the match, when filtering by name or field
console.log(product.matchedVariants);
}
Filters
matchingName
string
Product or variant name contains the value.
inMarket
string | null
Products in a market, or with no market when `null` is passed.
whereText
fieldUuid, operator, value
Operators: `contains`, `is_set`, `is_empty`.
whereNumber
fieldUuid, operator, value, value2
Operators: `eq`, `gt`, `gte`, `lt`, `lte`, `between` (uses `value2`), `is_set`, `is_empty`. Send numbers as strings.
whereDate
fieldUuid, operator, value, value2
Operators: `eq`, `before`, `after`, `between` (uses `value2`), `is_set`, `is_empty`. Dates are `yyyy-mm-dd`.
whereBoolean
fieldUuid, value
`true`, `false`, `is_set` or `is_empty`.
whereSelect
fieldUuid, operator, optionUuids
Operators: `in`, `is_set`, `is_empty`.
whereMultiselect
fieldUuid, operator, optionUuids, match
Operators: `in`, `is_set`, `is_empty`. `match` is `any` (default) or `all`.
Fetch a single product with its product type, every non-deleted variant (including hidden ones), and the markets it and its variants reference.
Typescript
const product = await lingo.fetchProduct("<productUuid>");
const name = product.fields.product_name.value;
for (const variant of product.variants) {
console.log(variant.optionValues, variant.fields.sku.value);
}
Keys in fields are the product type's field identifiers and keys in optionValues are field names. Unlike other properties, these are returned exactly as defined in Lingo rather than camel cased.
Arguments
uuid
string • required
The uuid of the product.
Product Properties
uuid
string
The unique identifier for the product.
productTypeUuid
string
The product type that defines the product's fields.
name
string
The product's name, copied from its name field.
status
string
`active` or `archived`.
fields
object
Field values keyed by the field's identifier. See Field Values below.
options
object[]
The fields that distinguish variants, in order, as `{ fieldUuid }`.
marketUuid
string | null
The product's market, if any.
sourceUuid
string | null
For a frozen copy in a kit version, the uuid of the live product.
counts
object
Counts of `variants`, `activeVariants`, `options` and `markets`.
productType
ProductType
The product type with its fields, when fetched directly.
variants
Variant[]
Every non-deleted variant, when fetched directly or requested.
previewAsset
Asset
The first image from the product's asset fields, when listing.
Variant Properties
uuid
string
The unique identifier for the variant.
productUuid
string
The product the variant belongs to.
availability
string
`draft`, `hidden`, `coming_soon`, `available` or `retired`.
optionValues
object
The variant's values for the product's option fields, keyed by field name.
fields
object
Field values keyed by identifier, each with a `source` of `override` or `inherited`.
marketUuid
string | null
A market that overrides the product's market.
computedMarketUuid
string | null
The variant's market, falling back to the product's.
Every entry in fields is keyed by the field's identifier and carries a source. On products source is always null. On variants it is override when the variant holds its own value and inherited when the value comes from the product. The rest of the entry depends on the field type.
Field type
Shape
text, name, description, sku, date, boolean
{ "value": ..., "source": ... }
number, price
{ "value": "22.50", "source": ... }, a fixed-point string using the field's decimals
select
{ "option": { "uuid", "value", "label" }, "source": ... }
multiselect
{ "options": [ { "uuid", "value", "label" } ], "source": ... }
asset, images
{ "assets": [ { "asset_uuid", "display_order", "inherited", "asset" } ], "source": ... }
Keys inside a value, such as
asset_uuid, are camel cased by LingoJS (assetUuid). Only the top level keys offieldsandoptionValuesare left as defined.
Product types define the fields a product can have. Each field has an identifier used as the key in product and variant fields, and select fields carry their options.
Typescript
const { productTypes, total } = await lingo.fetchProductTypes();
const productType = await lingo.fetchProductType("<productTypeUuid>");
const colorField = productType.fields.find(f => f.identifier === "color");
fetchProductTypes Options
page
number
The page to fetch (default 1).
limit
number
Product types per page, up to 200 (default 50).
sort
string
`name`, `date_added`, `fields` or `products`. Prefix with `-` to reverse.
Product Type Properties
uuid
string
The unique identifier for the product type.
name
string
The name of the product type.
description
string
A description, if any.
fields
ProductTypeField[]
The field definitions.
counts
object
Counts of `fields` and `products`.
Product Type Field Properties
uuid
string
The unique identifier for the field.
name
string
The display name of the field.
identifier
string
The slug used as the key in product and variant `fields`.
fieldType
string
`text`, `number`, `date`, `select`, `multiselect`, `boolean`, `name`, `description`, `sku`, `price`, `asset`, `images`, or a cannabis type: `cannabis_subcategory`, `phenotype`, `taste`, `mood`, `ingredients` or `terpenes`.
required
boolean
Whether products must provide a value.
displayOrder
number
A relative order of the field.
options
object[]
For select fields, the choices as `{ uuid, value, label, displayOrder }`.
config
object
Type specific settings, such as `decimals` for number fields.
Creating and updating content requires an API token with write access enabled.
Create a product type, optionally with its fields. Fields can also be added later.
Typescript
const productType = await lingo.createProductType({
name: "Apparel",
description: "Clothing and outerwear",
fields: [
{ name: "Name", fieldType: "name", required: true },
{ name: "SKU", fieldType: "sku" },
{ name: "Price", fieldType: "price", config: { currency: "USD" } },
{ name: "Size", fieldType: "select", options: ["S", "M", { value: "l", label: "Large" }] },
],
});
Arguments
name
string • required
The product type's name. Names are unique within a space.
description
string
A description.
fields
ProductTypeFieldData[]
The fields to create, in display order. See Writing Fields below.
A field is written with the properties below. The identifier is generated from the name when left out, and a field's type can't change once it exists.
name
string • required
The display name, unique within the type.
fieldType
string
One of the field types listed above. Required when creating a field.
identifier
string
Lowercase letters, digits and underscores, starting with a letter.
required
boolean
Whether products must provide a value (default false).
displayOrder
number
Position among the type's fields. Appended when left out.
options
(string | object)[]
For `select` and `multiselect`: the choices as `"S"` or `{ value, label }`. Pass `uuid` to keep an existing choice.
config
object
Type specific settings, listed below.
The special types name, description, sku, price, images and each cannabis type may appear once per product type. Cannabis types come with preset choices and don't accept options.
Field type
Config keys
text
`multiline` (boolean)
number
`number_type` (`default`, `currency` or `percentage`), `decimals` (0 to 10, default 2), `thousands_separator` (boolean), `currency` (ISO code, for `currency`)
price
`currency` (ISO code), `decimals`, `thousands_separator`
date
`date_format` (`YYYY-MM-DD`, `MM/DD/YYYY` or `DD/MM/YYYY`), `include_time` (boolean), `time_format` (`12h` or `24h`)
asset, images
`max_assets` (number), `asset_types` (string[])
cannabis_subcategory
`parent_category`, which limits the choices to one category
LingoJS converts config keys for you, so
thousandsSeparatorandthousands_separatorboth work.
Creating and updating content requires an API token with write access enabled.
Update a product type. Like products, it is saved as a whole: fields is the complete list, so leaving a field out deletes it along with its values on every product. Existing fields are matched by uuid and can be renamed, reordered, made required, or given new choices; fields without a uuid are created. A fetched product type's fields can be sent back unchanged.
Typescript
const productType = await lingo.fetchProductType("<productTypeUuid>");
const size = productType.fields.find(f => f.identifier === "size");
await lingo.updateProductType(productType.uuid, {
fields: [
...productType.fields.filter(f => f !== size),
{ ...size, options: [...size.options, "XL"] },
{ name: "Weight", fieldType: "number", config: { decimals: 1 } },
],
});
Arguments
uuid
string • required
The uuid of the product type.
data.name
string
A new name, unique within the space.
data.description
string | null
A new description. Pass `null` to clear it.
data.fields
ProductTypeFieldData[]
The complete field list. Left out, the fields are untouched.
Some changes are refused to protect existing products: a field's field_type can't change, a select choice that any variant is built on can't be removed, and a field that drives variants on any product can't be deleted until it is removed as an option there.
Creating and updating content requires an API token with write access enabled.
Delete a product type. The request fails with invalid_params while any active or archived product uses the type.
Typescript
await lingo.deleteProductType("<productTypeUuid>");
Creating and updating content requires an API token with write access enabled.
Fields can also be added, changed and deleted one at a time. The properties are the same as in Writing Fields above.
Typescript
const field = await lingo.createProductTypeField("<productTypeUuid>", {
name: "Weight",
fieldType: "number",
config: { decimals: 1 },
});
await lingo.updateProductTypeField("<productTypeUuid>", field.uuid, { required: true });
await lingo.deleteProductTypeField("<productTypeUuid>", field.uuid);
createProductTypeField and updateProductTypeField return the field; deleteProductTypeField returns the product type without it.
A market is a region or channel grouping that products and variants can be assigned to. Products and variants reference markets by their uuid.
Markets are only available on the Business and Enterprise plans.
Typescript
const { markets, total } = await lingo.fetchMarkets();
Options
page
number
The page to fetch (default 1).
limit
number
Markets per page, up to 200 (default 50).
sort
string
`name`, `notes`, `date_added` or `products`. Prefix with `-` to reverse.
Market Properties
uuid
string
The unique identifier for the market.
name
string
The name of the market.
notes
string | null
Notes about the market, if any.
counts
object
Count of `products` in the market.
Creating and updating content requires an API token with write access enabled.
Typescript
const market = await lingo.createMarket({ name: "North America", notes: "USD pricing" });
Arguments
name
string • required
The market's name. Names are unique within a space.
notes
string
Notes about the market.
Creating and updating content requires an API token with write access enabled.
Change a market's name or notes. Properties left out are unchanged.
Typescript
const market = await lingo.updateMarket("<marketUuid>", { name: "Americas", notes: null });
Arguments
uuid
string • required
The uuid of the market.
data.name
string
A new name, unique within the space.
data.notes
string | null
New notes. Pass `null` to clear them.
Creating and updating content requires an API token with write access enabled.
Delete a market. A market can't be deleted while any product or variant is assigned to it; the request fails with invalid_params until those assignments are removed.
Typescript
await lingo.deleteMarket("<marketUuid>");
Arguments
uuid
string • required
The uuid of the market.
Creating and updating content requires an API token with write access enabled.
Create a product. Every product gets a base variant, which inherits all of the product's values.
Typescript
const product = await lingo.createProduct({
productTypeUuid: "<productTypeUuid>",
fields: {
product_name: "Trail Jacket",
color: "Forest",
images: [{ assetUuid: "<assetUuid>" }],
},
marketUuid: "<marketUuid>",
});
Arguments
productTypeUuid
string • required
The product type that defines the product's fields.
fields
object • required
Values keyed by field identifier (or uuid). See Writing Field Values below.
marketUuid
string
A market to assign the product to.
Field values can be sent in the shape they are returned in, or as bare values. Send an empty string for a text or number field, or an empty array for an asset field, to store an explicit empty value. Required fields must always have a value.
Field type
Accepted values
text, name, description, sku
"Forest" or { "value": "Forest" }
number, price
"22.50" (a string keeps the exact decimals) or { "value": "22.50" }
date
"2026-03-02"
boolean
true
select
"m" (the option's value) or { "option": { "value": "m" } }
multiselect
["s", "m"] or { "options": [{ "value": "s" }, { "value": "m" }] }
asset, images
[{ "asset_uuid": "..." }] or { "assets": [{ "asset_uuid": "..." }] }
In LingoJS, asset entries take
assetUuidand are converted for you.
Creating and updating content requires an API token with write access enabled.
Update a product. Products are saved as whole objects: each property you include replaces that part of the product entirely, and properties you leave out are untouched.
Typescript
const product = await lingo.updateProduct("<productUuid>", {
fields: { product_name: "Trail Jacket", color: "Forest" },
options: [{ fieldUuid: "<sizeFieldUuid>" }],
variants: [
{ uuid: "<variantUuid>", fields: { size: "s" }, availability: "available" },
{ fields: { size: "m" }, availability: "available" },
],
});
Arguments
uuid
string • required
The uuid of the product.
data.fields
object
The product's values, keyed by field identifier (or uuid). Fields left out are cleared.
data.marketUuid
string | null
The product's market. Pass `null` to clear it. Can't be set while any variant has its own market.
data.status
string
`active` or `archived`. Archived products can't be edited until they are made active again.
data.options
object[]
The fields that drive variants, in order, as `{ fieldUuid }`. Every variant must then carry a value for each of these fields.
data.variants
VariantData[]
The complete list of variants. Variants left out are deleted. Reference existing variants by `uuid`. See Variants below.
Creating and updating content requires an API token with write access enabled.
Archive a product. Its available and coming soon variants become retired, and any kit items that place it are moved to the trash. Archived products are kept with their history and can be listed with the archived status. To restore one, update it with a status of active; its variants return to draft. Products are never permanently deleted through the API.
Typescript
const product = await lingo.archiveProduct("<productUuid>");
Arguments
uuid
string • required
The uuid of the product.
Creating and updating content requires an API token with write access enabled.
Variants are created through a product update; the routes below edit or hide one at a time. A variant's fields are its overrides: a value for each of the product's option fields, plus any field whose value differs from the product's. A field left out inherits the product's value. Values marked as inherited in a fetched variant are ignored when sent back, so a fetched variant can be resubmitted unchanged.
Each combination of option values can only exist once per product, and a product always keeps at least one variant. Setting options does not create variants: the client sends each variant it wants, with a value for every option field. Variants are hidden rather than deleted; to remove variants, send a product update whose variants list leaves them out. With more than one option, Lingo treats the grid of combinations as complete: when the product is next opened in the app, any missing combination is offered as a new Draft variant.
Variant Properties
uuid
string
The variant to update when sent in a product update. New variants get a uuid from the API.
fields
object
The variant's overrides, keyed by field identifier (or uuid).
availability
string
`draft` (default), `hidden`, `coming_soon`, `available` or `retired`. Unchanged when left out.
marketUuid
string | null
A market for this variant. Only allowed when the product itself has no market.
displayOrder
number
The variant's position. Unchanged when left out.
Replace a variant. fields is the variant's complete set of overrides, so a field left out reverts to the product's value.
Returns the updated variant.
Typescript
const variant = await lingo.updateVariant("<productUuid>", "<variantUuid>", {
fields: { size: "l", sku: "TJ-L-FOREST" },
availability: "available",
});
Hide a variant. As in Lingo, a hidden variant stays on the product but is left out of default views. This is the same as updating the variant with availability set to hidden.
Returns the product with the variant hidden.
Typescript
const product = await lingo.hideVariant("<productUuid>", "<variantUuid>");