---
title: "Create Discount"
method: POST
path: "/workspaces/{workspace_id}/discounts"
tags: ["Discount"]
---

# Create Discount

`POST /workspaces/{workspace_id}/discounts`

## Request

- Base URL: `https://myworkspace.myclickfunnels.com/api/v2`
- URL: `https://myworkspace.myclickfunnels.com/api/v2/workspaces/{workspace_id}/discounts`
- Auth: one of:
  - HTTP bearer
  - OAuth 2
    - Authorization code: `https://accounts.myclickfunnels.com/oauth/authorize`
    - Token URL: `https://accounts.myclickfunnels.com/oauth/token`
- TypeScript: <https://skmtc.dev/myclickfunnels/apis/clickfunnels-api/docs/workspaces/:workspace_id/discounts/post.ts> (a function with typed inputs; its response is checked against the schema)

Create a discount in a workspace. A `code`-type discount (the default) needs a unique `code`; a `percentage` discount needs `percent`; a `fixed` discount needs a positive `amount`.

Creating a discount publishes the code; it does not attach it to anything. The link between an order and a discount is a separate applied-discount record, written when a buyer redeems the code at checkout or when a ClickFunnels user applies it in the admin UI - which is why `POST /api/v2/workspaces/{workspace_id}/orders` takes no discount field and the `discount_ids` on an order are read-only. Writing applied discounts is not part of this API yet, and when it lands it will be that record you create rather than a discount field on the order. Until then an integration creates, reads, updates, expires and deletes discounts, and reads back which orders ended up carrying them.

## Path parameters

- `workspace_id` string, required

## Request body

- object
  - `discount` DiscountParameters: Fields accepted when creating a discount.
    - `name` string, required: Internal name for the discount.
    - `code` string: Required for a `code`-type discount. Letters, numbers and hyphens only - spaces, underscores and punctuation return 422. Uppercased on save; must be unique within the workspace (it cannot collide with a coupon code either).
    - `discount_type` 'code' | 'automatic' | 'sale': Defaults to `code`.
    - `discount_method` 'percentage' | 'fixed' | 'shipping' | 'bogo': Defaults to `percentage`. `percentage` requires `percent`; `fixed` requires a positive `amount`.
    - `percent` string: Percentage off (0.01-100). Required when `discount_method` is `percentage`.
    - `amount` string: Fixed amount off as a decimal string. Required when `discount_method` is `fixed`.
    - `currency` string: ISO currency code. Defaults to the workspace currency.
    - `applies_to` 'all_products' | 'specific_products' | 'specific_variants' | 'specific_collections': Defaults to `all_products`.
    - `apply_from` string, date-time: When the discount becomes active. Defaults to now. Set in the future to schedule it.
    - `apply_until` string, date-time: When the discount expires. Omit for no expiry. There is no `expired` create field - sending one returns 422 whatever its value, including `false`, because expiring on create can never succeed (it would set `apply_until` to now, and `apply_from` then defaults to a hair later). Create the discount first, then expire it with `PATCH { "discount": { "expired": true } }`.
    - `limit_1_per_customer` boolean
    - `require_minimum_spend` boolean
    - `require_minimum_spend_amount` integer: Minimum order subtotal, in whole currency units rather than cents - `50` on a `usd` discount means $50. Must be positive when `require_minimum_spend` is true.
    - `discounts_per_redemption` integer
    - `product_ids` integer[]: Product ids to scope to, when `applies_to` is `specific_products`.
    - `variant_ids` integer[]: Variant ids to scope to, when `applies_to` is `specific_variants`.
    - `products_collection_ids` integer[]: Collection ids to scope to, when `applies_to` is `specific_collections`.

## Response `201`

Created

- DiscountAttributes: A reusable, workspace-scoped discount applied on top of a price.
  - `id` integer: Discount ID
  - `public_id` string, nullable: Discount public ID
  - `workspace_id` integer: Workspace ID
  - `name` string: Internal name for the discount.
  - `code` string: The code a buyer redeems at checkout. Uppercased on save; unique within the workspace (cannot collide with a coupon code).
  - `discount_type` 'code' | 'automatic' | 'sale': `code` (the default) is a redeemable coupon code. `automatic` and `sale` are deprecated.
  - `discount_method` 'percentage' | 'fixed' | 'shipping' | 'bogo': How the discount is calculated. `percentage` uses `percent`; `fixed` uses `amount`. `shipping` and `bogo` exist but are outside the simple flow.
  - `amount` string, nullable: Fixed amount off, as a decimal string (e.g. "10.00"). `null` for a percentage discount.
  - `percent` string, nullable: Percentage off between 0.01 and 100 (e.g. "20.0"). `null` for a fixed discount.
  - `currency` string: ISO currency code, lowercased. Defaults to the workspace currency.
  - `applies_to` 'all_products' | 'specific_products' | 'specific_variants' | 'specific_collections': Scope of the discount. `all_products` (the default) applies workspace-wide; the `specific_*` values restrict it to the ids passed in `product_ids` / `variant_ids` / `products_collection_ids`.
  - `apply_from` string, date-time: When the discount becomes active. Defaults to the creation time.
  - `apply_until` string, date-time, nullable: When the discount expires. `null` means it never expires.
  - `limit_1_per_customer` boolean: When true, each contact can redeem the discount only once.
  - `require_minimum_spend` boolean: When true, the order must meet `require_minimum_spend_amount` for the discount to apply.
  - `require_minimum_spend_amount` integer, nullable: Minimum order subtotal required, when `require_minimum_spend` is true. In whole currency units, not cents - `50` on a `usd` discount means $50.
  - `discounts_per_redemption` integer, nullable: Optional cap on how many line items a single redemption discounts.
  - `redemptions_count` integer: How many times the discount has been redeemed on an invoice. On Stripe every invoice counts, renewals included; on Payments AI each customer redemption counts once, however many renewal invoices it covers. This is not a deletability signal - a discount can report zero redemptions and still return 422 on DELETE because it is applied to an order that has no invoice yet.
  - `product_ids` integer[]: The products the discount is scoped to (empty unless `applies_to` is `specific_products`).
  - `variant_ids` integer[]: The variants the discount is scoped to (empty unless `applies_to` is `specific_variants`).
  - `products_collection_ids` integer[]: The collections the discount is scoped to (empty unless `applies_to` is `specific_collections`).
  - `active` boolean: Derived - true when the discount is neither scheduled for the future nor expired.
  - `expired` boolean: Derived - true when `apply_until` is in the past. An expired discount is frozen - every further update returns 422, so rename before you expire.
  - `scheduled` boolean: Derived - true when `apply_from` is in the future.
  - `created_at` string, date-time, nullable: Added
  - `updated_at` string, date-time, nullable: Updated

## Other responses

- `401`: Unauthorized
- `403`: Forbidden - the access token's scopes do not cover this request
- `404`: Not Found
- `422`: Unprocessable Entity - validation failed

## Changes

- **2026-11-03** `511d88b30aea`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/myclickfunnels/apis/clickfunnels-api/changes/workspaces/:workspace_id/discounts/post.md)

---

[API](https://skmtc.dev/myclickfunnels/apis/clickfunnels-api.md) - [All operations](https://skmtc.dev/myclickfunnels/apis/clickfunnels-api/llms.txt) - [OpenAPI document](https://skmtc.dev/myclickfunnels/apis/clickfunnels-api/revisions/511d88b30aea?raw)
