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

# Products

> Product master data: stock, prices, lead times, lifecycle, and family/model attributes.

## What it is

Product master data — one row **per variant** (per size/version, not per model). Combines shop-synced facts (SKU, name, price, stock, active flag) with procurement master data you maintain in the app: supplier assignment, purchase price, MOQ, lead times, safety/target stock days, seasonality profile, and lifecycle status.

Variants of the same model are grouped by `product_family` (source product gid) with a human-readable `product_family_label` derived from the title. The procurement views — [Product Demand](/api/data-model/product-demand), [Procurement Queue](/api/data-model/procurement-queue), [Family Trend](/api/data-model/family-trend) — are all computed on top of this table.

## How it gets data

Ingest-enabled — shop syncs push rows through the [Ingest API](/api/ingest); the procurement columns are edited in the app (or via MCP `data_row_update`) and survive re-syncs:

```http theme={null}
POST /api/ingest/products
Authorization: Bearer pk_...
Content-Type: application/json
```

* One JSON record per request; unknown fields are dropped.
* **Upsert identity is the variant, not the SKU:** products upsert on `(tenant_id, identity_key)` where `identity_key = COALESCE(external_id, sku)`. Send the source **variant id** as `external_id` (Shopify ProductVariant gid) — several sizes of one model can share a SKU, and without `external_id` they would overwrite each other. Integrations that only have a SKU may omit `external_id`; then the SKU is the identity.
* `tenant_id` / `workspace_id` come from the API key context, never from the body.
* A ready-made **Make.com flow template** is downloadable in Settings → Data Sources.

Minimal example payload:

```json theme={null}
{
  "external_id": "gid://shopify/ProductVariant/445566",
  "sku": "6304",
  "name": "Claire 6304 — 16 cm",
  "price": 249.90,
  "currency": "EUR",
  "stock": 12,
  "active": true,
  "product_family": "gid://shopify/Product/778899"
}
```

## Fields

### Identity & shop facts

| Field                  | Label              | Type     | Writable | Description                                                                                                                                                                          |
| ---------------------- | ------------------ | -------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `external_id`          | Variant ID         | text     | ingest   | Stable source variant id (Shopify ProductVariant gid). The preferred identity; when NULL the upsert identity falls back to the SKU                                                   |
| `identity_key`         | —                  | text     | API only | Generated upsert arbiter: `COALESCE(external_id, sku)`. Unique per tenant; system-maintained, never writable                                                                         |
| `sku`                  | SKU                | text     | ingest   | Article/model number. Deliberately **not** the identity — several sizes of one model can share a SKU                                                                                 |
| `name`                 | Name               | text     | ingest   | Variant title. Required                                                                                                                                                              |
| `product_family`       | Shopify Product ID | text     | ✓        | Source model/product group (Shopify Product gid). All sizes of a model share it. Not a key — manually correctable; NULL rows are treated as their own group by the procurement views |
| `product_family_label` | Product Family     | text     | trigger  | Human-readable model family, derived from the product title (rule: `procurement_config.family_rule`). Set by a database trigger on every write                                       |
| `price`                | Price              | currency | ingest   | Selling price                                                                                                                                                                        |
| `currency`             | Currency           | text     | ingest   | Selling currency                                                                                                                                                                     |
| `stock`                | Stock              | number   | ingest   | Stock on hand from the shop sync — the number all coverage calculations start from                                                                                                   |
| `active`               | Active             | boolean  | ingest   | Whether the variant is active/sellable in the shop                                                                                                                                   |
| `inventory_synced_at`  | Last Sync          | datetime | ingest   | When stock was last synced — feeds the data-freshness gates in the procurement queue                                                                                                 |
| `metadata`             | —                  | json     | API only | Raw source fields that don't map to a column                                                                                                                                         |

### Procurement master data (maintained in the app)

| Field                      | Label             | Type     | Writable | Description                                                                                                                                                  |
| -------------------------- | ----------------- | -------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `procurement_trackable`    | Track             | boolean  | ✓        | Include this variant in procurement planning. Untracked variants never appear in the queue                                                                   |
| `lifecycle_status`         | Lifecycle         | status   | ✓        | `launch` / `growth` / `stable` / `declining` / `discontinued`. Scales planned demand via a lifecycle factor and gates recommendations for discontinued items |
| `supplier_id`              | Supplier          | text     | ✓        | Assigned supplier — joins to `suppliers.id`; the variant inherits the supplier's default lead time / target stock when its own are empty                     |
| `supplier_sku`             | Supplier SKU      | text     | ✓        | Article number at the supplier, printed on order lists                                                                                                       |
| `purchase_price`           | Purchase Price    | currency | ✓        | Buying price per unit                                                                                                                                        |
| `purchase_currency`        | Purchase Currency | text     | ✓        | Buying currency                                                                                                                                              |
| `minimum_order_quantity`   | Min. Order        | number   | ✓        | Supplier MOQ — recommendations are rounded up to it                                                                                                          |
| `order_multiple`           | Order Multiple    | number   | ✓        | Order quantities are rounded to this multiple (e.g. carton size)                                                                                             |
| `lead_time_days`           | Lead Time (d)     | number   | ✓        | Variant-specific lead time; overrides the supplier default                                                                                                   |
| `lead_time_buffer_days`    | Lead Buffer (d)   | number   | ✓        | Extra buffer on top of the lead time for customs/quality delays                                                                                              |
| `safety_stock_days`        | Safety Stock (d)  | number   | ✓        | Days of demand held as safety stock — part of the replenishment risk window                                                                                  |
| `target_stock_days`        | Target Stock (d)  | number   | ✓        | Target coverage in days an order should restore                                                                                                              |
| `seasonality_profile`      | Seasonality       | status   | ✓        | Which seasonality profile applies — see [Seasonality](/api/data-model/seasonality)                                                                           |
| `is_advertised`            | Advertised        | boolean  | ✓        | Currently being advertised — context for interpreting demand spikes                                                                                          |
| `manual_demand_multiplier` | Demand Multiplier | number   | ✓        | Manual factor multiplied into planned demand (e.g. 1.5 before a campaign)                                                                                    |
| `manual_target_quantity`   | Manual Target Qty | number   | ✓        | Manual forecast for variants without sales history (`demand_status = MANUAL_FORECAST`)                                                                       |
| `procurement_paused_until` | Paused Until      | date     | ✓        | Exclude the variant from recommendations until this date                                                                                                     |
| `procurement_note`         | Procurement Note  | text     | ✓        | Free-text note for the buyer/agent, shown alongside recommendations                                                                                          |

"ingest" = populated through the ingest endpoint; read-only in the app. ✓ = editable in the app and settable on create; edits survive shop re-syncs. "API only" = real database column not registered as a visible app column. "trigger" = computed by the database on write.

<Note>
  Like every workspace table, products also carry the system columns `id`, `tenant_id`, `workspace_id`, `created_at`, and `updated_at` — set automatically, never sent by integrations.
</Note>

## Query it

```json theme={null}
{
  "method": "tools/call",
  "params": {
    "name": "data_query",
    "arguments": {
      "view_slug": "products",
      "filters": { "procurement_trackable": true, "active": true },
      "select_fields": ["sku", "name", "stock", "lead_time_days", "supplier_sku"],
      "sort_by": "stock",
      "sort_desc": false,
      "limit": 50
    }
  }
}
```
