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

# Catalog Upload

> Upload your product catalog to VIZOCHOK for AI-powered search. Learn the data format, bulk upload, job tracking, and partial updates.

VIZOCHOK stores your product catalog for AI search and recommendations. Prices and stock availability are **not** stored here -- those come from your backend via [webhooks](/webhooks/overview) at query time.

The catalog API requires a **secret API key** (`sk_...`) with `catalog` scope.

***

## Product Data Format

Each product is described by the `ProductInput` schema:

<ParamField body="sku" type="string" required>
  Unique product identifier (Stock Keeping Unit). Used as the primary key -- uploading the same SKU again updates the existing product.
</ParamField>

<ParamField body="name" type="string" required>
  Product name. This is the most important field for search quality.
</ParamField>

<ParamField body="category_path" type="string">
  Category hierarchy, e.g. `"Dairy > Milk > UHT Milk"`. Improves search context.
</ParamField>

<ParamField body="brand" type="string">
  Brand name. Customers frequently search by brand.
</ParamField>

<ParamField body="description" type="string">
  Product description. Included in the embedding for semantic search.
</ParamField>

<ParamField body="ingredients" type="string">
  Ingredients list. Enables the AI to answer "what's in this product?" questions.
</ParamField>

<ParamField body="country" type="string">
  Country of origin.
</ParamField>

<ParamField body="unit" type="string" default="pcs">
  Unit of measure: `"pcs"`, `"kg"`, `"l"`, etc.
</ParamField>

<ParamField body="quantity_step" type="number" default={1.0}>
  Minimum increment for quantity changes (e.g., `0.1` for items sold by weight).
</ParamField>

<ParamField body="min_quantity" type="number" default={1.0}>
  Minimum order quantity.
</ParamField>

<ParamField body="weight" type="number">
  Product weight in grams.
</ParamField>

<ParamField body="volume" type="number">
  Product volume in milliliters.
</ParamField>

<ParamField body="image_url" type="string">
  URL to the product image. Displayed in product cards within the chat.
</ParamField>

<ParamField body="attributes" type="object">
  Arbitrary key-value metadata stored as JSON.
</ParamField>

<ParamField body="barcode" type="string">
  Product barcode (EAN/UPC).
</ParamField>

<ParamField body="is_active" type="boolean" default={true}>
  Set to `false` to soft-delete a product from search results.
</ParamField>

### Content Fields

The following fields are combined into an enriched text and used for embedding generation: `name`, `description`, `ingredients`, `brand`, `category_path`, `country`. Changing any of these triggers re-embedding of the product.

***

## Bulk Upload

Upload products using `PUT /api/v1/catalog/products`. This endpoint performs an upsert -- new products are created, existing products (matched by `sku`) are updated.

<Steps>
  <Step title="Prepare your JSON payload">
    ```json theme={null}
    {
      "products": [
        {
          "sku": "milk-001",
          "name": "Молоко Lactel 2.5% 1л",
          "brand": "Lactel",
          "category_path": "Молочні продукти > Молоко",
          "description": "Ультрапастеризоване молоко 2.5% жирності",
          "ingredients": "Молоко коров'яче нормалізоване",
          "unit": "pcs",
          "weight": 1000,
          "image_url": "https://your-cdn.com/milk-001.jpg",
          "country": "Україна"
        },
        {
          "sku": "bread-042",
          "name": "Хліб Київхліб Столичний 950г",
          "brand": "Київхліб",
          "category_path": "Хліб та випічка > Хліб",
          "unit": "pcs",
          "weight": 950
        }
      ]
    }
    ```
  </Step>

  <Step title="Upload via API">
    ```bash theme={null}
    curl -X PUT https://api.vizochok.com/api/v1/catalog/products \
      -H "Authorization: Bearer sk_your_secret_key" \
      -H "Content-Type: application/json" \
      -d @products.json
    ```

    Response (`202 Accepted`):

    ```json theme={null}
    {
      "job_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "status": "accepted",
      "total": 2,
      "to_embed": 2,
      "unchanged": 0
    }
    ```

    <Info>
      Product metadata is saved to the database immediately. Embedding generation runs in the background. Products are searchable once their embeddings are complete.
    </Info>
  </Step>

  <Step title="Poll for job completion">
    ```bash theme={null}
    curl https://api.vizochok.com/api/v1/catalog/jobs/a1b2c3d4-e5f6-7890-abcd-ef1234567890 \
      -H "Authorization: Bearer sk_your_secret_key"
    ```

    Response:

    ```json theme={null}
    {
      "job_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "tenant_id": "your-tenant-id",
      "status": "embedding",
      "total": 2,
      "embedded": 1,
      "created": 2,
      "updated": 0,
      "unchanged": 0,
      "errors": [],
      "started_at": "2026-03-18T10:30:00Z",
      "completed_at": null
    }
    ```

    Job statuses: `pending` -> `embedding` -> `complete` (or `failed`).
  </Step>
