cimplify
TypeScript SDK

Catalogue

Browse products, variants, categories, collections, bundles, composites, deals, and price quotes. Read-only on the public client; safe to call from any environment.

List products

const r = await client.catalogue.getProducts({
  limit: 24,
  page: 1,
  category: 'cat_beverages',
  search: 'latte',
  featured: true,
  in_stock: true,
  sort_by: 'price',
  sort_order: 'asc',
})

if (!r.ok) {
  console.error(r.error.code, r.error.message)
  return
}

console.log(r.value.items)        // Product[]
console.log(r.value.is_complete)  // boolean: true when fully paginated
console.log(r.value.pagination)   // { total_count, current_page, page_size, total_pages, has_more }

Single product

Both getProductById and getProductBySlug resolve to the same endpoint; the server accepts either a UUID or a slug.

const byId = await client.catalogue.getProductById('prod_xxx')
const bySlug = await client.catalogue.getProductBySlug('studio-tee-natural')

if (bySlug.ok) {
  const product = bySlug.value          // ProductWithDetails
  console.log(product.id, product.name)
  console.log(product.variants)
  console.log(product.add_ons)
  console.log(product.images)
}

Variants

const variants = await client.catalogue.getVariants('prod_xxx')
const single = await client.catalogue.getVariantById('prod_xxx', 'var_yyy')

// Find a variant by axis selections (size: M, color: blue)
const found = await client.catalogue.getVariantByAxisSelections('prod_xxx', {
  size: 'M',
  color: 'blue',
})

if (found.ok && found.value) {
  console.log(found.value.id, found.value.default_price)
}

Categories and collections

const categories = await client.catalogue.getCategories()
const collections = await client.catalogue.getCollections()

// Pull products under a category
const drinks = await client.catalogue.getCategoryProducts('cat_beverages', { limit: 50 })

// Or under a collection
const summer = await client.catalogue.getCollectionProducts('col_summer', { limit: 50 })

Bundles and composites

Bundles are fixed product groupings with a single combined price. Composites are build-your-own: the customer picks one option per component and the price is computed from those selections.

// Bundles
const bundles = await client.catalogue.getBundles()
const bundle = await client.catalogue.getBundleById('bun_xxx')

// Composites
const composites = await client.catalogue.getComposites()
const composite = await client.catalogue.getCompositeById('comp_xxx')

// Price a composite from a set of selections
const price = await client.catalogue.calculateCompositePrice(
  'comp_xxx',
  [
    { component_id: 'comp_base', selected_option_id: 'opt_classic' },
    { component_id: 'comp_milk', selected_option_id: 'opt_oat' },
  ],
  'loc_main',
)

Add-ons

const addOns = await client.catalogue.getAddOns('prod_xxx')
if (addOns.ok) {
  for (const group of addOns.value) {
    console.log(group.name, group.options)
  }
}

Price quotes

Whenever variants, add-ons, bundle picks, or composite picks affect price, fetch a quote first and pass quote_id to cart.addItem. Quotes have an expires_at; refresh if the user dwells.

const quote = await client.catalogue.fetchQuote({
  product_id: 'prod_xxx',
  variant_id: 'var_yyy',
  quantity: 2,
  add_on_option_ids: ['addopt_oat_milk'],
  location_id: 'loc_main',
})

if (!quote.ok) {
  console.error(quote.error.code, quote.error.message)
  return
}

await client.cart.addItem({
  item_id: 'prod_xxx',
  quantity: 2,
  variant_id: 'var_yyy',
  add_on_options: ['addopt_oat_milk'],
  quote_id: quote.value.quote_id,
})

// Refresh an aging quote
const refreshed = await client.catalogue.refreshQuote({
  quote_id: quote.value.quote_id,
  quantity: 3,
})
if (refreshed.ok) {
  console.log(refreshed.value.quote.quote_id)
}

Discounts

Validate a code at the line / subtotal level before applying it to the cart. Order subtotal is passed as a Money string.

const r = await client.catalogue.validateDiscountCode('WELCOME10', '49.99', 'loc_main')
if (r.ok) {
  console.log(r.value.is_valid, r.value.discount_amount)
}

Taxonomy

const tree = await client.catalogue.getTaxonomies()           // top-level
const subtree = await client.catalogue.getTaxonomies('tax_root') // children of a node
const node = await client.catalogue.getTaxonomy('tax_xxx')      // node + children
const path = await client.catalogue.getTaxonomyPath('tax_xxx')  // ancestor chain
const matches = await client.catalogue.searchTaxonomies('coffee', 10)

Method reference

MethodReturns
getProducts(opts?)Result<CatalogueResult<Product>>
getProductById(id)Result<ProductWithDetails>
getProductBySlug(slug)Result<ProductWithDetails>
getVariants(productId)Result<ProductVariant[]>
getVariantById(productId, variantId)Result<ProductVariant>
getVariantByAxisSelections(productId, axes)Result<ProductVariant | null>
getAddOns(productId)Result<AddOn[]>
getCategories()Result<Category[]>
getCategoryProducts(id, opts?)Result<Product[]>
getCollections()Result<Collection[]>
getCollectionProducts(id, opts?)Result<Product[]>
getBundles()Result<BundleSummary[]>
getBundleById(id)Result<Bundle>
getComposites()Result<Composite[]>
getCompositeById(id)Result<Composite>
calculateCompositePrice(id, sels, locId?)Result<CompositePriceResult>
fetchQuote(input, opts?)Result<PriceQuote>
getQuote(id)Result<PriceQuote>
refreshQuote(input)Result<RefreshQuoteResult>
validateDiscountCode(code, subtotal, locId?)Result<DiscountValidation>
getTaxonomies(parentId?)Result<ProductTaxonomy[]>
searchTaxonomies(q, limit?)Result<ProductTaxonomy[]>
search(query, opts?)Result<Product[]>

On this page