# Catalog

Beauty Router publishes its product catalog as files. You download the full file once, then the daily delta to stay in
sync. You can also [search the catalog](#search-the-catalog) over the API. The catalog is the same for every salon.

The files and the search results have no wholesale prices. Wholesale prices show only to companies that are
`prequalified` or `verified`, in Beauty Router's interface and through [offers](/create-orders/#get-offers) in the API.
See [Companies and verification](/companies/).

## Get the file links

`GET /v1/catalog/files` returns links to the latest files. It takes the app token.

```bash
curl https://api.beautyrouter.com/v1/catalog/files \
  -H "Authorization: Bearer $BEAUTY_ROUTER_APP_TOKEN"
```

```json
{
  "full": {
    "url": "https://files.beautyrouter.com/catalog/full-2026-10-29.jsonl.gz?expires=1793286000&signature=b41f0c9a7e",
    "generatedAt": "2026-10-29T05:00:00Z",
    "productsCount": 48213
  },
  "delta": {
    "url": "https://files.beautyrouter.com/catalog/delta-2026-10-29.jsonl.gz?expires=1793286000&signature=7c2e9d1b04",
    "from": "2026-10-28T05:00:00Z",
    "to": "2026-10-29T05:00:00Z",
    "productsCount": 1376
  },
  "categories": {
    "url": "https://files.beautyrouter.com/catalog/categories-2026-10-29.jsonl.gz?expires=1793286000&signature=e93a6d2f18",
    "generatedAt": "2026-10-29T05:00:00Z",
    "categoriesCount": 186
  }
}
```

The links are valid for 1 hour. Download the files within that hour, or call the endpoint again for new links.

```bash
curl -o catalog-full.jsonl.gz "<full.url>"
gunzip catalog-full.jsonl.gz
```

## File format

The files are gzip-compressed JSON Lines: one object per line. Beauty Router generates them daily.

- **Full file:** every product in the catalog.
- **Delta file:** the products added, changed, or removed in the last 24 hours, between `from` and `to`. A removed
  product has `removed: true`.
- **Categories file:** every category. See [Categories](#categories).

Download the full file first. After that, apply the delta every day. If you miss a day, download the full file again.

## Product fields

Here is one line from a file:

```json
{"id":"prod-4Fz9QbX2kL","groupId":"group-6Tn1vBz8Mc","brand":{"id":"brand-5Lm2xNc7Qp","name":"Amberlin","logo":"https://images.beautyrouter.com/brand-5Lm2xNc7Qp/logo.png"},"name":"Amberlin Bond Repair Shampoo 33.8 fl oz","nameInGroup":"33.8 fl oz","category":{"id":"cat-0Kj5hGf4Ds","path":["Hair care","Shampoo"]},"type":"professional","barcodes":["850012345678"],"image":"https://images.beautyrouter.com/prod-4Fz9QbX2kL/1.jpg","image48x48":"https://images.beautyrouter.com/prod-4Fz9QbX2kL/1-48x48.jpg","image64x64":"https://images.beautyrouter.com/prod-4Fz9QbX2kL/1-64x64.jpg","description":"A sulfate-free shampoo that cleanses gently and strengthens chemically treated hair.","setMembers":[],"msrp":48.00,"currency":"usd","availableInMarketplace":true,"updatedAt":"2026-10-28T06:12:44Z"}
```

| Field                    | Description                                                                 |
| ------------------------ | --------------------------------------------------------------------------- |
| `id`                     | The product ID, such as `prod-4Fz9QbX2kL`.                                  |
| `groupId`                | The product group, or `null`. Sizes of the same product share it.           |
| `brand`                  | The brand: `id`, `name`, and `logo` (a URL or `null`). `null` for a set.    |
| `name`                   | The full product name: brand, what it is, and size.                         |
| `nameInGroup`            | The name that tells products in the same group apart, such as the size.     |
| `category`               | The category: `id`, and `path` from the top category down.                  |
| `type`                   | `professional` for use in the salon, or `retail` for resale to clients.     |
| `barcodes`               | The product's barcodes.                                                     |
| `image`                  | The URL of the product image, or `null`.                                    |
| `image48x48`             | The URL of a 48x48 thumbnail, or `null`.                                    |
| `image64x64`             | The URL of a 64x64 thumbnail, or `null`.                                    |
| `description`            | The product description.                                                    |
| `setMembers`             | The products in a set. It is empty for a product that is not a set.         |
| `msrp`                   | The manufacturer's suggested retail price, in US dollars, or `null`.        |
| `currency`               | Always `"usd"`.                                                             |
| `availableInMarketplace` | Whether the marketplace sells the product, even if it is out of stock now.  |
| `updatedAt`              | When the product last changed.                                              |

The catalog has more products than the marketplace sells at any moment, so check `availableInMarketplace` before you offer
a product for sale.

## Sizes and groups

Products in different sizes are separate products, each with its own `id`. They share a `groupId`. For example,
Amberlin Bond Repair Shampoo 33.8 fl oz and Amberlin Bond Repair Shampoo 10.1 fl oz are two products in one group.

## Sets

A set bundles other catalog products, each with a quantity. Each member has its own brand, so a set has `brand: null`.
A non-empty `setMembers` means the product is a set.

```json
{
  "id": "prod-7Rt3yUp6Qa",
  "name": "Amberlin and Jadewell Color Care Duo",
  "brand": null,
  "setMembers": [
    { "productId": "prod-4Fz9QbX2kL", "quantity": 2, "order": 1, "brandId": "brand-5Lm2xNc7Qp" },
    { "productId": "prod-2Df7gHj4Kl", "quantity": 1, "order": 2, "brandId": "brand-3Xs8cDv1Bn" }
  ]
}
```

The example shows only some of the fields.

## Categories

The categories file has one category per line. `parentId` is `null` for a top category, and `order` sorts the
categories that share a parent.

```json
{"id":"cat-3Ty7uIo1Pa","name":"Hair care","parentId":null,"order":1}
{"id":"cat-0Kj5hGf4Ds","name":"Shampoo","parentId":"cat-3Ty7uIo1Pa","order":1}
```

| Field      | Description                                         |
| ---------- | --------------------------------------------------- |
| `id`       | The category ID, the same as `category.id` on a product. |
| `name`     | The category name.                                  |
| `parentId` | The parent category, or `null` for a top category.  |
| `order`    | The position among categories with the same parent. |

## Search the catalog

`GET /v1/catalog/search` searches the catalog. Use it to search the catalog in your own screens, with no index of your
own. It takes the app token, so call it from your server.

It takes the search text in `q`, plus `limit` and `startingAfter` for [paging](/conventions/#paging).

```bash
curl "https://api.beautyrouter.com/v1/catalog/search?q=bond%20repair%20shampoo&limit=1" \
  -H "Authorization: Bearer $BEAUTY_ROUTER_APP_TOKEN"
```

It returns `data` and `hasMore`. Each item has the same [fields](#product-fields) as a line in the catalog files, so it
has the MSRP and no wholesale prices.

```json
{
  "data": [
    {
      "id": "prod-4Fz9QbX2kL",
      "groupId": "group-6Tn1vBz8Mc",
      "brand": {
        "id": "brand-5Lm2xNc7Qp",
        "name": "Amberlin",
        "logo": "https://images.beautyrouter.com/brand-5Lm2xNc7Qp/logo.png"
      },
      "name": "Amberlin Bond Repair Shampoo 33.8 fl oz",
      "nameInGroup": "33.8 fl oz",
      "category": { "id": "cat-0Kj5hGf4Ds", "path": ["Hair care", "Shampoo"] },
      "type": "professional",
      "barcodes": ["850012345678"],
      "image": "https://images.beautyrouter.com/prod-4Fz9QbX2kL/1.jpg",
      "image48x48": "https://images.beautyrouter.com/prod-4Fz9QbX2kL/1-48x48.jpg",
      "image64x64": "https://images.beautyrouter.com/prod-4Fz9QbX2kL/1-64x64.jpg",
      "description": "A sulfate-free shampoo that cleanses gently and strengthens chemically treated hair.",
      "setMembers": [],
      "msrp": 48.00,
      "currency": "usd",
      "availableInMarketplace": true,
      "updatedAt": "2026-10-28T06:12:44Z"
    }
  ],
  "hasMore": true
}
```

## Product details

`GET /v1/catalog/products/{productId}` returns one product with everything a product screen needs. It takes the user
token, so your page can call it when a user opens product details. It has no batch form, so use the
[files](#get-the-file-links) for lists.

```bash
curl https://api.beautyrouter.com/v1/catalog/products/prod-4Fz9QbX2kL \
  -H "Authorization: Bearer $USER_TOKEN"
```

The response has every [catalog field](#product-fields), plus these:

| Field         | Description                                                                                |
| ------------- | ------------------------------------------------------------------------------------------ |
| `images`      | The URLs of all the product images.                                                        |
| `benefits`    | What the product does, as text, or `null`.                                                 |
| `ingredients` | The ingredients, as text, or `null`.                                                       |
| `usage`       | How to use the product, as text, or `null`.                                                |
| `attributes`  | Ready-made text rows: `name` and `value`, such as Volume and 33.8 fl oz.                   |
| `suppliers`   | One entry per supplier that sells any size in the product group, sorted best first for this salon. |

```json
{
  "id": "prod-4Fz9QbX2kL",
  "groupId": "group-6Tn1vBz8Mc",
  "brand": {
    "id": "brand-5Lm2xNc7Qp",
    "name": "Amberlin",
    "logo": "https://images.beautyrouter.com/brand-5Lm2xNc7Qp/logo.png"
  },
  "name": "Amberlin Bond Repair Shampoo 33.8 fl oz",
  "nameInGroup": "33.8 fl oz",
  "category": { "id": "cat-0Kj5hGf4Ds", "path": ["Hair care", "Shampoo"] },
  "type": "professional",
  "barcodes": ["850012345678"],
  "image": "https://images.beautyrouter.com/prod-4Fz9QbX2kL/1.jpg",
  "image48x48": "https://images.beautyrouter.com/prod-4Fz9QbX2kL/1-48x48.jpg",
  "image64x64": "https://images.beautyrouter.com/prod-4Fz9QbX2kL/1-64x64.jpg",
  "description": "A sulfate-free shampoo that cleanses gently and strengthens chemically treated hair.",
  "setMembers": [],
  "msrp": 48.00,
  "currency": "usd",
  "availableInMarketplace": true,
  "updatedAt": "2026-10-28T06:12:44Z",
  "images": [
    "https://images.beautyrouter.com/prod-4Fz9QbX2kL/1.jpg",
    "https://images.beautyrouter.com/prod-4Fz9QbX2kL/2.jpg"
  ],
  "benefits": "Rebuilds broken bonds and leaves hair stronger after every wash.",
  "ingredients": "Water, Sodium Cocoyl Isethionate, Cocamidopropyl Betaine, Glycerin, Hydrolyzed Keratin.",
  "usage": "Massage into wet hair, lather, and rinse. Follow with Amberlin Bond Repair Conditioner.",
  "attributes": [
    { "name": "Volume", "value": "33.8 fl oz" },
    { "name": "Hair type", "value": "Color-treated" }
  ],
  "suppliers": [
    {
      "productId": "prod-4Fz9QbX2kL",
      "id": "sup-1Ry6tUi3Op",
      "name": "Amberlin",
      "logo": "https://images.beautyrouter.com/sup-1Ry6tUi3Op/logo.png",
      "msrp": 48.00
    },
    {
      "productId": "prod-9Cv5bNm3Ws",
      "id": "sup-8Wq2eRt5Yu",
      "name": "Dunmore Beauty Supply",
      "logo": "https://images.beautyrouter.com/sup-8Wq2eRt5Yu/logo.png",
      "msrp": 22.00
    }
  ]
}
```

In `suppliers`, `productId` is the size that supplier sells, and `id` is the supplier. There are no prices here. Prices
come from [offers](/create-orders/#get-offers).

## Use a product in your salon app

To show the full product without building a screen, call `showProductDetails(element, productId, options)`. You can turn
the marketplace features on or off, such as the add-to-cart button and recommended products. See
[Product details](/embedded-marketplace/#product-details).