</Steps>

### Job Status Fields

<ResponseField name="job_id" type="string">
  Unique job identifier.
</ResponseField>

<ResponseField name="status" type="string">
  Current status: `pending`, `embedding`, `complete`, or `failed`.
</ResponseField>

<ResponseField name="total" type="number">
  Total number of products in the upload.
</ResponseField>

<ResponseField name="embedded" type="number">
  Number of products that have been embedded so far. Use this with `total` to show progress.
</ResponseField>

<ResponseField name="created" type="number">
  Number of new products created.
</ResponseField>

<ResponseField name="updated" type="number">
  Number of existing products updated (content fields changed).
</ResponseField>

<ResponseField name="unchanged" type="number">
  Number of products that were identical to the existing version (no re-embedding needed).
</ResponseField>

<ResponseField name="errors" type="string[]">
  List of error messages for individual products that failed.
</ResponseField>

***

## Partial Updates (PATCH)

Update individual product fields without re-uploading the entire catalog:

```bash theme={null}
curl -X PATCH https://api.vizochok.com/api/v1/catalog/products/milk-001 \
  -H "Authorization: Bearer sk_your_secret_key" \
  -H "Content-Type: application/json" \
  -d '{
    "description": "Ультрапастеризоване молоко 2.5% жирності. Без лактози.",
    "image_url": "https://your-cdn.com/milk-001-v2.jpg"
  }'
```

Response:

```json theme={null}
{
  "sku": "milk-001",
  "updated_fields": ["description", "image_url"],
  "re_embedded": true
}
```

<Info>
  Only content fields (`name`, `description`, `ingredients`, `brand`, `category_path`, `country`) trigger re-embedding. Changing non-content fields like `image_url`, `weight`, or `unit` updates metadata instantly without re-embedding.
</Info>

***

## Soft Delete

Deactivate a product (removes it from search results but keeps the data):

```bash theme={null}
curl -X DELETE https://api.vizochok.com/api/v1/catalog/products/milk-001 \
  -H "Authorization: Bearer sk_your_secret_key"
```

Returns `204 No Content` on success. To reactivate, PATCH with `"is_active": true`.

***

## Force Reindex

Trigger a full re-embedding of all active products. Use this after changing your embedding model or if search quality seems degraded:

```bash theme={null}
curl -X POST https://api.vizochok.com/api/v1/catalog/reindex \
  -H "Authorization: Bearer sk_your_secret_key"
```

Response:

```json theme={null}
{
  "status": "started",
  "product_count": 1500
}
```

<Warning>
  Reindexing re-embeds every active product. For large catalogs, this may take several minutes and will consume embedding API credits.
</Warning>

***

## List Products

Browse your uploaded catalog with pagination and filtering:

```bash theme={null}
curl "https://api.vizochok.com/api/v1/catalog/products?page=1&limit=50&brand=Lactel" \
  -H "Authorization: Bearer sk_your_secret_key"
```

Supported query parameters:

| Parameter   | Type    | Description                             |
| ----------- | ------- | --------------------------------------- |
| `page`      | integer | Page number (default: 1)                |
| `limit`     | integer | Items per page (1-200, default: 50)     |
| `category`  | string  | Filter by category path (partial match) |
| `brand`     | string  | Filter by brand (partial match)         |
| `search`    | string  | Text search in name and brand           |
| `is_active` | boolean | Filter by active status                 |

***

## List Jobs

View recent catalog upload jobs:

```bash theme={null}
curl https://api.vizochok.com/api/v1/catalog/jobs \
  -H "Authorization: Bearer sk_your_secret_key"
```

Returns up to 50 most recent jobs, sorted by most recent first. Job data is stored in Redis with a 24-hour TTL.

***

## Tips for Large Catalogs

<Accordion title="Batch size recommendations">
  The API accepts any number of products in a single request, but for catalogs over 5,000 products we recommend splitting into batches of 1,000-2,000. This gives you more granular progress tracking and reduces the risk of timeouts.
</Accordion>

<Accordion title="Smart change detection">
  VIZOCHOK computes an MD5 hash of each product's content fields and compares it with the existing record. Products with identical content are skipped entirely -- no re-embedding occurs. This makes repeated full catalog syncs efficient.
</Accordion>

<Accordion title="Embedding batching">
  Embeddings are generated in batches of 50 products. For a 10,000-product catalog, this means 200 embedding API calls. The process runs in the background and does not block your API request.
</Accordion>

<Accordion title="Automated sync patterns">
  Set up a daily cron job or webhook from your PIM/ERP to push the full catalog:

  ```bash theme={null}
  # Example: nightly catalog sync
  0 3 * * * curl -X PUT https://api.vizochok.com/api/v1/catalog/products \
    -H "Authorization: Bearer sk_your_secret_key" \
    -H "Content-Type: application/json" \
    -d @/path/to/catalog-export.json
  ```

  Thanks to smart change detection, only modified products are re-embedded.
</Accordion>
