Collections group products together for display on your storefront (e.g. "Featured", "New Arrivals", "Sale Items"). YNS supports **manual** collections (products added individually) and several **smart** collection types that auto-include products based on price range, variant attributes, or recency.

## List Collections

```
GET /api/v1/collections
```

### Query Parameters

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `limit` | `number` | 10 | Collections per page (1-100) |
| `offset` | `number` | 0 | Collections to skip |
| `query` | `string` | – | Search by collection name |
| `active` | `boolean` | – | Filter by active status |
| `group` | `string` | – | Filter by collection group |
| `lang` | `string` | – | Locale code for translations (e.g. `pl-PL`) |

### Response

```json
{
  "data": [
    {
      "id": "0191abc0-1234-7def-8000-000000000001",
      "name": "Featured Products",
      "slug": "featured-products",
      "image": "https://cdn.example.com/featured.jpg",
      "filter": { "type": "manual" },
      "active": true,
      "productCollections": [
        { "productId": "0191abc0-0000-7000-8000-000000000100" }
      ],
      "translations": []
    }
  ],
  "meta": {
    "count": 1
  }
}
```

---

## Get Collection

```
GET /api/v1/collections/:idOrSlug
```

Returns a single collection by UUID or slug, with associated products and translations. Supports `?lang=` for translated content.

```json
{
  "id": "0191abc0-1234-7def-8000-000000000001",
  "name": "Featured Products",
  "slug": "featured-products",
  "filter": { "type": "manual" },
  "active": true,
  "productCollections": [
    {
      "productId": "0191abc0-0000-7000-8000-000000000100",
      "product": {
        "id": "0191abc0-0000-7000-8000-000000000100",
        "name": "Classic Tee",
        "slug": "classic-tee"
      }
    }
  ]
}
```

---

## Create Collection

```
POST /api/v1/collections
```

Creates a product collection. Slug is auto-generated from the name if not provided. Image URLs are downloaded and re-uploaded to the store's CDN.

### Request Body

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `name` | `string` | Yes | Collection name |
| `slug` | `string` | No | URL slug (auto-generated from name) |
| `description` | `string` | No | Plain text description |
| `image` | `string` | No | Image URL |
| `filter` | `object` | No | Filter type (default: `{ type: "manual" }`) |
| `active` | `boolean` | No | Visible on storefront (default: `true`) |
| `group` | `string` | No | Grouping key for organizing collections (1-100 chars) |

### Filter Types

| Type | Description |
|------|-------------|
| `{ type: "manual" }` | Products are manually added |
| `{ type: "dynamicPrice", min?: number, max?: number }` | Products auto-included by price range |
| `{ type: "variantValues", values: Record<string, string[]> }` | Products auto-included when a variant attribute label matches the given values (e.g. `{ "Color": ["Red", "Blue"] }`) |
| `{ type: "newest", days?: number }` | Products created within the last `days` are auto-included, ordered newest-first. Omit `days` to include the whole catalog sorted by newest. |

```bash
# Manual collection
curl -X POST \
  -H "Authorization: Bearer your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"name": "Featured", "filter": {"type": "manual"}}' \
  https://your-store.yns.store/api/v1/collections

# Dynamic price collection
curl -X POST \
  -H "Authorization: Bearer your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"name": "Budget Friendly", "filter": {"type": "dynamicPrice", "max": 50}}' \
  https://your-store.yns.store/api/v1/collections

# Variant attribute collection
curl -X POST \
  -H "Authorization: Bearer your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"name": "Red Products", "filter": {"type": "variantValues", "values": {"Color": ["Red"]}}}' \
  https://your-store.yns.store/api/v1/collections

# New arrivals (last 30 days)
curl -X POST \
  -H "Authorization: Bearer your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"name": "New Arrivals", "filter": {"type": "newest", "days": 30}}' \
  https://your-store.yns.store/api/v1/collections
```

### Response (201)

Returns the created collection with full data.

---

## Update Collection

```
PATCH /api/v1/collections/:idOrSlug
```

Partially updates a collection by UUID or slug. Only provided fields change. Pass `null` to clear optional fields like `description`, `image`, or `group`.

### Request Body

All fields from Create Collection are accepted and optional.

```bash
curl -X PATCH \
  -H "Authorization: Bearer your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"filter": {"type": "newest", "days": 14}, "group": "seasonal"}' \
  https://your-store.yns.store/api/v1/collections/new-arrivals
```

### Response (200)

Returns the updated collection.

---

## Delete Collection

```
DELETE /api/v1/collections/:idOrSlug
```

Permanently deletes a collection by UUID or slug.

```bash
curl -X DELETE \
  -H "Authorization: Bearer your_api_key" \
  https://your-store.yns.store/api/v1/collections/old-sale
```

### Response (200)

```json
{
  "ok": true,
  "deleted": 1
}
```

---

## Translations

```
PUT /api/v1/collections/:idOrSlug/translations/:locale
```

Upsert or delete a single locale's translated fields for a collection. See the [Localization page](/docs/api-reference/localization#writing-translations) for the full field reference, behaviour, and examples.

---

## Import Memberships (CSV)

```
POST /api/v1/collections/import-memberships
Content-Type: multipart/form-data
```

Bulk-import (collection_slug, product_slug) pairs into manual collections. Idempotent – existing memberships are skipped on re-runs. Rows targeting dynamic-filter collections are rejected per-row.

```bash
curl -X POST \
  -H "Authorization: Bearer your_api_key" \
  -F "file=@memberships.csv" \
  https://your-store.yns.store/api/v1/collections/import-memberships
```

### Response

```json
{
  "ok": true,
  "processed": 1000,
  "inserted": 950,
  "skipped_existing": 40,
  "errors": []
}
```