Create Promotion Code
Create a new promotion code in your store.
The store must be connected to Stripe. The endpoint returns 422 until the connection is
in place.
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
| Parameter | Type | Required | Description |
|---|---|---|---|
code | string | Yes | The redemption string. Alphanumeric and hyphen only (^[a-zA-Z0-9-]+$), max 255 characters, unique per store. |
discount_type | string | Yes | amount_off (fixed amount) or percent_off (percentage). Discriminator for amount_off / percent_off below. |
amount_off | integer | When discount_type=amount_off | Discount in minor units (e.g. cents/grosze), minimum 1. |
percent_off | number | When discount_type=percent_off | Percentage discount, 1–100. |
currency | string | When amount_off is set, and when minimum_amount is set on a global code | Three-letter ISO currency code (lowercase). |
duration | string | Yes | How long the discount applies: once (first invoice), repeating (for duration_in_months invoices), or forever (every invoice). |
duration_in_months | integer | When duration=repeating | Months the discount applies for, minimum 1. |
name | string | No | Optional internal name shown in dashboards, max 40 characters. |
max_redemptions | integer | No | Maximum total redemptions across all customers, minimum 1. |
expires_at | string | No | When the code stops being redeemable (ISO 8601 format). Must be in the future. |
first_time_transaction | boolean | No | Whether the code is restricted to a customer's first purchase. Default false. |
minimum_amount | integer | No | Minimum transaction amount in minor units required for redemption, minimum 1. |
product_id | string | No | Product UUID. When set, the code is product-scoped; otherwise it is global. |
price_uuids | array | When set, requires product_id | Array 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 rejectedThis 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."
}