Skip to main content

Create Promotion Code

Create a new promotion code in your store.

Stripe connection required

The store must be connected to Stripe. The endpoint returns 422 until the connection is in place.

Most fields are immutable after create

Only active, name, and price_uuids can be changed after creation. The discount amount/percent, currency, duration, expiry, max redemptions, and the first_time_transaction / minimum_amount restrictions are frozen. To change a frozen field, archive the code and create a new one.

Request

POST /promotion-codes

Request Body

ParameterTypeRequiredDescription
codestringYesThe redemption string. Alphanumeric and hyphen only (^[a-zA-Z0-9-]+$), max 255 characters, unique per store.
discount_typestringYesamount_off (fixed amount) or percent_off (percentage). Discriminator for amount_off / percent_off below.
amount_offintegerWhen discount_type=amount_offDiscount in minor units (e.g. cents/grosze), minimum 1.
percent_offnumberWhen discount_type=percent_offPercentage discount, 1100.
currencystringWhen amount_off is set, and when minimum_amount is set on a global codeThree-letter ISO currency code (lowercase).
durationstringYesHow long the discount applies: once (first invoice), repeating (for duration_in_months invoices), or forever (every invoice).
duration_in_monthsintegerWhen duration=repeatingMonths the discount applies for, minimum 1.
namestringNoOptional internal name shown in dashboards, max 40 characters.
max_redemptionsintegerNoMaximum total redemptions across all customers, minimum 1.
expires_atstringNoWhen the code stops being redeemable (ISO 8601 format). Must be in the future.
first_time_transactionbooleanNoWhether the code is restricted to a customer's first purchase. Default false.
minimum_amountintegerNoMinimum transaction amount in minor units required for redemption, minimum 1.
product_idstringNoProduct UUID. When set, the code is product-scoped; otherwise it is global.
price_uuidsarrayWhen set, requires product_idArray of variant UUIDs. Narrows a product-scoped code to specific variants of product_id. Pass null or omit to apply to every price.
amount_off + duration=forever is rejected

This combination returns 422. A fixed-amount discount cannot apply indefinitely against future renewals — use duration=once, or duration=repeating with duration_in_months.

Example Request — global percent_off code

curl -X POST "https://cart.easy.tools/api/v1/promotion-codes" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"code": "BLACKFRIDAY20",
"name": "Black Friday 2026",
"discount_type": "percent_off",
"percent_off": 20,
"duration": "once",
"max_redemptions": 100,
"expires_at": "2026-12-31T23:59:59+00:00"
}'

Example Request — fixed-amount code for a specific product variant

curl -X POST "https://cart.easy.tools/api/v1/promotion-codes" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"code": "LAUNCH10",
"discount_type": "amount_off",
"amount_off": 1000,
"currency": "pln",
"duration": "once",
"first_time_transaction": true,
"minimum_amount": 5000,
"product_id": "550e8400-e29b-41d4-a716-446655440000",
"price_uuids": [
"550e8400-e29b-41d4-a716-446655440001"
]
}'

Example Request — recurring discount for the next 3 invoices

curl -X POST "https://cart.easy.tools/api/v1/promotion-codes" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"code": "THREE-MONTHS-FREE-50",
"discount_type": "percent_off",
"percent_off": 50,
"duration": "repeating",
"duration_in_months": 3
}'

Response

Success Response (201)

Returns the newly created promotion code.

{
"id": "550e8400-e29b-41d4-a716-446655440030",
"code": "BLACKFRIDAY20",
"name": "Black Friday 2026",
"discount_type": "percent_off",
"amount_off": null,
"percent_off": 20,
"currency": null,
"duration": "once",
"duration_in_months": null,
"max_redemptions": 100,
"times_redeemed": 0,
"expires_at": "2026-12-31T23:59:59+00:00",
"first_time_transaction": false,
"minimum_amount": null,
"minimum_amount_currency": null,
"scope": {
"type": "global"
},
"status": "active",
"created_at": "2026-05-28T11:00:00+00:00",
"updated_at": "2026-05-28T11:00:00+00:00"
}

See the Get Promotion Codes reference for a full description of each field.

Error Responses

Validation Error (422)

Returned for missing/invalid fields and for business-rule failures: Stripe not connected, amount_off + duration=forever, duplicate code, or price_uuids without product_id.

{
"message": "The given data was invalid.",
"errors": {
"code": [
"The code field is required."
],
"discount_type": [
"The discount type field is required."
]
}
}
{
"message": "Account is not connected to Stripe"
}
{
"message": "`forever` duration is not allowed with a fixed amount discount"
}
{
"message": "Promotion code \"BLACKFRIDAY20\" is already taken"
}
{
"message": "`price_uuids` requires `product_id`"
}

Unauthorized (401)

{
"message": "Unauthenticated."
}