> ## Documentation Index
> Fetch the complete documentation index at: https://docs.heylua.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Products

> The agent's product catalog with pagination, filtering, and semantic search

`Products` manages the agent's product catalog: free-form JSON products keyed by an `id` you choose, listed with pagination and a [Lua Query](/reference/sdk/query) filter, or found by semantic search. Available in tools, jobs, webhooks, triggers, processors, and workflow code steps. A product goes into a [basket](/reference/sdk/baskets) by its `id`.

*Verified against lua-cli 3.33.0.*

```ts theme={null}
import { Products } from 'lua-cli';
```

## Quick example

```ts theme={null}
import { Products } from 'lua-cli';

const product = await Products.create({ id: 'MBP-14', name: 'MacBook Pro 14', price: 1999, inStock: true });
const hits = await Products.search('laptop for video editing');
console.log(product.name, hits.map((p) => p.name));
```

## Methods

### get()

Returns one page of products, optionally filtered.

```ts theme={null}
Products.get(page?: number, limit?: number): Promise<ProductPaginationInstance>
Products.get(options?: { page?: number; limit?: number; filter?: LuaQuery }): Promise<ProductPaginationInstance>
```

<ParamField path="page" type="number" default={1}>Page number, starting at 1.</ParamField>
<ParamField path="limit" type="number" default={10}>Products per page.</ParamField>
<ParamField path="filter" type="LuaQuery">Matches product fields; see [Lua Query](/reference/sdk/query). Options form only.</ParamField>

**Returns**

<ResponseField name="page" type="ProductPaginationInstance">
  An iterable page. It has no `data` property.

  <Expandable title="properties">
    <ResponseField name="products" type="ProductInstance[]">The page's products.</ResponseField>
    <ResponseField name="pagination" type="object">`currentPage`, `totalPages`, `totalCount`, `limit`, `hasNextPage`, `hasPrevPage`, `nextPage`, and `prevPage`.</ResponseField>
    <ResponseField name="length" type="number">Products on this page.</ResponseField>
    <ResponseField name="map, filter, forEach, find, findIndex, some, every, reduce" type="function">Array helpers over `products`; the instance is also iterable with `for...of`.</ResponseField>
    <ResponseField name="nextPage(), prevPage()" type="() => Promise<ProductPaginationInstance>">Fetch the adjacent page with the same `limit`. Throw `No next page` and `No previous page` at the ends.</ResponseField>
  </Expandable>
</ResponseField>

**Example**

```ts theme={null}
import { Products } from 'lua-cli';

const page = await Products.get({ page: 1, limit: 20, filter: { inStock: true, price: { $lte: 500 } } });
for (const product of page) {
  console.log(product.name, product.price);
}
if (page.pagination.hasNextPage) {
  const next = await page.nextPage();
  console.log(next.length);
}
```

**Errors** — `Failed to get products`; `No next page` and `No previous page` from the paging helpers.

### create()

Creates a product, or updates the product that already has the same `id`.

```ts theme={null}
Products.create(product: Product): Promise<ProductInstance>
```

<ParamField path="product" type="{ id: string; [key: string]: any }" required>
  `id` is your identifier (a SKU, a catalog id, a UUID you mint) and the only required field. Everything else is free-form.
</ParamField>

**Returns** — the product as a [`ProductInstance`](#productinstance).

**Example**

```ts theme={null}
import { Products } from 'lua-cli';

const mouse = await Products.create({
  id: 'MOUSE-001',
  name: 'Wireless mouse',
  price: 29.99,
  category: 'Electronics',
  inStock: true,
});
console.log(mouse.id, mouse.price);
```

**Errors** — `Failed to create product` when the platform rejects the write.

### delete()

Deletes a product by id.

```ts theme={null}
Products.delete(id: string): Promise<DeleteProductResponse>
```

**Returns**

<ResponseField name="deleted" type="boolean">`true` when the product was removed.</ResponseField>

**Example**

```ts theme={null}
import { Products } from 'lua-cli';

const { deleted } = await Products.delete('MOUSE-001');
```

**Errors** — `Failed to delete product` when no product has that id.

### search()

Returns up to five products semantically closest to a query.

```ts theme={null}
Products.search(query: string): Promise<ProductSearchInstance>
```

<ParamField path="query" type="string" required>Natural-language query.</ParamField>

**Returns**

<ResponseField name="results" type="ProductSearchInstance">
  An iterable result set with `products`, `length`, and the same array helpers as a page. No `pagination`, no `data`, and no `limit` argument: the platform returns at most five matches.
</ResponseField>

**Example**

```ts theme={null}
import { Products } from 'lua-cli';

const results = await Products.search('wireless headphones');
const affordable = results.filter((p) => p.price < 100);
console.log(results.length, affordable.map((p) => p.name));
```

**Errors** — `Failed to search products`.

### getById()

Returns one product by id.

```ts theme={null}
Products.getById(id: string): Promise<ProductInstance>
```

**Returns** — the product as a [`ProductInstance`](#productinstance). There is no `null` return: an unknown id throws.

**Example**

```ts theme={null}
import { Products } from 'lua-cli';

let product;
try {
  product = await Products.getById('MBP-14');
} catch {
  throw new Error('Product not found: MBP-14');
}
console.log(product.name);
```

**Errors** — `Failed to get product` when no product has that id.

## ProductInstance

The object `create()`, `getById()`, and the page and search results hold. Product fields are readable and writable directly (`product.price`) and through `product.data`; direct assignments stay local until `save()`.

<ResponseField name="data" type="Product">The product object, including `id`.</ResponseField>

### update()

Merges fields into the product on the server and locally.

```ts theme={null}
product.update(data: Record<string, any>): Promise<Product>
```

**Returns** — the updated product.

**Errors** — `Failed to update product` when the platform reports no update.

### delete()

Deletes the product and empties the local `data`.

```ts theme={null}
product.delete(): Promise<Product>
```

**Returns** — the emptied product object.

### save()

Writes the whole local `data` to the server.

```ts theme={null}
product.save(): Promise<boolean>
```

**Returns** — `true`.

**Example**

```ts theme={null}
import { Products } from 'lua-cli';

const product = await Products.getById('MBP-14');
product.price = 1799;
product.inStock = false;
await product.save();
```

**Errors** — `Failed to save product data`.

### toJSON()

Returns `data`, which is what `JSON.stringify(product)` and `console.log(product)` print.

## Types

`ProductInstance` is exported. `Product` (`{ id: string; [key: string]: any }`), `ProductFilterOptions`, `ProductPaginationInstance`, and `ProductSearchInstance` are not; name them from the method that returns them.

```ts theme={null}
import { Products } from 'lua-cli';
import type { ProductInstance } from 'lua-cli';

type ProductPage = Awaited<ReturnType<typeof Products.get>>;
type ProductSearch = Awaited<ReturnType<typeof Products.search>>;

export function cheapest(page: ProductPage | ProductSearch): ProductInstance | undefined {
  return page.reduce<ProductInstance | undefined>(
    (best, p) => (best === undefined || p.price < best.price ? p : best),
    undefined,
  );
}
```

## See also

* [`Baskets`](/reference/sdk/baskets) — add products to a basket by `id`
* [`Orders`](/reference/sdk/orders) — orders created from a basket
* [Lua Query](/reference/sdk/query) — the filter `get()` accepts
* [Commerce REST API](/reference/rest/commerce) — the same catalog over HTTP
