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

# Validate Single Coupon

> Check whether one coupon can be used by a customer, optionally against the cart they are about to pay for, and reserve it while the order is completed.

This API validates a single coupon identified by `{code}` and checks its eligibility for use by a customer. You can also describe the cart, so the coupon is checked against what the customer is actually buying, and set `lock` to `true` to reserve the coupon so it cannot be spent elsewhere while the order is being completed.

Available on **v4.0** and **v4.1**. Requires the **Gameball Coupon Engine** feature on your plan — accounts without it are rejected before any validation runs.

There are two ways to burn a coupon when placing an order:

* **Option 1 — Use the coupon code directly in the Order API**: Pass the coupon code in the Order API without needing the lock reference. The coupon burns automatically.
* **Option 2 — Use the lock reference in the Order API**: Pass the `lockReference` from this API into `redemption.couponsLockReference` in the Order API. The coupon burns automatically — no separate Burn Coupon API call is needed.

<Warning>
  If you validate a coupon with `lock: true` and then call the **Release Coupons API** before placing the order, the lock is released. If you then try to use the coupon code directly in the Order API, the request will fail. Only release the lock if you are canceling the transaction entirely.
</Warning>

<Info>
  To validate several coupon codes against the same cart in one call, use [Validate Multiple Coupons](/api-reference/coupons/validate-multiple-coupons).
</Info>

<Info>
  **Channel Merging Available**\
  If your system uses different customer IDs across multiple channels (e.g., online and offline), Gameball's channel merging feature helps unify customer profiles. By including the customer's mobile number or email (based on your merging configuration) with each request, Gameball will combine activities into a single profile.

  For more information, head to the [Omni-Channel Handling Guide](/tutorials/experiences/more/omni-channel).
</Info>

<Info>
  **Security**: Requires **apikey** and **secretkey** headers.
</Info>

## Describing the cart

