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

# Wine and whiskey family journeys

> Build vintage and age-statement browsing without merging SKU-level price, status, or inventory.

Product families let an application present related source SKUs together while preserving the facts that belong to each SKU. A family is a reviewed relationship, not a shared inventory record.

The examples use `NEKTIR_API_BASE` for the API origin supplied with pilot access. `api.nektir.com` currently hosts documentation, not the production API service.

## Wine: browse vintages and choose an available bottle

### 1. Search once per family

Use `collapse=family` with a text search. A grouped result has a non-null `search_group`; an ungrouped product keeps `search_group: null`.

```bash theme={"dark"}
curl "$NEKTIR_API_BASE/v1/products?q=eisele%20cabernet&collapse=family&limit=20"
```

```json theme={"dark"}
{
  "data": [
    {
      "name": "Eisele Cabernet Sauvignon 2023 750 mL",
      "product_family": {
        "id": "df744b61-b5e5-4403-9194-898f57c8dad6",
        "slug": "eisele-cabernet-sauvignon",
        "name": "Eisele Cabernet Sauvignon",
        "relationship_type": "expression"
      },
      "search_group": {
        "type": "product_family",
        "id": "df744b61-b5e5-4403-9194-898f57c8dad6",
        "member_count": 11,
        "representative_product_id": "<uuid>"
      }
    }
  ]
}
```

The representative product makes collapsed search fast and compact. Its price and availability belong only to that SKU. Use the returned family ID before rendering choices.

### 2. Load every current vintage

```bash theme={"dark"}
curl "$NEKTIR_API_BASE/v1/product-families/df744b61-b5e5-4403-9194-898f57c8dad6"
```

Each member includes `vintage_year`, `package_size`, `price`, `status`, `inventory_retrieved_at`, and `availability_summary`. Sort or label options from these structured values instead of reparsing the display name.

```json theme={"dark"}
{
  "id": "<product-uuid>",
  "source_ids": {"utah_dabs_sku": "931262"},
  "name": "Eisele Cabernet Sauvignon 2023 750 mL",
  "relationship_type": "expression",
  "vintage_year": 2023,
  "age_statement": null,
  "package_size": {"milliliters": 750, "display": "750 mL", "normalization_state": "normalized"},
  "price": {"amount_minor": 62552, "currency": "USD", "display": "$625.52"},
  "status": {"code": "S", "label": "Special order", "lifecycle": "restricted"},
  "inventory_retrieved_at": "<retrieval-time-or-null>",
  "availability_summary": {"reported_positive_store_count": 0, "upstream_age_known": false}
}
```

Prices and status values can change. Example values illustrate the response shape and should not be treated as current without making the request.

### 3. Show nearby availability for the selected SKU

After the customer chooses a vintage and package, use that member's product ID:

```bash theme={"dark"}
curl "$NEKTIR_API_BASE/v1/products/<product-uuid>/inventory?lat=40.7608&lng=-111.8910&radius_m=25000&inventory_age=within_policy&sort=distance"
```

Do not interpret an empty page as no stock unless `meta.coverage.status` and `unchecked_pair_count` establish complete coverage for the requested policy.

## Whiskey: compare age statements, then package variants

### 1. Find reviewed whiskey families

Browse families directly when the interface starts from a brand or product line:

```bash theme={"dark"}
curl "$NEKTIR_API_BASE/v1/product-families?q=macallan&category_ids=cat_whiskey&limit=20"
```

Or collapse product search:

```bash theme={"dark"}
curl "$NEKTIR_API_BASE/v1/products?q=macallan%20double%20cask&category_ids=cat_whiskey&collapse=family"
```

### 2. Compare ages without parsing names

```bash theme={"dark"}
curl "$NEKTIR_API_BASE/v1/product-families/11b3b5b8-2acc-478a-8d3a-2100eb8a5cf9"
```

The Macallan Double Cask family exposes `15 Year`, `18 Year`, and `30 Year` as member-level `age_statement` values. Price, lifecycle status, package size, and availability remain member-specific.

```json theme={"dark"}
{
  "name": "Macallan Double Cask 18 Year 750 mL",
  "source_ids": {"utah_dabs_sku": "005934"},
  "relationship_type": "expression",
  "vintage_year": null,
  "age_statement": "18 Year",
  "package_size": {"milliliters": 750, "display": "750 mL", "normalization_state": "normalized"},
  "price": {"amount_minor": 37999, "currency": "USD", "display": "$379.99"},
  "status": {"code": "L", "label": "Limited distribution", "lifecycle": "restricted"}
}
```

If an age also has multiple package variants, keep both members visible or filter products with `family_id` and `relationship_type=package_variant`:

```bash theme={"dark"}
curl "$NEKTIR_API_BASE/v1/products?family_id=<family-uuid>&relationship_type=package_variant&sort=price"
```

### 3. Open a product without another family request

Both product lookup routes include `family_members`, so a product page can render its current vintage, age, and package alternatives immediately:

```bash theme={"dark"}
curl "$NEKTIR_API_BASE/v1/products/by-sku/005934"
curl "$NEKTIR_API_BASE/v1/products/<product-uuid>"
```

The detail response also contains compact `meta.freshness.catalog` and `meta.freshness.inventory` clocks. `retrieved_at` is Nektir's retrieval time; `source_updated_at` remains null when DABS does not provide one.

## Show observed family changes

Family detail includes an `observed_history` summary across its current members: retained event counts, observed price range, last price and inventory change times, members currently reporting stock, and retention boundaries. Load the underlying SKU-specific events when an interface needs a timeline:

```bash theme={"dark"}
curl "$NEKTIR_API_BASE/v1/product-families/<family-uuid>/changes?types=price_changed,status_changed,store_inventory_changed&limit=50"
```

The API computes this view from retained product events at the pinned publication revision; it does not create a second family-history record. A family can gain reviewed members later, so persist event IDs for deduplication and keep each event's product SKU visible. Inventory differences remain observations—not purchases, sales, or guaranteed restocks.

## Lifecycle and availability rules

* A family does not make a discontinued or unavailable member active.
* Never sum member inventory unless the application explicitly wants availability across substitutable products.
* `reported_positive_store_count` reports observations, not consumer purchase eligibility.
* Prefer stable family and product UUIDs. Preserve all six characters of DABS SKUs, including leading zeroes.
* Treat null vintage, age, price, or retrieval time as unknown—not zero, non-vintage, free, or recently checked.
