Overview
Yabetoo’s promotion system consists of two entities:
Coupon
Defines the discount to apply (percentage or fixed amount)
Promo Code
Code usable by customers, linked to a coupon with additional restrictions
Coupon "Summer Sale 20%"
├── Promo Code "SUMMER20" (public, max 1000 uses)
├── Promo Code "SUMMERVIP" (VIP, min €50 purchase)
└── Promo Code "SUMMER-JOHN" (limited to customers: cus_xxx)
Coupons
What is a Coupon?
A Coupon defines the type and amount of discount. It can be reused by multiple promo codes with different restrictions.
Identifier format: coupon_ followed by 24 alphanumeric characters.Example: coupon_summer2024_20percent
Coupon Attributes
| Attribute | Type | Description |
|---|
id | string | Unique identifier |
name | string | Coupon name (internal) |
percentOff | number | null | Discount percentage (0-100) |
amountOff | number | null | Fixed discount amount |
currencyOptions | object | Amounts per currency |
duration | enum | once, forever, repeating |
durationInMonths | number | null | Duration in months (if repeating) |
maxRedemptions | number | null | Maximum number of uses |
timesRedeemed | number | Current number of uses |
redeemBy | DateTime | null | Expiration date |
appliesToSkus | string[] | Eligible SKUs (optional) |
valid | boolean | Coupon active |
Discount Types
Percentage
Fixed Amount
Multi-Currency
Percentage Discount
Applies a percentage discount on the total amount.curl -X POST https://api.yabetoo.com/v1/coupons \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"name": "Summer Sale 20%",
"percentOff": 20,
"duration": "once"
}'
Example: 20% on a €100 cart = €80 to payFixed Amount Discount
Subtracts a fixed amount from the total.curl -X POST https://api.yabetoo.com/v1/coupons \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"name": "Welcome €10",
"amountOff": 10,
"currency": "EUR",
"duration": "once"
}'
Example: -€10 on a €50 cart = €40 to payMulti-Currency Discount
Define different fixed amounts per currency.curl -X POST https://api.yabetoo.com/v1/coupons \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"name": "Welcome Discount",
"amountOff": 10,
"currencyOptions": {
"EUR": { "amountOff": 10 },
"USD": { "amountOff": 11 },
"XOF": { "amountOff": 6500 }
},
"duration": "once"
}'
The system automatically applies the amount matching the order currency.
Discount Duration
Once
One time onlyThe discount applies only to the first payment.Ideal for: welcome offers, first purchases
Forever
AlwaysThe discount applies to all future payments.Ideal for: permanent discounts, partnerships
Repeating
Limited timeThe discount applies for X months.Ideal for: temporary promotions on subscriptions
// Discount for 3 months on a subscription
{
"name": "3 months at -50%",
"percentOff": 50,
"duration": "repeating",
"durationInMonths": 3
}
SKU Restrictions
Limit a coupon to certain products/SKUs:
{
"name": "Course Promo",
"percentOff": 30,
"duration": "once",
"appliesToSkus": [
"sku_course_js",
"sku_course_react",
"sku_course_node"
]
}
If appliesToSkus is defined, the discount only applies to the listed SKUs. Other cart items don’t get the discount.
A Promo Code (PromotionCode) is the code customers use. It’s associated with a coupon and can have additional restrictions.
Identifier format: promo_ followed by 24 alphanumeric characters.Example: promo_summer20_public
| Attribute | Type | Description |
|---|
id | string | Unique identifier |
couponId | string | Associated coupon |
code | string | Usable code (auto-uppercase) |
active | boolean | Code active |
expiresAt | DateTime | null | Expiration date |
maxRedemptions | number | null | Maximum number of uses |
timesRedeemed | number | Current number of uses |
minimumAmount | number | null | Minimum purchase amount |
minimumAmountCurrency | string | null | Currency for minimum |
firstTimeTransaction | boolean | Reserved for first purchases |
customerIds | string[] | Customer whitelist |
curl -X POST https://api.yabetoo.com/v1/promotion-codes \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"couponId": "coupon_summer2024",
"code": "SUMMER20",
"maxRedemptions": 1000,
"expiresAt": "2024-08-31T23:59:59Z"
}'
The code is automatically converted to uppercase. summer20 becomes SUMMER20.
Advanced Restrictions
Minimum Amount
First Purchase
Specific Customers
Time Limited
Require a minimum purchase amount:{
"couponId": "coupon_20percent",
"code": "SAVE20",
"minimumAmount": 50,
"minimumAmountCurrency": "EUR"
}
The code only works if the cart exceeds €50. Reserve for new customers:{
"couponId": "coupon_welcome",
"code": "WELCOME15",
"firstTimeTransaction": true
}
The code only works for the customer’s first order. Limit to certain customers:{
"couponId": "coupon_vip",
"code": "VIP50",
"customerIds": [
"cus_abc123",
"cus_def456",
"cus_ghi789"
]
}
Only listed customers can use this code. Define a validity period:{
"couponId": "coupon_blackfriday",
"code": "BLACKFRIDAY",
"expiresAt": "2024-11-30T23:59:59Z",
"maxRedemptions": 500
}
The code expires at the defined date OR after 500 uses.
When using a promo code, the system performs several checks:
Code exists and active
The code must exist and active must be true
Not expired
Current date must be before expiresAt
Limit not reached
timesRedeemed must be less than maxRedemptions
Coupon valid
Associated coupon must be valid (valid: true)
Minimum amount
Cart must reach minimumAmount if defined
First purchase
If firstTimeTransaction, verify it’s the first order
Customer authorized
If customerIds defined, verify customer is in the list
Eligible SKUs
If coupon has appliesToSkus, verify cart contains these SKUs
Currency supported
Order currency must be supported by the coupon
Validation Errors
| Error | Description |
|---|
INVALID_CODE | Non-existent or inactive code |
EXPIRED | Code or coupon expired |
MAX_REDEMPTIONS | Usage limit reached |
MINIMUM_NOT_MET | Minimum amount not reached |
NOT_FIRST_PURCHASE | Reserved for first purchases |
SKUS_NOT_ELIGIBLE | No eligible SKU in cart |
CURRENCY_MISMATCH | Currency not supported |
CUSTOMER_NOT_ALLOWED | Customer not authorized |
COUPON_INVALID | Coupon deactivated |
Usage History
Each coupon/promo code usage is recorded in CouponRedemption:
{
"id": "redemption_abc123",
"couponId": "coupon_summer2024",
"promotionCodeId": "promo_summer20",
"customerId": "cus_xyz789",
"customerEmail": "customer@example.com",
"orderId": "ord_def456",
"discountAmount": 20,
"currency": "EUR",
"createdAt": "2024-07-15T14:30:00Z"
}
This history allows:
- Auditing of granted discounts
- Promotion performance analysis
- Verification of uses per customer
Discount Calculation
Percentage Discount
const discount = (subtotal * coupon.percentOff) / 100;
Fixed Amount Discount
const discount = coupon.getAmountOffForCurrency(currency);
// Returns amount for currency or null if not supported
Complete Example
Cart: €120
Coupon: 20% discount
Calculation:
- Subtotal: €120
- Discount: 120 × 0.20 = €24
- Total: 120 - 24 = €96
API Responses
{
"id": "coupon_summer2024_20percent",
"object": "coupon",
"name": "Summer Sale 20%",
"percentOff": 20,
"amountOff": null,
"currencyOptions": {},
"duration": "once",
"durationInMonths": null,
"maxRedemptions": 1000,
"timesRedeemed": 156,
"redeemBy": "2024-08-31T23:59:59.000Z",
"appliesToSkus": null,
"valid": true,
"createdAt": "2024-06-01T00:00:00.000Z",
"updatedAt": "2024-07-15T14:30:00.000Z"
}
{
"id": "promo_summer20_public",
"object": "promotion_code",
"couponId": "coupon_summer2024_20percent",
"code": "SUMMER20",
"active": true,
"expiresAt": "2024-08-31T23:59:59.000Z",
"maxRedemptions": 1000,
"timesRedeemed": 156,
"minimumAmount": null,
"minimumAmountCurrency": null,
"firstTimeTransaction": false,
"customerIds": null,
"coupon": {
"id": "coupon_summer2024_20percent",
"name": "Summer Sale 20%",
"percentOff": 20
},
"createdAt": "2024-06-01T00:00:00.000Z",
"updatedAt": "2024-07-15T14:30:00.000Z"
}
Common Operations
Create a coupon
curl -X POST https://api.yabetoo.com/v1/coupons \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"name": "Black Friday 30%",
"percentOff": 30,
"duration": "once",
"maxRedemptions": 500,
"redeemBy": "2024-11-30T23:59:59Z"
}'
curl -X POST https://api.yabetoo.com/v1/promotion-codes \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"couponId": "coupon_blackfriday",
"code": "BLACKFRIDAY30"
}'
curl -X POST https://api.yabetoo.com/v1/promotion-codes/validate \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"code": "SUMMER20",
"amount": 100,
"currency": "EUR",
"customerEmail": "customer@example.com"
}'
curl -X PATCH https://api.yabetoo.com/v1/promotion-codes/promo_xxx \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"active": false
}'
Best Practices
- Use memorable and easy-to-type codes
- Avoid ambiguous characters (0/O, 1/l)
- Include context:
SUMMER20, WELCOME15, VIP50
- Keep codes short (6-12 characters)
- Always define a limit (
maxRedemptions or expiresAt)
- Unlimited promotions can impact your margins
- Plan a safety margin on limits
- Clearly display usage conditions
- Indicate expiration date to customers
- Explain why a code is rejected
Use Case Examples
Welcome Offer
Black Friday
VIP Program
Discounted Subscription
{
"coupon": {
"name": "Welcome €10",
"amountOff": 10,
"currency": "EUR",
"duration": "once"
},
"promotionCode": {
"code": "WELCOME10",
"firstTimeTransaction": true,
"minimumAmount": 30,
"minimumAmountCurrency": "EUR"
}
}
Result: -€10 for new customers with cart > €30{
"coupon": {
"name": "Black Friday 40%",
"percentOff": 40,
"duration": "once",
"maxRedemptions": 1000,
"redeemBy": "2024-11-30T23:59:59Z"
},
"promotionCode": {
"code": "BLACK40",
"expiresAt": "2024-11-30T23:59:59Z"
}
}
Result: -40% limited to 1000 uses until November 30{
"coupon": {
"name": "VIP Permanent 15%",
"percentOff": 15,
"duration": "forever"
},
"promotionCode": {
"code": "VIP15",
"customerIds": ["cus_vip1", "cus_vip2", "cus_vip3"]
}
}
Result: -15% permanent for VIP customers only{
"coupon": {
"name": "3 months at -50%",
"percentOff": 50,
"duration": "repeating",
"durationInMonths": 3
},
"promotionCode": {
"code": "START50"
}
}
Result: -50% on the first 3 months of subscription
Next Steps
Create a checkout session
Use promo codes in your checkout sessions
Webhooks
Receive notifications when promo codes are used