A coupon's restrictions are only enforced when the request describes a cart, and whether it does is decided from **that coupon's own restrictions** — see [Eligibility checks](#eligibility-checks). The fields that describe a cart fall into two groups, and the group decides how the restriction behaves.

| Group | Fields | Behaviour |
| :- | :- | :- |
| **Cart-level** | `merchantId`, `branchId`, `totalPurchaseAmount` | An order is placed with one merchant at one branch, so these gate the whole basket. They have no per-item form. |
| **Item-level — flat** | `collectionId`, `collectionsIds`, `categories`, `productsIds` | What the basket contains, without saying which item contains what. |
| **Item-level — lines** | `lineItems[]` | The same cart described item by item. |

<Warning>
  Send `lineItems` **or** the flat lists, not both. When `lineItems` is present, the top-level `collectionId`, `collectionsIds`, `categories` and `productsIds` are ignored for matching — put every category, collection and product on the lines instead. The cart-level fields are always read from the top of the body.
</Warning>

<Note>
  `variantId` on a line item is stored and echoed back on the coupon, but is **never evaluated** — there is no variant restriction.
</Note>

### Size limits

`lineItems` accepts at most **500** entries, and any single ID list at most **200** values. A request over either limit is rejected with `400`.

## Example request

```bash cURL theme={null}
curl --request POST \
  --url 'https://api.gameball.co/api/v4.0/integrations/coupons/SONYAUDIO/validate' \
  --header 'APIKey: <your-api-key>' \
  --header 'secretkey: <your-secret-key>' \
  --header 'Content-Type: application/json' \
  --data '{
    "customerId": "omar-9912",
    "lock": true,
    "lockDuration": 30,
    "merchantId": "cairo-downtown",
    "branchId": "branch-14",
    "totalPurchaseAmount": 1450.0,
    "lineItems": [
      {
        "productId": "p-sony-bravia",
        "variantId": "v-55in",
        "categories": ["TV"],
        "collectionsIds": ["electronics"]
      },
      {
        "productId": "p-sony-wh1000",
        "variantId": "v-black",
        "categories": ["Audio"],
        "collectionsIds": ["electronics", "headphones"]
      }
    ]
  }'
```

## Example response

```json theme={null}
{
  "valid": true,
  "coupon": {
    "code": "sonyaudio",
    "type": "percentage",
    "value": 25.0,
    "usageLimit": -1,
    "limitPerCustomer": 1,
    "startDate": "2026-09-01T00:00:00Z",
    "expiryDate": "2026-12-31T23:59:59Z",
    "capping": 500.0,
    "minReward": null,
    "minOrderValue": 200.0,
    "entitledProductIds": ["p-sony-wh1000", "p-sony-wf1000"],
    "entitledVariantIds": null,
    "entitledCollectionIds": null,
    "entitledMerchantIds": ["cairo-downtown"],
    "entitledBranchIds": null,
    "entitledCategoryIds": ["Audio"],
    "couponRulesLogicalOperator": 1,
    "combinesWith": {
      "orderDiscounts": false,
      "productDiscounts": true,
      "shippingDiscounts": true
    }
  },
  "lockReference": "e7c1f2a9-3b44-4d1e-8f77-2c9ab0d51e63",
  "dateToExpire": "2026-09-16T14:15:00Z"
}
```

## How validation works

A validate call answers three questions in order. The first failure ends the call — you get one error, never a list.

<Steps>
  <Step title="Is the customer real and active in your Gameball program?">
    Resolved from `customerId`, `email` or `mobile`.
  </Step>

  <Step title="If you referenced an existing lock, is it still usable by this customer?">
    Resolved from `lockReference`.
  </Step>

  <Step title="Does the coupon exist, does it apply to this cart, and may this customer use it?">
    Runs the ordered checks below.
  </Step>
</Steps>

The third question runs these checks in this exact order. The order matters: a coupon that is both expired *and* wrong for the cart reports the cart problem, because restrictions are checked first.

| Order | Check | Error |
| :- | :- | :- |
| 1 | The coupon code exists for your account | `404`, code `4004` |
| 2 | The coupon's restrictions match the cart | See [Restriction errors](#restriction-errors) |
| 3 | `totalPurchaseAmount` is present and at or above the coupon's `minOrderValue` | `9017` |
| 4 | The coupon is active | `9014` |
| 5 | The coupon's start date has passed | `9011` |
| 6 | The coupon has not expired | `9013` |
| 7 | The customer is in the coupon's audience, and the coupon is not assigned to a different customer | `9011` |
| 8 | The customer has not hit the coupon's per-customer limit | `9012` |
| 9 | The coupon has not hit its total usage limit | `9012` |

## How restrictions are matched

A coupon can carry restrictions in five dimensions, plus a minimum order value. The group a restriction belongs to decides how it behaves.

| Group | Restrictions | Behaviour |
| :- | :- | :- |
| **Cart-level** | merchant, branch | An order is placed with one merchant at one branch, so these gate the whole basket. They must **always** match. |
| **Item-level** | collection, category, product | These vary from one product to the next, so they are the ones `couponRulesLogicalOperator` arbitrates over. |
| **Amount** | `minOrderValue` | Gates the whole basket like a cart-level restriction, but is set on the coupon's configuration rather than as a rule, so `couponRulesLogicalOperator` never applies to it. |

`couponRulesLogicalOperator` is returned on the coupon object and tells you how its **item-level** restrictions combine:

| Value | Mode | Meaning |
| :- | :- | :- |
| `null` | Flat | Every item-level restriction must be satisfied by something in the cart — not necessarily the same item. If the cart carries no data at all for one of them, the call is rejected. |
| `1` | AND | One **single item** must satisfy every item-level restriction. Use this for coupons like "25% off Sony audio", which must not be satisfied by a Sony television plus another brand's headphones. |
| `2` | OR | One satisfied restriction is enough. A restriction the cart carries no data for is skipped, never rejected. |

<Note>
  Merchant and branch are checked in **every** mode, including OR. An OR coupon restricted to one merchant still rejects a different merchant, no matter what else in the cart matches.
</Note>

### When lineItems is required

If a coupon has **two or more** item-level restrictions and `couponRulesLogicalOperator` is `1` (AND), the flat lists cannot answer the question — they show that the basket holds a Sony item *and* an audio item, without showing whether one item is both. The call is rejected with code `9032`, and you resend the same cart as `lineItems`.

In every other case `lineItems` is optional.

### Matching rules

* Coupon codes and restriction values are compared **case-insensitively**, and surrounding whitespace is trimmed.
* Values within a single restriction are alternatives — matching any one of them is enough.
* `collectionId` and `collectionsIds` are evaluated **together** as one list.
* `categories` are free-text names, not IDs — whatever was typed when the coupon was configured, compared as plain strings.
* `variantId` on a line item is stored and echoed back but **never evaluated**.

## Eligibility checks

Whether a call is a cart question or an eligibility question is decided **from the coupon's own restrictions**, not from which fields you happened to send.

The request describes a cart when it carries data for a dimension **this coupon restricts on**. When it does, every restriction the coupon holds is enforced — including ones the request said nothing about. When it does not, the call is an eligibility question — *"does this customer hold this coupon, and is it live?"* — and the restrictions are reported back on the coupon instead, so you can discover what to send next. Every other check (active, dates, audience, limits) runs either way.

A field the coupon does not restrict on is ignored as though it were never sent. Sending `collectionsIds` to a coupon with no collection restriction neither satisfies anything nor triggers anything.

| Coupon restricts on | Request carries | Outcome |
| :- | :- | :- |
| Collection | `customerId` only | Eligibility — `valid: true` |
| Collection, no `minOrderValue` | `customerId`, `totalPurchaseAmount` | Eligibility — the amount matches no restriction, so it is ignored |
| Collection | `collectionsIds` | Cart — the collection restriction is enforced |
| Branch | `collectionsIds` | Eligibility — a collection matches no restriction on this coupon |
| Merchant, collection, product | `merchantId`, `collectionsIds` | Cart — all three are enforced, so the absent product is rejected with `9030` |
| Collection, plus `minOrderValue` | `totalPurchaseAmount` | Cart — the amount speaks to the minimum, so the collection restriction is enforced too |

<Note>
  **A restriction you send that the coupon does not carry is ignored.**

  Request data is matched to restrictions **by type**. `collectionsIds` is only ever checked against a collection restriction, `merchantId` against a merchant restriction, `totalPurchaseAmount` against `minOrderValue`, and so on. When the coupon holds no restriction of that type, the field is dropped before matching — it cannot satisfy a different restriction, and it cannot cause a rejection.

  The coupon below restricts on collection only. Every other field in the request corresponds to nothing on it, so all of them are discarded, the call stays an eligibility check, and it returns `valid: true`.

  ```json Request theme={null}
  {
    "customerId": "sara-1180",
    "merchantId": "cairo-downtown",
    "branchId": "branch-14",
    "categories": ["Outerwear"],
    "productsIds": ["p-parka-navy"],
    "totalPurchaseAmount": 1450.0
  }
  ```

  ```json Response theme={null}
  {
    "valid": true,
    "coupon": {
      "code": "winter25",
      "entitledCollectionIds": ["winter-24"],
      "entitledMerchantIds": null,
      "entitledBranchIds": null,
      "entitledCategoryIds": null,
      "entitledProductIds": null,
      "minOrderValue": null,
      "couponRulesLogicalOperator": null,
      "...": "..."
    },
    "lockReference": null,
    "dateToExpire": null
  }
  ```

  No collection was sent, and nothing else in the body speaks to a restriction this coupon carries — so there is nothing to enforce, and nothing to reject. Send `collectionsIds` and the same call becomes a cart question.
</Note>

<Warning>
  `valid: true` on an eligibility call means *"this customer may use this coupon"*, **not** *"this coupon applies to this order"*. Read `entitledCollectionIds`, `entitledCategoryIds`, `entitledBranchIds`, `entitledMerchantIds`, `entitledProductIds`, `minOrderValue` and `couponRulesLogicalOperator` off the response to see what the cart will have to satisfy, then validate again with those fields populated before you complete the order. That second call can reject a coupon that just came back valid.
</Warning>

### The minimum order value

`minOrderValue` behaves like any other restriction. `totalPurchaseAmount` is matched against it and nothing else, so on a coupon with no `minOrderValue` the amount is ignored outright — it cannot make the call a cart question. On a coupon that has one, the amount is enough to describe a cart on its own, and the minimum is then enforced whether or not you sent a value.

| Coupon | Request describes a cart | `totalPurchaseAmount` | Result |
| :- | :- | :- | :- |
| `minOrderValue: 80` | Yes | `100` | Passes |
| `minOrderValue: 80` | Yes | `50` | `9017` |
| `minOrderValue: 80` | Yes | not sent | `9017` |
| `minOrderValue: 80` | No | not sent | Eligibility — not checked |
| `minOrderValue: null` | Either | anything | Not checked — there is no minimum to meet |

<Warning>
  A cart that omits `totalPurchaseAmount` no longer slips past a coupon that has a `minOrderValue`. Both "you sent too little" and "you sent nothing" return `9017`; read the `message` to tell them apart.
</Warning>

## Locking a coupon

Set `lock` to `true` to reserve the coupon when it validates. The response carries a `lockReference` and a `dateToExpire`, and the coupon cannot be validated-and-locked by another order until the lock expires or is consumed.

<Note>
  A coupon that is already locked still returns `valid: true` when this request does not lock it. Omit `lock`, `lockReference`, and `lockDuration` — for example, do not send `"lock": true`, `"lockReference": "123"`, and `"lockDuration": 1440`. Read `isLocked` on [Get Customer Coupons](/api-reference/customers/management/get-customer-coupons) to see the current lock state.
</Note>

| Setting | Effect |
| :- | :- |
| `lock: false` (default) | Validation only. `lockReference` and `dateToExpire` come back `null`. |
| `lock: true`, no `lockReference` | A new lock is created and its reference is returned. |
| `lock: true` with an existing `lockReference` | The existing lock is reused and the coupon is added to it. If the coupon is already in that lock, the lock is overwritten rather than rejected. |
| `lockDuration` | Overrides your account's default lock duration, in minutes. Must be greater than `0` and less than `21600` (15 days). |

When you pass an existing `lockReference` it is checked before anything else, and must be unused, unexpired and belong to the same customer — otherwise the call fails with `9009` or `9010`.

A lock is only ever created **after** the coupon has passed validation, so a returned `lockReference` always means the coupon was usable by that customer at that moment.

### Locking without a cart

You can lock on an eligibility check. Send only `customerId` with `lock: true` and no cart fields: a coupon that carries restrictions still validates — its restrictions are reported back rather than enforced — and it is still locked against that customer. The lock is built from the coupon code and the customer alone; the cart plays no part in it.

```json theme={null}
{
  "customerId": "sara-1180",
  "lock": true,
  "lockDuration": 30
}
```

This is the intended way to reserve a coupon before the basket is known. Read `entitledCollectionIds`, `entitledCategoryIds`, `entitledBranchIds`, `entitledMerchantIds`, `entitledProductIds` and `couponRulesLogicalOperator` off the response to see what the cart will have to satisfy.

<Warning>
  A cart-less lock does **not** prove the coupon fits the eventual cart. Restrictions were never evaluated, and nothing re-evaluates them later — not when the lock is reused, and not when the order is placed. Validate again with the full cart, reusing the same `lockReference`, before you complete the order: that re-runs the restriction checks against the real basket and keeps the same lock.
</Warning>

## Errors

Errors use the standard Gameball [error envelope](/api-reference/overview/status-error-codes). The `message` names the specific values that failed, so it is the most useful field for diagnosing a rejection. It is returned in lower case.

```json theme={null}
{
  "code": 9026,
  "type": "TRANSACTION_ERROR",
  "message": "this coupon requires a valid category to be applied. no category was provided.",
  "documentationUrl": "https://docs.gameball.co/api-reference/coupons/validate-single-coupon",
  "requestId": "3f9c21b8a4d7"
}
```

### By status

| Status | When |
| :- | :- |
| `400 Bad Request` | Invalid request payload, a missing required field, or a cart over the size limits — more than 500 `lineItems`, or more than 200 values in any single ID list. |
| `401 Unauthorized` | Missing or invalid API key or secret key. |
| `404 Not Found` | The customer does not exist, the coupon code does not exist, or the `lockReference` does not exist. |
| `422 Unprocessable Entity` | The coupon exists but cannot be applied. |
| `429 Too Many Requests` | Rate limit exceeded. |
| `500 Internal Server Error` | An unexpected error occurred while validating. |

### Customer and request errors

| Code | Status | Meaning |
| :- | :- | :- |
| `7000` | 404 | The customer was not found for the provided ID, email or mobile. |
| `3009` | 422 | The customer exists but is not active in your Gameball program. |
| `3000` | 400 | A required field is missing, the coupon code is empty, or the cart exceeded the size limits. |
| `4004` | 404 | The coupon code or `lockReference` does not exist. |

### Coupon eligibility errors

All `422`.

| Code | Meaning |
| :- | :- |
| `9011` | The coupon is not applicable — the customer is outside its audience, it is assigned to a different customer, or it has not started yet. |
| `9012` | A usage limit was reached — either the coupon's total limit or this customer's per-customer limit. |
| `9013` | The coupon has expired. |
| `9014` | The coupon is inactive. |
| `9017` | `totalPurchaseAmount` is below the coupon's `minOrderValue`, or the request described a cart and omitted it while the coupon has one. |

### Restriction errors

All `422`. A *required* code means the coupon restricts that dimension and your request described nothing for it. A *not eligible* code means you described it and the value was not on the coupon's list.

| Code | Restriction | Raised in |
| :- | :- | :- |
| `9018` | Merchant — none provided | Every mode |
| `9019` | Merchant — not eligible | Every mode |
| `9024` | Branch — none provided | Every mode |
| `9025` | Branch — not eligible | Every mode |
| `9020` | Collection — none provided | Flat and AND |
| `9021` | Collection — not eligible | Flat, AND, and single-restriction OR |
| `9026` | Category — none provided | Flat and AND |
| `9027` | Category — not eligible | Flat, AND, and single-restriction OR |
| `9030` | Product — none provided | Flat and AND |
| `9031` | Product — not eligible | Flat, AND, and single-restriction OR |
| `9022` | Several restrictions were checked and none was satisfied | Multi-restriction OR only |
| `9032` | The coupon must be evaluated against individual cart items — resend with `lineItems` | AND only |
| `9033` | `lineItems` was sent, every restriction is met somewhere, but no single item meets them all | AND only |

An OR coupon never returns a *required* code for a collection, category or product — a restriction with no cart data is skipped. It can still return `9018` or `9024`, because merchant and branch are checked in every mode.

### Lock errors

| Code | Status | Meaning |
| :- | :- | :- |
| `9009` | 422 | The `lockReference` is invalid, has expired, or belongs to a different customer. |
| `9010` | 422 | The `lockReference` has already been used. |
| `9015` | 422 | `lockDuration` is negative. |
| `4000` | 400 | `lockDuration` is outside the permitted range — it must be greater than 0 and less than 21,600 minutes. |

<Note>
  A `lockDuration` outside the permitted range is currently reported as the generic `4000` rather than the dedicated `9016`. Read the `message` for the specific reason.
</Note>

## What changed

This endpoint previously matched a coupon against two things only: the **merchant** and the **collection**. It now matches against four, and the request can describe the cart line by line.

| Area | Change |
| :- | :- |
| Request | New `branchId`, `categories`, `productsIds` and `lineItems[]` |
| Response | New `entitledBranchIds`, `entitledCategoryIds` and `couponRulesLogicalOperator` |
| Response | `entitledProductIds` is no longer limited to a single value |
| Errors | New `422` codes: `9024`–`9027` and `9030`–`9033` |

<AccordionGroup>
  <Accordion title="Behaviour changes on existing requests">
    These affect calls you are already making, with no change on your side.

    | What | Before | Now |
    | :- | :- | :- |
    | A coupon restricted by branch, category or product | The restriction was stored but not enforced | Enforced |
    | A coupon with two or more restrictions, where your cart described only one of them | Accepted | Rejected with the matching *required* code |
    | A coupon restricted by merchant, with `couponRulesLogicalOperator` = `2` (OR) | The wrong merchant could still pass if another restriction matched | The merchant must always match |
    | Sending `collectionId` **and** `collectionsIds` together | Only `collectionsIds` was evaluated | Both are evaluated as one list |
    | A validate call sending **no** cart fields at all | Rejected with `9018` | Returns `200` with the coupon's restrictions attached |
  </Accordion>

  <Accordion title="Restriction matching is now decided per coupon">
    Previously any cart field switched on enforcement of **every** restriction the coupon held. A body carrying nothing but `customerId` and `totalPurchaseAmount` was enough to have a collection-restricted coupon rejected with `9020`, for a dimension the caller never meant to describe.

    Enforcement is now decided from the coupon's own restrictions. These affect calls you are already making, with no change on your side.

    | What | Before | Now |
    | :- | :- | :- |
    | A cart field the coupon does not restrict on | Switched on enforcement of the coupon's other restrictions | Ignored as though it were never sent |
    | `totalPurchaseAmount` alone, coupon has no `minOrderValue` | Enforced every restriction; a restricted coupon was rejected | Eligibility check — `valid: true` with restrictions attached |
    | `totalPurchaseAmount` alone, coupon **has** a `minOrderValue` | Enforced every restriction | Unchanged — the amount speaks to the minimum, so it describes a cart |
    | A cart describing some of a coupon's restrictions | Enforced all of them | Unchanged — the missing ones still return their *required* code |
    | A cart omitting `totalPurchaseAmount`, coupon has a `minOrderValue` | Accepted — the minimum was skipped | Rejected with `9017` |

    Error precedence, the `couponRulesLogicalOperator` modes and every error code are unchanged.
  </Accordion>

  <Accordion title="Migration notes">
    * **Add `branchId` if your coupons are branch-restricted.** There is no other way to satisfy a branch restriction.
    * **Choose between `lineItems` and the flat lists.** Sending both is not an error, but the flat lists are silently ignored. If you are unsure, send `lineItems` — it answers every case the flat lists do, plus the AND case they cannot.
    * **Handle `9032` by retrying with `lineItems`.** It is not a permanent failure; it is a request for more detail about the same cart.
    * **API v3 cannot satisfy the new restrictions.** `POST /api/v3.0/integrations/coupons/{code}/validate` accepts only `merchantId` and `collectionId`, so a coupon restricted by branch, category or product is rejected with no field available to fix it. Move to v4 before configuring those restrictions.
  </Accordion>
</AccordionGroup>


## OpenAPI

````yaml POST /api/v4.0/integrations/coupons/{code}/validate
openapi: 3.1.0
info:
  title: Gameball API
  description: >-
    Gameball REST API v4.0 - Complete API reference for integrating loyalty,
    gamification, and customer engagement features
  version: 4.0.0
servers:
  - url: https://api.gameball.co
security:
  - bearerAuth: []
paths:
  /api/v4.0/integrations/coupons/{code}/validate:
    post:
      description: >-
        Validates a single coupon identified by {code} and checks its
        eligibility for use by a customer. Describe the cart as well, and the
        coupon is additionally matched against its merchant, branch, collection,
        category and product restrictions. Set the lock flag to True to reserve
        the coupon by creating a lock reference, so it cannot be used by others
        while the order is being completed. By default the lock flag is False,
        allowing only validation without reserving the coupon.
      parameters:
        - name: code
          in: path
          required: true
          schema:
            type: string
          description: The coupon code you want to validate. Compared case-insensitively.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - customerId
              properties:
                customerId:
                  type: string
                  description: >-
                    Unique identifier for the customer that you can reference
                    across the customer's whole lifetime. Could be a database
                    ID, random string, email or anything that uniquely
                    identifies the customer.
                email:
                  type: string
                  description: >-
                    Customer's email address. Required if your account uses
                    email-based channel merging.
                mobile:
                  type: string
                  description: >-
                    Customer's mobile number. Required if your account uses
                    mobile-based channel merging.
                lock:
                  type: boolean
                  description: >-
                    Indicates whether the request is intended to validate the
                    coupon or to lock it for a future redemption.
                lockReference:
                  type: string
                  description: >-
                    Required only if the lock flag is set to True and you need
                    to validate and lock a new or updated list of coupons within
                    an existing lock session.
                lockDuration:
                  type: integer
                  description: >-
                    Represents the number of minutes for which a coupon will be
                    locked if the lock flag is set to True.
                merchantId:
                  type: string
                  description: >-
                    Cart-level. The merchant the order is placed with. Required
                    if the coupon is restricted to specific merchants.
                  example: cairo-downtown
                branchId:
                  type: string
                  description: >-
                    Cart-level. The branch or outlet the order is placed at.
                    Required if the coupon is restricted to specific branches.
                    There is no plural or per-item form.
                  example: branch-14
                collectionId:
                  type: string
                  description: >-
                    Item-level. A collection the basket touches. Evaluated
                    together with collectionsIds as a single list. Ignored when
                    lineItems is present.
                collectionsIds:
                  type: array
                  items:
                    type: string
                  maxItems: 200
                  description: >-
                    Item-level. Every collection the basket touches. Maximum 200
                    values. Ignored when lineItems is present.
                categories:
                  type: array
                  items:
                    type: string
                  maxItems: 200
                  description: >-
                    Item-level. Every category the basket touches. These are
                    free-text category names, not IDs. Maximum 200 values.
                    Ignored when lineItems is present.
                  example:
                    - Audio
                    - TV
                productsIds:
                  type: array
                  items:
                    type: string
                  maxItems: 200
                  description: >-
                    Item-level. Every product ID in the basket. Maximum 200
                    values. Ignored when lineItems is present.
                lineItems:
                  type: array
                  maxItems: 500
                  description: >-
                    The cart described item by item. Maximum 500 entries.
                    Required for a coupon that must satisfy two or more
                    item-level restrictions on the same item. When lineItems is
                    present, the flat collectionId, collectionsIds, categories
                    and productsIds fields are ignored for matching.
                  items:
                    type: object
                    properties:
                      productId:
                        type: string
                        maxLength: 100
                        description: This line's product ID.
                      variantId:
                        type: string
                        maxLength: 100
                        description: >-
                          This line's variant ID. Stored and echoed back, but
                          never evaluated - there is no variant restriction.
                      categories:
                        type: array
                        items:
                          type: string
                        maxItems: 200
                        description: The categories this product belongs to.
                      collectionsIds:
                        type: array
                        items:
                          type: string
                        maxItems: 200
                        description: The collections this product belongs to.
                totalPurchaseAmount:
                  type: number
                  description: >-
                    Cart-level. The total value of the purchase the coupon will
                    be applied to. Checked against the coupon's minOrderValue.
                    Sending this field describes a cart only when the coupon
                    carries a minOrderValue; for those coupons it switches on
                    full restriction enforcement, and omitting it is rejected
                    the same way a value below the minimum is.
      responses:
        '200':
          description: Coupon validated successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  valid:
                    type: boolean
                    description: >-
                      Indicates whether the coupon is valid to be used by the
                      customer or not.
                  coupon:
                    $ref: '#/components/schemas/ValidatedCoupon'
                  lockReference:
                    type: string
                    description: >-
                      The unique reference code associated with the coupon lock
                      session.
                  dateToExpire:
                    type: string
                    format: date-time
                    description: The exact date and time when the coupon lock will expire.
      security:
        - apiKey: []
          secretKey: []
components:
  schemas:
    ValidatedCoupon:
      type: object
      description: >-
        A coupon as returned by the validate endpoints, including the
        restrictions it carries and how they combine.
      properties:
        code:
          type: string
          description: The coupon code the customer uses. Returned in lower case.
        type:
          type: string
          enum:
            - shipping
            - fixed
            - percentage
            - product
            - percentage-fees
            - fixed-cashback
            - percentage-cashback
          description: >-
            Type of the coupon, such as fixed amount, percentage discount, free
            shipping, cashback or product-specific coupon.
        value:
          type: number
          description: >-
            The coupon's amount - a percentage for percentage types, a currency
            amount for fixed types.
        usageLimit:
          type: number
          description: >-
            The total number of times the coupon can be used across all
            customers. -1 means unlimited.
        limitPerCustomer:
          type: number
          description: >-
            The number of times a single customer can use the coupon. -1 means
            unlimited.
        startDate:
          type: string
          format: date-time
          description: When the coupon becomes usable. null means immediately.
        expiryDate:
          type: string
          format: date-time
          description: When the coupon stops being usable. null means never.
        capping:
          type: number
          description: >-
            The maximum discount the coupon can produce, applied on top of
            percentage calculations.
        minReward:
          type: number
          description: >-
            The minimum discount a customer is guaranteed to receive on a
            percentage-based coupon.
        minOrderValue:
          type: number
          description: >-
            The minimum order value the coupon requires, evaluated against
            totalPurchaseAmount.
        entitledProductIds:
          type: array
          items:
            type: string
          description: Products the coupon applies to. null means unrestricted.
        entitledVariantIds:
          type: array
          items:
            type: string
          description: >-
            Variants named on the coupon. Informational - variants are not
            matched against the cart.
        entitledCollectionIds:
          type: array
          items:
            type: string
          description: Collections the coupon applies to. null means unrestricted.
        entitledMerchantIds:
          type: array
          items:
            type: string
          description: >-
            Merchant external IDs the coupon may be used with. null means
            unrestricted.
        entitledBranchIds:
          type: array
          items:
            type: string
          description: Branches the coupon may be used at. null means unrestricted.
        entitledCategoryIds:
          type: array
          items:
            type: string
          description: >-
            Categories the coupon applies to. These are free-text names, despite
            the field name. null means unrestricted.
        couponRulesLogicalOperator:
          type: integer
          enum:
            - 1
            - 2
          description: >-
            How the coupon's item-level restrictions combine: 1 = AND (one
            single item must satisfy them all), 2 = OR (one satisfied
            restriction is enough), null = flat (each must be satisfied by
            something in the cart, not necessarily the same item).
        combinesWith:
          type: object
          description: Which other discount types this coupon may be combined with.
          properties:
            orderDiscounts:
              type: boolean
              description: >-
                Indicates if the coupon can be combined with order-level
                discounts.
            productDiscounts:
              type: boolean
              description: >-
                Indicates if the coupon can be combined with product-specific
                discounts.
            shippingDiscounts:
              type: boolean
              description: Indicates if the coupon can be combined with shipping discounts.
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
    apiKey:
      type: apiKey
      in: header
      name: apikey
    secretKey:
      type: apiKey
      in: header
      name: secretkey

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.