# Tiers

Tier management

## Get all tiers

> Retrieve all customer tiers for the authenticated shop

```json
{"openapi":"3.0.0","info":{"title":"Joy Loyalty Program - REST API v2","version":"2.0.0"},"tags":[{"name":"Tiers","description":"Tier management"}],"servers":[{"url":"https://dev-api.joy.so","description":"Staging"},{"url":"https://api.joy.so","description":"Production"}],"security":[{"JoyAuth":[],"JoySecretAuth":[]}],"components":{"securitySchemes":{"JoyAuth":{"type":"apiKey","in":"header","name":"X-Joy-Loyalty-App-Key"}},"schemas":{"ListResponse":{"allOf":[{"$ref":"#/components/schemas/SuccessResponse"},{"type":"object","properties":{"data":{"type":"array","items":{"type":"object"}},"meta":{"type":"object","properties":{"count":{"type":"integer","description":"Number of items returned"}}}}}]},"SuccessResponse":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object"},"meta":{"type":"object","description":"Additional metadata such as counts and pagination"},"message":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}}},"Tier":{"type":"object","description":"Customer tier with all fields that may be returned by the API. Note: Actual responses may only include non-null fields due to automatic filtering.","properties":{"id":{"type":"string","description":"Tier ID"},"name":{"type":"string","description":"Tier name"},"icon":{"type":"string","description":"Default icon URL"},"iconCustom":{"type":"string","description":"Custom icon URL"},"targetPoint":{"type":"integer","description":"Points required to reach this tier"},"targetPointUpdate":{"type":"integer","description":"Updated points required"},"members":{"type":"integer","description":"Number of customers in this tier"},"placedOrderReward":{"type":"boolean","description":"Whether tier has place order rewards"},"rateMoney":{"type":"number","description":"Money rate for earning points"},"earnPoint":{"type":"integer","description":"Points earning configuration"},"shopId":{"type":"string","description":"Shop ID"},"imageBlock":{"type":"string","description":"Image block URL"},"tierRewards":{"type":"array","description":"Tier-specific rewards/benefits","items":{"$ref":"#/components/schemas/TierReward"}},"inactive":{"type":"boolean","description":"Whether tier is inactive"},"isExclusiveTier":{"type":"boolean","description":"Whether tier is exclusive"},"prevNames":{"type":"array","description":"Previous names of this tier","items":{"type":"string"}},"createdAt":{"type":"string","format":"date-time","description":"Tier creation timestamp"},"updatedAt":{"type":"string","format":"date-time","description":"Last update timestamp"}}},"TierReward":{"type":"object","properties":{"id":{"type":"string","description":"Reward ID"},"status":{"type":"boolean","description":"Whether reward is active"},"title":{"type":"string","description":"Reward title"},"redeemType":{"type":"string","enum":["fixed","dynamic"],"description":"Type of redemption"},"spendPoint":{"type":"integer","description":"Points required to redeem"},"minSpendPoint":{"type":"string","description":"Minimum points to spend"},"maxSpendPoint":{"type":"string","description":"Maximum points to spend"},"appliedTo":{"type":"string","enum":["all","specific","sf_product"],"description":"Where reward applies"},"orderReq":{"type":"string","enum":["none","min_amount"],"description":"Order requirements"},"orderReqAmount":{"type":"number","description":"Minimum order amount required"},"expiredAfter":{"type":"string","description":"When reward expires"},"expiredTime":{"type":"string","format":"date-time","nullable":true,"description":"Specific expiration time"},"type":{"type":"string","description":"Reward type"},"codeName":{"type":"string","description":"Code name for reward"},"earnAmount":{"type":"string","description":"Amount earned"},"event":{"type":"string","description":"Event type for reward"},"bonusPoints":{"type":"string","description":"Bonus points awarded"},"priority":{"type":"integer","description":"Reward priority"},"showLoyaltyPage":{"type":"boolean","description":"Whether to show on loyalty page"},"translateTitle":{"type":"object","description":"Translated titles for different languages"},"expired":{"type":"boolean","description":"Whether reward has expired"},"createdAt":{"type":"string","format":"date-time","description":"Reward creation timestamp"},"updatedAt":{"type":"string","format":"date-time","description":"Last update timestamp"}}},"ErrorResponse":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"statusCode":{"type":"integer"},"details":{"type":"object"}}},"timestamp":{"type":"string","format":"date-time"}}}}},"paths":{"/rest_api/v2/tiers":{"get":{"tags":["Tiers"],"summary":"Get all tiers","description":"Retrieve all customer tiers for the authenticated shop","responses":{"200":{"description":"List of tiers","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ListResponse"},{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Tier"}}}}]}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Plan upgrade required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}}}
```

## Create a new tier

