Coupons are discount codes customers enter at checkout. Each coupon takes either a **percentage** off or a **fixed amount** off, and can be limited by date range, total redemptions, cart product count, specific products, collections, categories, or brands.

## List Coupons

```
GET /api/v1/coupons
```

### Query Parameters

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `limit` | `number` | 10 | Coupons per page (1-100) |
| `offset` | `number` | 0 | Coupons to skip |
| `query` | `string` | – | Search by coupon code |

### Response

```json
{
  "data": [
    {
      "id": "0191abc0-1234-7def-8000-000000000001",
      "code": "WELCOME10",
      "type": "percentage",
      "value": 10,
      "timesRedeemed": 42,
      "maxUses": 1000,
      "startDate": "2024-06-01T00:00:00.000Z",
      "endDate": "2024-12-31T23:59:59.000Z",
      "minProductCount": 1,
      "maxProductCount": null,
      "productIds": [],
      "collectionIds": [],
      "categoryIds": [],
      "brandIds": [],
      "currencyAmounts": {},
      "createdAt": "2024-05-20T10:30:00.000Z",
      "updatedAt": "2024-05-20T10:30:00.000Z"
    }
  ],
  "meta": {
    "count": 1,
    "offset": 0,
    "limit": 10
  }
}
```

---

## Create Coupon

```
POST /api/v1/coupons
```

Creates a discount code. Codes are case-sensitive and unique per store – creating a coupon with an existing code returns a `409`. For `fixed` coupons on multi-currency stores, supply per-currency amounts via `currencyAmounts`.

### Request Body

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `code` | `string` | Yes | The code customers enter at checkout (case-sensitive) |
| `type` | `"percentage"` \| `"fixed"` | Yes | A percentage off or a fixed amount off |
| `value` | `number` | Yes | Percentage (e.g. `10` = 10%, max 100) or fixed amount in store currency (min 1) |
| `startDate` | `string` | No | ISO 8601 datetime the coupon becomes valid |
| `endDate` | `string` | No | ISO 8601 datetime the coupon expires. Must be after `startDate` when both are set |
| `maxUses` | `number` | No | Maximum total redemptions across all customers |
| `minProductCount` | `number` | No | Minimum cart product count required |
| `maxProductCount` | `number` | No | Maximum cart product count the coupon applies to. Must be >= `minProductCount` when both are set |
| `products` | `string[]` | No | Product UUIDs the coupon is restricted to (empty = no product restriction) |
| `collections` | `string[]` | No | Collection UUIDs the coupon is restricted to; membership is evaluated at checkout |
| `categories` | `string[]` | No | Category UUIDs the coupon is restricted to; membership is evaluated at checkout |
| `brands` | `string[]` | No | Brand UUIDs the coupon is restricted to; membership is evaluated at checkout |
| `currencyAmounts` | `object` | No | Per-currency fixed amounts for multi-currency stores (fixed coupons only) |

```bash
curl -X POST \
  -H "Authorization: Bearer your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"code": "WELCOME10", "type": "percentage", "value": 10, "maxUses": 1000}' \
  https://your-store.yns.store/api/v1/coupons
```

### Response (201)

```json
{
  "id": "0191abc0-1234-7def-8000-000000000001",
  "code": "WELCOME10",
  "type": "percentage",
  "value": 10,
  "timesRedeemed": 0,
  "maxUses": 1000,
  "startDate": null,
  "endDate": null,
  "minProductCount": null,
  "maxProductCount": null,
  "productIds": [],
  "collectionIds": [],
  "categoryIds": [],
  "brandIds": [],
  "currencyAmounts": {},
  "createdAt": "2024-06-15T10:30:00.000Z",
  "updatedAt": "2024-06-15T10:30:00.000Z"
}
```

---

## Get Coupon

```
GET /api/v1/coupons/:idOrCode
```

Returns a single coupon, addressable by its UUID or by its code.

### Response

```json
{
  "id": "0191abc0-1234-7def-8000-000000000001",
  "code": "WELCOME10",
  "type": "percentage",
  "value": 10,
  "timesRedeemed": 42,
  "maxUses": 1000,
  "startDate": "2024-06-01T00:00:00.000Z",
  "endDate": "2024-12-31T23:59:59.000Z",
  "minProductCount": 1,
  "maxProductCount": null,
  "productIds": [],
  "collectionIds": [],
  "categoryIds": [],
  "brandIds": [],
  "currencyAmounts": {},
  "createdAt": "2024-05-20T10:30:00.000Z",
  "updatedAt": "2024-05-20T10:30:00.000Z"
}
```

---

## Update Coupon

```
PATCH /api/v1/coupons/:idOrCode
```

Replaces the coupon's fields. Uses the same body as **Create Coupon**. Renaming onto a code another coupon already owns returns a `409`.

### Request Body

Same fields as [Create Coupon](#create-coupon).

```bash
curl -X PATCH \
  -H "Authorization: Bearer your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"code": "WELCOME10", "type": "percentage", "value": 15}' \
  https://your-store.yns.store/api/v1/coupons/0191abc0-1234-7def-8000-000000000001
```

### Response (200)

```json
{
  "id": "0191abc0-1234-7def-8000-000000000001",
  "code": "WELCOME10",
  "type": "percentage",
  "value": 15,
  "timesRedeemed": 42,
  "maxUses": 1000,
  "startDate": "2024-06-01T00:00:00.000Z",
  "endDate": "2024-12-31T23:59:59.000Z",
  "minProductCount": 1,
  "maxProductCount": null,
  "productIds": [],
  "collectionIds": [],
  "categoryIds": [],
  "brandIds": [],
  "currencyAmounts": {},
  "createdAt": "2024-05-20T10:30:00.000Z",
  "updatedAt": "2024-06-15T11:00:00.000Z"
}
```

---

## Delete Coupon

```
DELETE /api/v1/coupons/:idOrCode
```

Hard-deletes the coupon by UUID or code. A coupon that is currently assigned to the store's enabled welcome offer cannot be deleted – disable the offer or reassign it first.

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

### Response (200)

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

### Welcome Offer Guard (409)

```json
{
  "error": "Coupon is used by the enabled welcome offer -- disable the offer or reassign it first"
}
```