---
title: "Create or update a coupon"
method: POST
path: "/coupons"
tags: ["Coupons"]
---

# Create or update a coupon

`POST /coupons`

Creates a new coupon or updates an existing one. The operation is
determined automatically based on whether the `coupon` path identifier
already exists — no separate update endpoint is required.

The `action` field in the response confirms which operation was
performed: `coupon.create` or `coupon.update`.

**Warning:** Including the `codes` array in an update request
permanently replaces all existing codes. Omit the `codes` field to
preserve existing codes.

## Request body

- CouponRequest
  - `coupon` string, required — Unique coupon path identifier. Accepts alphanumeric characters, hyphens, and underscores only.
  - `orderLevelDiscount` boolean — **(Beta)** Applies the discount to the entire order subtotal rather than individual line items. The discount is proportionally allocated across eligible items, excluding add-ons and fees. Taxes are calculated after the discount is applied. **This field is part of the closed, invite-only Order-Level Coupons beta.** Only accounts enrolled in the beta can set this field to `true`. When `true`, the following values are server-enforced regardless of what is sent in the request: | Field | Enforced value | |---|---| | `combine` | `true` | | `applyDiscountImmediately` | `true` | | `discountPeriodCount` | `1` | The `discount.type` must be `flat` when `orderLevelDiscount` is `true`.
  - `hasMultiDiscount` boolean — Enables multi-tier discount mode for tiered subscription offers. When `true`, the `discounts` array is required in place of the `discount` object. Cannot be `true` if `orderLevelDiscount` is `true`.
  - `discount` DiscountConfig — Discount type and amount configuration.
    - `type` 'percent' | 'flat' — Discount type. **(Beta)** Must be `flat` when `orderLevelDiscount` is `true`.
    - `percent` number — Percentage amount to discount. Required when `type` is `percent`. Decimal values are accepted.
    - `amount` object — Per-currency flat discount amounts. Required when `type` is `flat`. All values must be greater than `0`.
      - `USD` number, float — US Dollar amount.
      - `EUR` number, float — Euro amount.
      - `GBP` number, float — British Pound amount.
  - `discounts` MultiDiscountTier[] — Array of multi-tier discount configurations. Required when `hasMultiDiscount` is `true`. Tiers are automatically sorted descending by duration. Constraints: - Must contain between 2 and 25 tiers. - A product path may only appear in one tier. - At most one tier may omit the `products` array (applies to all products).
    - `type` 'percent' | 'flat' — Discount type for this tier.
    - `percent` number — Percentage amount for this tier. Required when `type` is `percent`. Decimal values are accepted.
    - `amount` object — Per-currency flat discount amounts for this tier. Required when `type` is `flat`.
      - `USD` number, float
      - `EUR` number, float
      - `GBP` number, float
    - `products` string[] — Product path identifiers this tier applies to. Omit to apply this tier to all products. At most one tier per coupon may omit this field.
    - `discountPeriodCount` integer — Number of billing periods this tier's discount applies to.
    - `applyDiscountImmediately` boolean — Applies this tier's discount starting in the first billing period rather than the next renewal period.
  - `discountPeriodCount` integer — Number of billing periods the discount applies to. Accepts values from `0` to `365`. `0` is treated as unlimited and stored as `null` in the response. Ignored and forced to `1` when `orderLevelDiscount` is `true`.
  - `applyDiscountImmediately` boolean — Applies the discount starting in the first billing period rather than the next renewal period. Requires a feature flag on your account. Ignored and forced to `true` when `orderLevelDiscount` is `true`.
  - `combine` boolean — Allows this discount to stack with other active discounts. Forced to `true` when `orderLevelDiscount` is `true`.
  - `autoSelectDiscount` boolean — Automatically selects the most favorable discount for the customer when multiple discounts are applicable. Only applies when `combine` is `false`. Requires a feature flag on your account. **Note:** This field is accepted without error but has no effect until the feature flag is enabled. It is not returned in GET responses.
  - `reason` object — Localized description of the discount, visible to customers at checkout. Provide each translation as a key-value pair using the ISO 639-1 language code as the key (e.g. `en`, `de`, `fr`).
    - `en` string — English localization.
  - `limit` integer — Maximum number of times this discount can be applied across all customers and codes. `0` means unlimited. **Note:** This value is returned as a string in GET responses. `0` sent → `""` received. Both represent unlimited.
  - `available` Availability — Active date range for the coupon. When omitted, the coupon has no date restrictions. Returns `{}` in GET responses when not configured. Accepted date formats for both `start` and `end`: - ISO 8601: `2026-06-01T00:00:00Z` - Date only: `2026-06-01` - Date and time: `2026-06-01 00:00` Validation rules: - `start` must not be a past date. - `end` must not be a past date. - `end` must be after `start`.
    - `start` string — Start date and time for coupon validity.
    - `end` string — End date and time for coupon validity. Must be in the future and after `start`.
  - `codes` string[] — Coupon codes that activate this discount at checkout. **Code handling** - **Case:** Codes are normalized to uppercase on storage. `summer10` and `SUMMER10` are stored as the same code. - **Duplicates:** Duplicate codes within this array are accepted without error and echoed verbatim in the response, but storage silently dedupes (case-insensitively) to a single entry per unique code. Call `GET /coupons/{coupon_id}/codes` after the write to confirm the stored state. - **Format:** Alphanumeric characters, hyphens, and underscores only. Spaces and other special characters are rejected.
  - `products` string[] — Product path identifiers this discount applies to. An empty array applies the discount to all eligible products.

## Response `200`

Returned for both successful operations and validation errors.
Always check the `result` field to determine the outcome.

- union
  - UpsertSuccessResponse
    - `coupon` string — Path identifier of the coupon that was created or updated.
    - `action` 'coupon.create' | 'coupon.update' — Indicates whether the coupon was created or updated. Use this field to determine which operation was performed.
    - `result` 'success' — Indicates the operation succeeded.
  - ErrorResponse — Describes a structured error triggered by an invalid request.
    - `status` string — The HTTP status code combined with a reason phrase indicating the class of the failure.
    - `timestamp` string — The ISO 8601 formatted timestamp marking the exact occurrence of the error.
    - `id` string — A unique platform-generated identifier tracking this specific error instance for debugging.
    - `message` string — A generalized, human-readable summary detailing the root cause of the error.
    - `errors` object[] — A collection of specific validation failures mapping to discrete fields.
      - `errorCode` string — A distinct string code mapping to the specific rule that failed.
      - `field` string — The dot-notated path explicitly pinpointing the payload property responsible for the error.
      - `message` string — A specific human-readable explanation of why the mapped field failed validation.
      - `rejectedValue` string — The raw value submitted within the field that triggered the rejection.

## Other responses

- `400` — Returned when the request body contains unknown or unsupported fields. Returns `DeserializationErrorResponse`.

---

[API](https://skmtc.dev/fastspring/apis/fastspring-api-accounts.md) · [All operations](https://skmtc.dev/fastspring/apis/fastspring-api-accounts/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/fastspring/fastspring-api-accounts/revisions/ae2f0b97db3e/schema)