> Creates a new VIP tier. Optionally include a \`rewards\` array to create\
> tier rewards inline with the tier.\
> \
> \*\*Constraints:\*\*\
> \- Tier name must be unique (409 if duplicate)\
> \- \`targetPoint\` is required and must be ≥ 0\
> \
> \*\*Side effects:\*\* \`publishApp()\` triggered after creation.<br>

```json
{"openapi":"3.0.0","info":{"title":"Joy Loyalty Program - REST API v2","version":"2.0.0"},"tags":[{"name":"Tiers","description":"Tier management"}],"servers":[{"url":"https://dev-api.joy.so","description":"Staging"},{"url":"https://api.joy.so","description":"Production"}],"security":[{"JoyAuth":[],"JoySecretAuth":[]}],"components":{"securitySchemes":{"JoyAuth":{"type":"apiKey","in":"header","name":"X-Joy-Loyalty-App-Key"}},"schemas":{"TierCreate":{"type":"object","required":["name","targetPoint"],"properties":{"name":{"type":"string","description":"Tier name (must be unique)"},"targetPoint":{"type":"integer","minimum":0,"description":"Points required to reach this tier"},"targetPointUpdate":{"type":"integer","description":"Defaults to targetPoint if not provided"},"icon":{"type":"string","description":"Icon ID (tier_bronze_icon, tier_sliver_icon, tier_gold_icon, tier_diamond_icon, tier_platinum_icon, tier_crown_icon)"},"iconCustom":{"type":"string","description":"Custom icon URL"},"hideDefaultTier":{"type":"boolean"},"isExclusiveTier":{"type":"boolean"},"tierNamesLanguage":{"type":"object","description":"Multi-language tier names {locale: name}"},"textColor":{"type":"string","description":"Hex color"},"progressBarColor":{"type":"string"},"boxBgColor":{"type":"string"},"boxTextColor":{"type":"string"},"bgCardColor":{"type":"string"},"cardOpacity":{"type":"number","minimum":0,"maximum":1},"iconColor":{"type":"string"},"bgType":{"type":"string"},"rewards":{"type":"array","description":"Inline rewards to create with the tier","items":{"$ref":"#/components/schemas/TierRewardCreate"}}}},"TierRewardCreate":{"type":"object","required":["event","title"],"properties":{"event":{"type":"string","enum":["amount_discount","percentage_discount","free_shipping","free_gift","bonus_point","buy_x_get_y"],"description":"Reward event type"},"title":{"type":"string","description":"Reward name"},"type":{"type":"string","enum":["tier_spending","tier_privileges","pre_tier_reward"],"default":"tier_spending"},"earnAmount":{"type":"string","description":"Discount amount/percentage. For free_gift always '100'"},"spendPoint":{"type":"string","description":"Points required to redeem"},"bonusPoints":{"type":"string","description":"Bonus points (for bonus_point event, ≥1)"},"codeName":{"type":"string","description":"Code name (for privilege rewards)"},"appliedTo":{"type":"string","enum":["all","specific_product","specific"]},"orderReq":{"type":"string","enum":["none","min_amount","min_quantity"]},"orderReqAmount":{"type":"string"},"specificProducts":{"type":"array","description":"Product list (max 1 for free_gift)","items":{"type":"object"}},"specificCollections":{"type":"array","items":{"type":"string"}},"combinedWith":{"type":"array","items":{"type":"string","enum":["orderDiscounts","shippingDiscounts","productDiscounts"]}},"hasLimitShipping":{"type":"boolean"},"limitShipping":{"type":"number"},"expiredAfter":{"type":"number"},"expiredUnit":{"type":"string","enum":["day","month"]},"status":{"type":"boolean"}}},"SuccessResponse":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object"},"meta":{"type":"object","description":"Additional metadata such as counts and pagination"},"message":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}}},"Tier":{"type":"object","description":"Customer tier with all fields that may be returned by the API. Note: Actual responses may only include non-null fields due to automatic filtering.","properties":{"id":{"type":"string","description":"Tier ID"},"name":{"type":"string","description":"Tier name"},"icon":{"type":"string","description":"Default icon URL"},"iconCustom":{"type":"string","description":"Custom icon URL"},"targetPoint":{"type":"integer","description":"Points required to reach this tier"},"targetPointUpdate":{"type":"integer","description":"Updated points required"},"members":{"type":"integer","description":"Number of customers in this tier"},"placedOrderReward":{"type":"boolean","description":"Whether tier has place order rewards"},"rateMoney":{"type":"number","description":"Money rate for earning points"},"earnPoint":{"type":"integer","description":"Points earning configuration"},"shopId":{"type":"string","description":"Shop ID"},"imageBlock":{"type":"string","description":"Image block URL"},"tierRewards":{"type":"array","description":"Tier-specific rewards/benefits","items":{"$ref":"#/components/schemas/TierReward"}},"inactive":{"type":"boolean","description":"Whether tier is inactive"},"isExclusiveTier":{"type":"boolean","description":"Whether tier is exclusive"},"prevNames":{"type":"array","description":"Previous names of this tier","items":{"type":"string"}},"createdAt":{"type":"string","format":"date-time","description":"Tier creation timestamp"},"updatedAt":{"type":"string","format":"date-time","description":"Last update timestamp"}}},"TierReward":{"type":"object","properties":{"id":{"type":"string","description":"Reward ID"},"status":{"type":"boolean","description":"Whether reward is active"},"title":{"type":"string","description":"Reward title"},"redeemType":{"type":"string","enum":["fixed","dynamic"],"description":"Type of redemption"},"spendPoint":{"type":"integer","description":"Points required to redeem"},"minSpendPoint":{"type":"string","description":"Minimum points to spend"},"maxSpendPoint":{"type":"string","description":"Maximum points to spend"},"appliedTo":{"type":"string","enum":["all","specific","sf_product"],"description":"Where reward applies"},"orderReq":{"type":"string","enum":["none","min_amount"],"description":"Order requirements"},"orderReqAmount":{"type":"number","description":"Minimum order amount required"},"expiredAfter":{"type":"string","description":"When reward expires"},"expiredTime":{"type":"string","format":"date-time","nullable":true,"description":"Specific expiration time"},"type":{"type":"string","description":"Reward type"},"codeName":{"type":"string","description":"Code name for reward"},"earnAmount":{"type":"string","description":"Amount earned"},"event":{"type":"string","description":"Event type for reward"},"bonusPoints":{"type":"string","description":"Bonus points awarded"},"priority":{"type":"integer","description":"Reward priority"},"showLoyaltyPage":{"type":"boolean","description":"Whether to show on loyalty page"},"translateTitle":{"type":"object","description":"Translated titles for different languages"},"expired":{"type":"boolean","description":"Whether reward has expired"},"createdAt":{"type":"string","format":"date-time","description":"Reward creation timestamp"},"updatedAt":{"type":"string","format":"date-time","description":"Last update timestamp"}}}}},"paths":{"/rest_api/v2/tiers":{"post":{"tags":["Tiers"],"summary":"Create a new tier","description":"Creates a new VIP tier. Optionally include a `rewards` array to create\ntier rewards inline with the tier.\n\n**Constraints:**\n- Tier name must be unique (409 if duplicate)\n- `targetPoint` is required and must be ≥ 0\n\n**Side effects:** `publishApp()` triggered after creation.\n","operationId":"createTier","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TierCreate"}}}},"responses":{"201":{"description":"Tier created","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/SuccessResponse"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Tier"}}}]}}}},"400":{"description":"Validation error"},"409":{"description":"Tier name already exists (TIER_NAME_DUPLICATE)"}}}}}}
```

## Get global tier settings

> Returns the global VIP tier program settings (entry method, demotion,\
> reset cycle, tags, etc.). This is a single document per shop.<br>

```json
{"openapi":"3.0.0","info":{"title":"Joy Loyalty Program - REST API v2","version":"2.0.0"},"tags":[{"name":"Tiers","description":"Tier management"}],"servers":[{"url":"https://dev-api.joy.so","description":"Staging"},{"url":"https://api.joy.so","description":"Production"}],"security":[{"JoyAuth":[],"JoySecretAuth":[]}],"components":{"securitySchemes":{"JoyAuth":{"type":"apiKey","in":"header","name":"X-Joy-Loyalty-App-Key"}},"schemas":{"SuccessResponse":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object"},"meta":{"type":"object","description":"Additional metadata such as counts and pagination"},"message":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}}},"TierSettings":{"type":"object","description":"Global VIP tier program settings","properties":{"id":{"type":"string","readOnly":true},"status":{"type":"boolean","description":"Tier program active (read-only via API, requires launch)"},"entryMethod":{"type":"string","enum":["pointEarned","moneySpent","numberOfOrders"],"description":"How customers qualify for tiers (read-only via API)"},"milestone":{"type":"string","enum":["lifetime","halfyear","specificDay"],"description":"Reset cycle (read-only via API)"},"progressionType":{"type":"string","enum":["cumulative","reset"],"description":"Points progression type (read-only via API)"},"startDate":{"type":"string","description":"Program start date (read-only via API)"},"prefix":{"type":"string","description":"Discount code prefix"},"isUsePrefixDiscountCode":{"type":"boolean"},"useDemotionTier":{"type":"boolean","description":"Enable tier demotion"},"typeDemotion":{"type":"string","enum":["downgrade_tier","reset_tier"]},"downgradeAfter":{"type":"string","description":"Duration before downgrade"},"downgradeAfterUnit":{"type":"string","enum":["day","month","year"]},"useResetTier":{"type":"boolean"},"monthResetTier":{"type":"string"},"dayResetTier":{"type":"string"},"isSyncTagsCustomerTier":{"type":"boolean","description":"Auto-tag customers in Shopify by tier"},"customerTierTagPrefix":{"type":"string"},"notificationDowngradeBefore":{"type":"string"},"notificationDowngradeBeforeUnit":{"type":"string","enum":["day","month"]}}}}},"paths":{"/rest_api/v2/tiers/settings":{"get":{"tags":["Tiers"],"summary":"Get global tier settings","description":"Returns the global VIP tier program settings (entry method, demotion,\nreset cycle, tags, etc.). This is a single document per shop.\n","operationId":"getTierSettings","responses":{"200":{"description":"Tier settings","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/SuccessResponse"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/TierSettings"}}}]}}}}}}}}}
```

## Update tier settings (safe fields only)

> Updates global tier program settings. Only safe fields are accepted —\
> fields that require tier launch (\`entryMethod\`, \`milestone\`, \`startDate\`,\
> \`progressionType\`, \`status\`) are \*\*excluded\*\* and must be changed via admin UI.\
> \
> \*\*Safe fields:\*\* demotion config, reset schedule, discount prefix, customer tags, notifications.<br>

```json
{"openapi":"3.0.0","info":{"title":"Joy Loyalty Program - REST API v2","version":"2.0.0"},"tags":[{"name":"Tiers","description":"Tier management"}],"servers":[{"url":"https://dev-api.joy.so","description":"Staging"},{"url":"https://api.joy.so","description":"Production"}],"security":[{"JoyAuth":[],"JoySecretAuth":[]}],"components":{"securitySchemes":{"JoyAuth":{"type":"apiKey","in":"header","name":"X-Joy-Loyalty-App-Key"}},"schemas":{"TierSettingsUpdate":{"type":"object","description":"Safe tier settings fields only. Fields requiring tier launch\n(entryMethod, milestone, startDate, progressionType, status) are excluded.\n","properties":{"prefix":{"type":"string"},"isUsePrefixDiscountCode":{"type":"boolean"},"useDemotionTier":{"type":"boolean"},"typeDemotion":{"type":"string","enum":["downgrade_tier","reset_tier"]},"downgradeAfter":{"type":"string"},"downgradeAfterUnit":{"type":"string","enum":["day","month","year"]},"useResetTier":{"type":"boolean"},"resetAfter":{"type":"string"},"resetAfterUnit":{"type":"string","enum":["day","month","year"]},"monthResetTier":{"type":"string"},"dayResetTier":{"type":"string"},"isSyncTagsCustomerTier":{"type":"boolean"},"customerTierTagPrefix":{"type":"string"},"notificationDowngradeBefore":{"type":"string"},"notificationDowngradeBeforeUnit":{"type":"string","enum":["day","month"]}}}}},"paths":{"/rest_api/v2/tiers/settings":{"put":{"tags":["Tiers"],"summary":"Update tier settings (safe fields only)","description":"Updates global tier program settings. Only safe fields are accepted —\nfields that require tier launch (`entryMethod`, `milestone`, `startDate`,\n`progressionType`, `status`) are **excluded** and must be changed via admin UI.\n\n**Safe fields:** demotion config, reset schedule, discount prefix, customer tags, notifications.\n","operationId":"updateTierSettings","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TierSettingsUpdate"}}}},"responses":{"200":{"description":"Updated tier settings"},"400":{"description":"No valid fields provided"},"404":{"description":"Tier settings not found (tiers never launched)"}}}}}}
```

## GET /rest\_api/v2/tiers/{tierId}

> Get tier by ID

```json
{"openapi":"3.0.0","info":{"title":"Joy Loyalty Program - REST API v2","version":"2.0.0"},"tags":[{"name":"Tiers","description":"Tier management"}],"servers":[{"url":"https://dev-api.joy.so","description":"Staging"},{"url":"https://api.joy.so","description":"Production"}],"security":[{"JoyAuth":[],"JoySecretAuth":[]}],"components":{"securitySchemes":{"JoyAuth":{"type":"apiKey","in":"header","name":"X-Joy-Loyalty-App-Key"}},"schemas":{"SuccessResponse":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object"},"meta":{"type":"object","description":"Additional metadata such as counts and pagination"},"message":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}}},"Tier":{"type":"object","description":"Customer tier with all fields that may be returned by the API. Note: Actual responses may only include non-null fields due to automatic filtering.","properties":{"id":{"type":"string","description":"Tier ID"},"name":{"type":"string","description":"Tier name"},"icon":{"type":"string","description":"Default icon URL"},"iconCustom":{"type":"string","description":"Custom icon URL"},"targetPoint":{"type":"integer","description":"Points required to reach this tier"},"targetPointUpdate":{"type":"integer","description":"Updated points required"},"members":{"type":"integer","description":"Number of customers in this tier"},"placedOrderReward":{"type":"boolean","description":"Whether tier has place order rewards"},"rateMoney":{"type":"number","description":"Money rate for earning points"},"earnPoint":{"type":"integer","description":"Points earning configuration"},"shopId":{"type":"string","description":"Shop ID"},"imageBlock":{"type":"string","description":"Image block URL"},"tierRewards":{"type":"array","description":"Tier-specific rewards/benefits","items":{"$ref":"#/components/schemas/TierReward"}},"inactive":{"type":"boolean","description":"Whether tier is inactive"},"isExclusiveTier":{"type":"boolean","description":"Whether tier is exclusive"},"prevNames":{"type":"array","description":"Previous names of this tier","items":{"type":"string"}},"createdAt":{"type":"string","format":"date-time","description":"Tier creation timestamp"},"updatedAt":{"type":"string","format":"date-time","description":"Last update timestamp"}}},"TierReward":{"type":"object","properties":{"id":{"type":"string","description":"Reward ID"},"status":{"type":"boolean","description":"Whether reward is active"},"title":{"type":"string","description":"Reward title"},"redeemType":{"type":"string","enum":["fixed","dynamic"],"description":"Type of redemption"},"spendPoint":{"type":"integer","description":"Points required to redeem"},"minSpendPoint":{"type":"string","description":"Minimum points to spend"},"maxSpendPoint":{"type":"string","description":"Maximum points to spend"},"appliedTo":{"type":"string","enum":["all","specific","sf_product"],"description":"Where reward applies"},"orderReq":{"type":"string","enum":["none","min_amount"],"description":"Order requirements"},"orderReqAmount":{"type":"number","description":"Minimum order amount required"},"expiredAfter":{"type":"string","description":"When reward expires"},"expiredTime":{"type":"string","format":"date-time","nullable":true,"description":"Specific expiration time"},"type":{"type":"string","description":"Reward type"},"codeName":{"type":"string","description":"Code name for reward"},"earnAmount":{"type":"string","description":"Amount earned"},"event":{"type":"string","description":"Event type for reward"},"bonusPoints":{"type":"string","description":"Bonus points awarded"},"priority":{"type":"integer","description":"Reward priority"},"showLoyaltyPage":{"type":"boolean","description":"Whether to show on loyalty page"},"translateTitle":{"type":"object","description":"Translated titles for different languages"},"expired":{"type":"boolean","description":"Whether reward has expired"},"createdAt":{"type":"string","format":"date-time","description":"Reward creation timestamp"},"updatedAt":{"type":"string","format":"date-time","description":"Last update timestamp"}}},"ErrorResponse":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"statusCode":{"type":"integer"},"details":{"type":"object"}}},"timestamp":{"type":"string","format":"date-time"}}}}},"paths":{"/rest_api/v2/tiers/{tierId}":{"get":{"tags":["Tiers"],"summary":"Get tier by ID","parameters":[{"name":"tierId","in":"path","required":true,"schema":{"type":"string"},"description":"Tier ID"}],"responses":{"200":{"description":"Tier details","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/SuccessResponse"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Tier"}}}]}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Plan upgrade required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Tier not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}}}
```

## Update a tier

> Updates tier fields. Only whitelisted fields accepted (name, design, milestone).\
> Tier name must be unique — returns 409 if duplicate.<br>

```json
{"openapi":"3.0.0","info":{"title":"Joy Loyalty Program - REST API v2","version":"2.0.0"},"tags":[{"name":"Tiers","description":"Tier management"}],"servers":[{"url":"https://dev-api.joy.so","description":"Staging"},{"url":"https://api.joy.so","description":"Production"}],"security":[{"JoyAuth":[],"JoySecretAuth":[]}],"components":{"securitySchemes":{"JoyAuth":{"type":"apiKey","in":"header","name":"X-Joy-Loyalty-App-Key"}},"schemas":{"TierUpdate":{"type":"object","description":"All fields optional — only send fields to change","properties":{"name":{"type":"string"},"targetPointUpdate":{"type":"integer","minimum":0},"icon":{"type":"string"},"iconCustom":{"type":"string"},"hideDefaultTier":{"type":"boolean"},"isExclusiveTier":{"type":"boolean"},"tierNamesLanguage":{"type":"object"},"textColor":{"type":"string"},"progressBarColor":{"type":"string"},"boxBgColor":{"type":"string"},"boxTextColor":{"type":"string"},"bgCardColor":{"type":"string"},"cardOpacity":{"type":"number"},"iconColor":{"type":"string"},"bgType":{"type":"string"}}}}},"paths":{"/rest_api/v2/tiers/{tierId}":{"put":{"tags":["Tiers"],"summary":"Update a tier","description":"Updates tier fields. Only whitelisted fields accepted (name, design, milestone).\nTier name must be unique — returns 409 if duplicate.\n","operationId":"updateTier","parameters":[{"name":"tierId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TierUpdate"}}}},"responses":{"200":{"description":"Updated tier with rewards"},"400":{"description":"No valid fields provided"},"404":{"description":"Tier not found (TIER_NOT_FOUND)"},"409":{"description":"Tier name already exists (TIER_NAME_DUPLICATE)"}}}}}}
```

## Delete a tier

> Permanently deletes the tier and all associated rewards.\
> \
> \*\*Blocked:\*\* Cannot delete the Bronze tier (\`systemType=bronze\` or \`targetPoint=0\`).\
> \
> \*\*Side effects:\*\*\
> \- \`afterDeleteTier()\` deletes all programs with matching \`tierId\`\
> \- Auto-discounts (Shopify) cleaned up for privilege rewards\
> \- \`publishApp()\` triggered<br>

```json
{"openapi":"3.0.0","info":{"title":"Joy Loyalty Program - REST API v2","version":"2.0.0"},"tags":[{"name":"Tiers","description":"Tier management"}],"servers":[{"url":"https://dev-api.joy.so","description":"Staging"},{"url":"https://api.joy.so","description":"Production"}],"security":[{"JoyAuth":[],"JoySecretAuth":[]}],"components":{"securitySchemes":{"JoyAuth":{"type":"apiKey","in":"header","name":"X-Joy-Loyalty-App-Key"}}},"paths":{"/rest_api/v2/tiers/{tierId}":{"delete":{"tags":["Tiers"],"summary":"Delete a tier","description":"Permanently deletes the tier and all associated rewards.\n\n**Blocked:** Cannot delete the Bronze tier (`systemType=bronze` or `targetPoint=0`).\n\n**Side effects:**\n- `afterDeleteTier()` deletes all programs with matching `tierId`\n- Auto-discounts (Shopify) cleaned up for privilege rewards\n- `publishApp()` triggered\n","operationId":"deleteTier","parameters":[{"name":"tierId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Tier deleted","content":{"application/json":{}}},"400":{"description":"Cannot delete Bronze tier (TIER_DELETE_BRONZE_BLOCKED)"},"404":{"description":"Tier not found (TIER_NOT_FOUND)"}}}}}}
```

## Add a reward to a tier

> Creates a new reward linked to the specified tier.\
> \
> \*\*Event types:\*\* \`amount\_discount\`, \`percentage\_discount\`, \`free\_shipping\`, \`free\_gift\`, \`bonus\_point\`, \`buy\_x\_get\_y\`\
> \
> \*\*Key fields by event:\*\*\
> \- amount\_discount: \`earnAmount\`, \`appliedTo\`, \`orderReq\`\
> \- percentage\_discount: \`earnAmount\` (1-100), \`appliedTo\`, \`orderReq\`\
> \- free\_shipping: \`hasLimitShipping\`, \`limitShipping\`\
> \- free\_gift: \`specificProducts\` (1 item), \`earnAmount=100\`\
> \- bonus\_point: \`bonusPoints\` (≥1)<br>

```json
{"openapi":"3.0.0","info":{"title":"Joy Loyalty Program - REST API v2","version":"2.0.0"},"tags":[{"name":"Tiers","description":"Tier management"}],"servers":[{"url":"https://dev-api.joy.so","description":"Staging"},{"url":"https://api.joy.so","description":"Production"}],"security":[{"JoyAuth":[],"JoySecretAuth":[]}],"components":{"securitySchemes":{"JoyAuth":{"type":"apiKey","in":"header","name":"X-Joy-Loyalty-App-Key"}},"schemas":{"TierRewardCreate":{"type":"object","required":["event","title"],"properties":{"event":{"type":"string","enum":["amount_discount","percentage_discount","free_shipping","free_gift","bonus_point","buy_x_get_y"],"description":"Reward event type"},"title":{"type":"string","description":"Reward name"},"type":{"type":"string","enum":["tier_spending","tier_privileges","pre_tier_reward"],"default":"tier_spending"},"earnAmount":{"type":"string","description":"Discount amount/percentage. For free_gift always '100'"},"spendPoint":{"type":"string","description":"Points required to redeem"},"bonusPoints":{"type":"string","description":"Bonus points (for bonus_point event, ≥1)"},"codeName":{"type":"string","description":"Code name (for privilege rewards)"},"appliedTo":{"type":"string","enum":["all","specific_product","specific"]},"orderReq":{"type":"string","enum":["none","min_amount","min_quantity"]},"orderReqAmount":{"type":"string"},"specificProducts":{"type":"array","description":"Product list (max 1 for free_gift)","items":{"type":"object"}},"specificCollections":{"type":"array","items":{"type":"string"}},"combinedWith":{"type":"array","items":{"type":"string","enum":["orderDiscounts","shippingDiscounts","productDiscounts"]}},"hasLimitShipping":{"type":"boolean"},"limitShipping":{"type":"number"},"expiredAfter":{"type":"number"},"expiredUnit":{"type":"string","enum":["day","month"]},"status":{"type":"boolean"}}}}},"paths":{"/rest_api/v2/tiers/{tierId}/rewards":{"post":{"tags":["Tiers"],"summary":"Add a reward to a tier","description":"Creates a new reward linked to the specified tier.\n\n**Event types:** `amount_discount`, `percentage_discount`, `free_shipping`, `free_gift`, `bonus_point`, `buy_x_get_y`\n\n**Key fields by event:**\n- amount_discount: `earnAmount`, `appliedTo`, `orderReq`\n- percentage_discount: `earnAmount` (1-100), `appliedTo`, `orderReq`\n- free_shipping: `hasLimitShipping`, `limitShipping`\n- free_gift: `specificProducts` (1 item), `earnAmount=100`\n- bonus_point: `bonusPoints` (≥1)\n","operationId":"createTierReward","parameters":[{"name":"tierId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TierRewardCreate"}}}},"responses":{"201":{"description":"Reward created"},"404":{"description":"Tier not found"}}}}}}
```

## Update a tier reward

> Partial update of a tier reward. Only send fields to change.\
> All fields are optional.<br>

```json
{"openapi":"3.0.0","info":{"title":"Joy Loyalty Program - REST API v2","version":"2.0.0"},"tags":[{"name":"Tiers","description":"Tier management"}],"servers":[{"url":"https://dev-api.joy.so","description":"Staging"},{"url":"https://api.joy.so","description":"Production"}],"security":[{"JoyAuth":[],"JoySecretAuth":[]}],"components":{"securitySchemes":{"JoyAuth":{"type":"apiKey","in":"header","name":"X-Joy-Loyalty-App-Key"}},"schemas":{"TierRewardUpdate":{"type":"object","description":"All fields optional — only send fields to change","properties":{"title":{"type":"string"},"earnAmount":{"type":"string"},"spendPoint":{"type":"string"},"bonusPoints":{"type":"string"},"codeName":{"type":"string"},"appliedTo":{"type":"string","enum":["all","specific_product","specific"]},"orderReq":{"type":"string","enum":["none","min_amount","min_quantity"]},"orderReqAmount":{"type":"string"},"specificProducts":{"type":"array","items":{"type":"object"}},"combinedWith":{"type":"array","items":{"type":"string"}},"hasLimitShipping":{"type":"boolean"},"limitShipping":{"type":"number"},"expiredAfter":{"type":"number"},"expiredUnit":{"type":"string","enum":["day","month"]},"status":{"type":"boolean"},"priority":{"type":"number"}}}}},"paths":{"/rest_api/v2/tiers/{tierId}/rewards/{rewardId}":{"put":{"tags":["Tiers"],"summary":"Update a tier reward","description":"Partial update of a tier reward. Only send fields to change.\nAll fields are optional.\n","operationId":"updateTierReward","parameters":[{"name":"tierId","in":"path","required":true,"schema":{"type":"string"}},{"name":"rewardId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TierRewardUpdate"}}}},"responses":{"200":{"description":"Reward updated"},"400":{"description":"No valid fields provided"}}}}}}
```

## Delete a tier reward

> Permanently deletes the reward. If the reward is a privilege type\
> with a Shopify auto-discount, the discount is also cleaned up.<br>

```json
{"openapi":"3.0.0","info":{"title":"Joy Loyalty Program - REST API v2","version":"2.0.0"},"tags":[{"name":"Tiers","description":"Tier management"}],"servers":[{"url":"https://dev-api.joy.so","description":"Staging"},{"url":"https://api.joy.so","description":"Production"}],"security":[{"JoyAuth":[],"JoySecretAuth":[]}],"components":{"securitySchemes":{"JoyAuth":{"type":"apiKey","in":"header","name":"X-Joy-Loyalty-App-Key"}}},"paths":{"/rest_api/v2/tiers/{tierId}/rewards/{rewardId}":{"delete":{"tags":["Tiers"],"summary":"Delete a tier reward","description":"Permanently deletes the reward. If the reward is a privilege type\nwith a Shopify auto-discount, the discount is also cleaned up.\n","operationId":"deleteTierReward","parameters":[{"name":"tierId","in":"path","required":true,"schema":{"type":"string"}},{"name":"rewardId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Reward deleted","content":{"application/json":{}}},"404":{"description":"Reward not found or not owned by shop"}}}}}}
```

## Get tier benefits by type

> Retrieve tier benefits filtered by benefit type

```json
{"openapi":"3.0.0","info":{"title":"Joy Loyalty Program - REST API v2","version":"2.0.0"},"tags":[{"name":"Tiers","description":"Tier management"}],"servers":[{"url":"https://dev-api.joy.so","description":"Staging"},{"url":"https://api.joy.so","description":"Production"}],"security":[{"JoyAuth":[],"JoySecretAuth":[]}],"components":{"securitySchemes":{"JoyAuth":{"type":"apiKey","in":"header","name":"X-Joy-Loyalty-App-Key"}},"schemas":{"ListResponse":{"allOf":[{"$ref":"#/components/schemas/SuccessResponse"},{"type":"object","properties":{"data":{"type":"array","items":{"type":"object"}},"meta":{"type":"object","properties":{"count":{"type":"integer","description":"Number of items returned"}}}}}]},"SuccessResponse":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object"},"meta":{"type":"object","description":"Additional metadata such as counts and pagination"},"message":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}}},"Program":{"type":"object","description":"Loyalty program (earning or spending) with all fields that may be returned by the API. Note: Actual responses may only include non-null fields due to automatic filtering.","properties":{"id":{"type":"string"},"title":{"type":"string"},"type":{"type":"string","enum":["earning","spending","tier_spending","tier"]},"event":{"type":"string"},"status":{"type":"boolean"},"priority":{"type":"integer"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"expired":{"type":"boolean"},"isDraft":{"type":"boolean"},"earnBy":{"type":"string","enum":["price","order"]},"rateMoney":{"type":"number"},"earnPoint":{"type":"integer"},"startDate":{"type":"string","format":"date-time"},"endDate":{"type":"string","format":"date-time"},"autoRemovePoints":{"type":"boolean"},"appliedPlaceOrderTo":{"type":"string","enum":["all","vip-tier"]},"appliedSource":{"type":"array","items":{"type":"string"}},"translateTitle":{"type":"object"},"typeMilestone":{"type":"string"},"milestones":{"type":"array","items":{"type":"object"}},"spendPoint":{"type":"integer"},"earnAmount":{"type":"number"},"redeemType":{"type":"string"},"appliedTo":{"type":"string"},"appliedCollectionIds":{"type":"array","items":{"type":"string"}},"redeemIn":{"type":"string","enum":["available_in_pos","available_in_online_store"]},"orderReq":{"type":"string","enum":["none","min_amount"]},"orderReqAmount":{"type":"number"},"minSpendPoint":{"type":"string"},"maxSpendPoint":{"type":"string"},"expiredTime":{"type":"string"},"userAvailability":{"type":"string","enum":["allUsers","userRedeemed"]},"showLoyaltyPage":{"type":"boolean"},"limitRedeem":{"type":"string","enum":["redeemWithoutLimit","redeemLimit"]},"totalLimitationRedeem":{"type":"integer"},"combinedWith":{"type":"array","items":{"type":"string"}},"specificProducts":{"type":"array","items":{"type":"object"}},"specificProductIds":{"type":"array","items":{"type":"string"}},"specificCollections":{"type":"array","items":{"type":"object"}},"variantIds":{"type":"array","items":{"type":"string"}},"freeProductIds":{"type":"array","items":{"type":"string"}},"giftStatus":{"type":"string","enum":["none","hot","expiring-soon"]},"excludeProducts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"image":{"type":"object","properties":{"src":{"type":"string"}}}}}},"includeProducts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"image":{"type":"object","properties":{"src":{"type":"string"}}}}}},"conditions":{"type":"array","items":{"type":"object"}},"earnPointsTiers":{"type":"array","items":{"type":"object","properties":{"earnPoint":{"type":"integer"},"rateMoney":{"type":"number"}}}},"roundingMethod":{"type":"string","enum":["round","floor","ceil"]},"skipEarnPointGuest":{"type":"boolean"}}},"ErrorResponse":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"statusCode":{"type":"integer"},"details":{"type":"object"}}},"timestamp":{"type":"string","format":"date-time"}}}}},"paths":{"/rest_api/v2/tiers/benefits/{benefitType}":{"get":{"tags":["Tiers"],"summary":"Get tier benefits by type","description":"Retrieve tier benefits filtered by benefit type","parameters":[{"name":"benefitType","in":"path","required":true,"schema":{"type":"string"},"description":"Type of benefit to retrieve"}],"responses":{"200":{"description":"List of tier benefits","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ListResponse"},{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Program"}}}}]}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Plan upgrade required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}}}
```
