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

# Validate Promotion Code

> Validate a promotion code and calculate the discount

This is the main endpoint for booking engines to validate promotion codes during checkout.

<ParamField header="Authorization" type="string" required>
  Bearer token with `marketing:read` scope
</ParamField>

## Request body

<ParamField body="code" type="string" required>
  The promotion code to validate
</ParamField>

<ParamField body="customer_id" type="string">
  Customer ID to check per-customer usage limits
</ParamField>

<ParamField body="product_ids" type="array">
  Array of product IDs in the order (for product-specific promotions)
</ParamField>

<ParamField body="category_ids" type="array">
  Array of category IDs in the order (for category-specific promotions)
</ParamField>

<ParamField body="order_total_minor" type="integer">
  Order total in minor units (cents) to calculate discount
</ParamField>

<ParamField body="currency" type="string">
  3-letter ISO currency code
</ParamField>

## Response

<ResponseField name="valid" type="boolean">
  Whether the code is valid
</ResponseField>

<ResponseField name="error" type="string">
  Error code if invalid (see table below)
</ResponseField>

<ResponseField name="message" type="string">
  Human-readable error message if invalid
</ResponseField>

<ResponseField name="promotion" type="object">
  Promotion details if valid
</ResponseField>

<ResponseField name="code" type="object">
  Code details if valid
</ResponseField>

<ResponseField name="discount_amount_minor" type="integer">
  Calculated discount in minor units (if order\_total\_minor provided)
</ResponseField>

<ResponseField name="currency" type="string">
  Currency of the discount
</ResponseField>

## Error codes

| Error                        | Description                                |
| ---------------------------- | ------------------------------------------ |
| `invalid_code`               | Code not found                             |
| `inactive_code`              | Code is not active                         |
| `not_yet_valid`              | Code validity period hasn't started        |
| `expired_code`               | Code has expired                           |
| `max_uses_reached`           | Code has reached maximum uses              |
| `promotion_not_found`        | Parent promotion not found                 |
| `promotion_inactive`         | Promotion is not active                    |
| `promotion_not_yet_valid`    | Promotion validity period hasn't started   |
| `promotion_expired`          | Promotion has expired                      |
| `promotion_max_uses_reached` | Promotion has reached maximum uses         |
| `below_minimum_order`        | Order doesn't meet minimum value           |
| `currency_mismatch`          | Order currency doesn't match promotion     |
| `product_excluded`           | Products in order are excluded             |
| `category_excluded`          | Categories in order are excluded           |
| `product_not_applicable`     | Products not eligible for this promotion   |
| `category_not_applicable`    | Categories not eligible for this promotion |
| `customer_max_uses_reached`  | Customer has used this code maximum times  |

```bash Request theme={null}
curl -X POST "https://api.voyantcloud.com/v1/promotions/validate" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "code": "SUMMER20",
    "customer_id": "cust_123",
    "order_total_minor": 150000,
    "currency": "EUR"
  }'
```

```json Response (Valid) theme={null}
{
  "valid": true,
  "promotion": {
    "id": "promo_abc123",
    "name": "Summer Sale 2024",
    "type": "percentage",
    "value": "20",
    "scope": "all_products",
    "stackable": false,
    "max_discount_minor": 50000
  },
  "code": {
    "id": "code_xyz",
    "code": "SUMMER20",
    "uses_remaining": 850
  },
  "discount_amount_minor": 30000,
  "currency": "EUR"
}
```

```json Response (Invalid) theme={null}
{
  "valid": false,
  "error": "below_minimum_order",
  "message": "Minimum order value is 100.00 EUR",
  "minimum_order_minor": 10000
}
```
