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

# Uploading products

> Bulk-import products from a CSV: column mapping, batches, and job tracking.

Uploading a CSV is the fastest way to bring many products into MerchantOps at
once — a spreadsheet export from your PIM, ERP, or another catalog becomes a
batch of [products](/catalog/products-variants). This page explains how columns
map to product fields, how the import runs, and how to track it. For one-off
additions, use the dashboard; for continuous feeds, see
[import connectors](/data-ingestion/import-connectors).

<Note>
  If your data lives in a spreadsheet, export it to CSV first. Pipe-delimited cells
  let a single column carry multiple values.
</Note>

## The file

Your CSV needs a header row. A few columns are required; the rest are up to you.

| Column          | Required                        | Maps to                                                                    |
| --------------- | ------------------------------- | -------------------------------------------------------------------------- |
| `key`           | Yes                             | The product's stable [key](/catalog/products-variants)                     |
| `name`          | Yes                             | The product's display name                                                 |
| `productType`   | Yes, unless a default is chosen | The [product type](/catalog/product-types-properties) it validates against |
| `source`        | No                              | Provenance of the row (defaults to merchant input)                         |
| `isPublished`   | No                              | Whether the product is marked published                                    |
| *anything else* | No                              | A product [attribute](/catalog/product-types-properties)                   |

A single upload accepts up to **250 rows**.

## How columns map

MerchantOps maps your columns automatically — there is no mapping dialog to fill
in on this path.

<Steps>
  <Step title="System columns map to product fields">
    `key`, `productType`, `source`, and `isPublished` set the product's core
    fields. Common headers are recognized case-insensitively and in a few
    spellings — for example `Product Type`, `product_type`, and `productType`
    all resolve to the product type.
  </Step>

  <Step title="Every other column becomes an attribute">
    Any column that isn't a system column is stored as a product-level
    [attribute](/catalog/product-types-properties), keyed by the column name —
    `brand`, `description`, `color`, `material`, and so on.
  </Step>

  <Step title="Pipe-delimited cells become multi-value">
    A cell like `Red|Blue` is split into a list of values. Use this for any
    attribute that holds more than one value.
  </Step>
</Steps>

<Tip>
  Brands that are new to the CSV are registered automatically as you import, so
  you don't have to create each [brand](/catalog/brands) up front.
</Tip>

## How the import runs

The upload creates a [Job](/jobs/overview) and processes rows in batches.

<Steps>
  <Step title="Rows are validated">
    Rows referencing a [product type](/catalog/product-types-properties) that
    doesn't exist in your organization are rejected and reported individually —
    with a "did you mean?" hint when a close match exists. The remaining valid
    rows still import.
  </Step>

  <Step title="Products are created">
    Each valid row becomes a product, validated and initialized against its
    product type. Attribute values are coerced to the type each
    [property definition](/catalog/product-types-properties) expects (for
    example, `TRUE`/`Yes`/`1` becomes a real boolean).
  </Step>

  <Step title="Enrichment runs if you asked for it">
    If you enable enrichment options on the upload, each new product is queued
    for [enrichment](/enrichment/how-it-works) — Lakehouse search, brand-site
    scraping, or content generation — tracked under the same Job.
  </Step>
</Steps>

## Tracking the import

The upload returns a [Job](/jobs/overview) ID. The Job reports totals for
created, failed, and — where applicable — skipped rows, and carries a per-row
error list (row number, product key, and reason) so you can fix a source file
and re-upload just what failed.

<Warning>
  Rows are reported by their position in the file, counting the header as row 1.
  When you correct errors, match the row numbers in the Job's error list against
  your original CSV.
</Warning>

## Display groups on property imports

Products validate against [property definitions](/catalog/product-types-properties),
which are organized into
[display groups](/catalog/display-groups-labels). When you import **property
definitions** by CSV, any display group named in that file that doesn't yet
exist is created for you. (This applies to the property-definition import, not
to the product CSV described above.)

<CardGroup cols={2}>
  <Card title="Import connectors" icon="arrows-rotate" href="/data-ingestion/import-connectors">
    Automate recurring imports instead of uploading by hand.
  </Card>

  <Card title="Jobs" icon="list-check" href="/jobs/overview">
    Watch progress and review per-row errors.
  </Card>

  <Card title="Product types & properties" icon="sitemap" href="/catalog/product-types-properties">
    The templates and attribute schema your rows validate against.
  </Card>

  <Card title="How enrichment works" icon="wand-magic-sparkles" href="/enrichment/how-it-works">
    What happens when you enable enrichment on an upload.
  </Card>
</CardGroup>
