Shop It Docs
Developer Resourcespromotion

Promotion Module API & Integration Guide

API contracts for promotion admin and customer/mobile coupon workflows.

Audience: Mobile/web frontend developers Scope: Coupon apply, validation endpoints

Promotion Module - API & Integration Guide

1. How to Read / Quick Metadata

  • Module: Promotion
  • Primary base URLs:
    • Admin: /api/admin/promotions
    • Customer: /api/promotions
    • Mobile-composed customer: /api/mobile/promotions
  • Auth model:
    • Admin routes: JwtAuthGuard + RoleGuard + @Permissions(...)
    • Customer routes: JwtAuthGuard
  • Response envelope: ResponseDto<T> with paginated responses for list endpoints

2. High-Level Overview

Promotion API covers:

  • admin coupon lifecycle management (CRUD + disable)
  • customer coupon operations (apply/remove)
  • customer read paths (active promotions and personal usage history)

POST /promotions/apply supports two behaviors:

  • cart mutation mode when productId is omitted
  • product-only validation mode when productId is provided

3. Core Concepts and Terminology

  • promotion: coupon contract (promotion table)
  • promotion scope: target filter rows (promotion_scope) tied to product/category/tag
  • promotion usage: redemption history (promotion_usage) linked to customer + cart/order
  • eligibility model: digital-cart + online-payment only
  • code normalization: trim + uppercase before evaluation

Schema integrity contracts behind API behavior:

  • promotion_usage_target_check: at least one of cart_id/order_id
  • promotion_scope_target_check: exactly one of product_id/category_id/tag_id
  • non-negative checks for discount/cart-value fields

4. Route Summary

4.1 Admin

MethodPathPermission
GET/api/admin/promotionsPromotions_READ
GET/api/admin/promotions/:idPromotions_READ
POST/api/admin/promotionsPromotions_CREATE
PATCH/api/admin/promotions/:idPromotions_UPDATE
DELETE/api/admin/promotions/:idPromotions_DELETE
POST/api/admin/promotions/:id/disablePromotions_UPDATE

4.2 Customer / Mobile

MethodPathBehavior
POST/api/promotions/applyapply to cart when productId missing; validate-only when productId present
POST/api/promotions/removeremove coupon from active cart
GET/api/promotionslist active promotions
GET/api/promotions/my-usagelist current user's promotion usage

Mobile-composed routes are available under /api/mobile/promotions/....

Route Details

Apply Coupon

AspectDetails
EndpointPOST /api/promotions/apply or /api/mobile/promotions/apply
AuthJwtAuthGuard
Request"" code: string, productId?: number ""
ResponseResponseDto<AppliedPromotionDto>
Errors400 not applicable, 404 not found

Remove Coupon

AspectDetails
EndpointPOST /api/promotions/remove or /api/mobile/promotions/remove
AuthJwtAuthGuard
Requestempty
ResponseResponseDto<void>
Errors-

5. Query Parameters

Common list fields from QueryDto:

  • pagination (default true)
  • page (default 1)
  • size (default 20)
  • search (optional)
  • order (asc/desc, default desc unless endpoint override)

Endpoint-specific parameters:

  • admin list (GET /api/admin/promotions): status, sort
  • customer active list (GET /api/promotions): discountType, sort, order (default asc)
  • customer usage list (GET /api/promotions/my-usage): sort (discountApplied|createdAt)

6. Response Shape Examples

Active promotion list item example:

"
  "id": 12,
  "code": "SUMMER2026",
  "description": "Summer sale discount",
  "discountType": "percentage",
  "discountValue": 10,
  "maxDiscountCap": 5000,
  "minCartValue": 1000,
  "cartTypeEligibility": "digital",
  "startDate": "2026-04-01T00:00:00.000Z",
  "endDate": "2026-04-30T23:59:59.000Z"
"

Product validation mode response (POST /api/promotions/apply with productId):

"
  "message": "Coupon validated for product",
  "data": "
    "valid": true,
    "discount": 1000,
    "promotionId": 42,
    "promotionCode": "SUMMER2026"
  "",
  "errorCode": null
"

7. Enums

Promotion enums in active contracts:

  • promotion_status: active, disabled, expired
  • discount_type: flat, percentage
  • cart_type_eligibility: digital
  • payment_method_eligibility: online
  • scope_type: product, category, tag

8. Integration Diagram

9. Caching

Key prefixPurpose
promotions:admin:list:admin listing
promotions:admin:detail:admin detail
promotions:customer:list:customer active listing
promotions:code:normalized code lookup

TTL envs:

  • PROMOTION_ADMIN_CACHE_TTL_SECONDS
  • PROMOTION_CUSTOMER_CACHE_TTL_SECONDS

10. Error Handling

All promotion API errors return the standard error envelope with errorCode.

"
  "statusCode": 400,
  "errorCode": "PROMOTION_NOT_APPLICABLE",
  "message": "Coupon is no longer active.",
  "timestamp": "2026-03-20T10:00:00.000Z",
  "path": "/api/promotions/apply"
"
HTTPerrorCodeCondition
400PROMOTION_USAGE_CONFLICTpromotion has usage history and cannot be modified/deleted
400PROMOTION_INVALID_DATE_RANGEdate validation failed
400PROMOTION_DISCOUNT_INVALIDdiscount validation failed
400PROMOTION_SCOPE_INVALIDone or more scope IDs are invalid
400PROMOTION_NOT_APPLICABLEcoupon evaluation failed
404PROMOTION_NOT_FOUNDpromotion does not exist
409PROMOTION_CODE_EXISTSduplicate promotion code
429RATE_LIMIT_EXCEEDEDadmin or customer rate limit exceeded

Time fields in this module are stored as timezone-aware values and should be handled as ISO-8601 instants by API consumers.

11. Endpoint Reference + Payload Cheatsheet

11.1 Payload Cheatsheet Table (Every Endpoint)

MethodPathAuth / PermissionRequest DTO / ParamsSuccess DTONotes
GET/api/admin/promotionsAdmin + Promotions_READQueryPromotionsAdminDto "" page?, size?, pagination?, search?, status?, sort?, order? ""PromotionDto[] paginatedAdmin lists all promotions with filters
GET/api/admin/promotions/:idAdmin + Promotions_READpath: id (int)PromotionDtoAdmin fetches single promotion detail with scopes
POST/api/admin/promotionsAdmin + Promotions_CREATECreatePromotionDto "" code: string, description?: string, discountType: "flat""percentage", discountValue: number, maxDiscountCap?: number, minCartValue?: number, cartTypeEligibility?: string, paymentMethodEligibility?: string, startDate: ISO8601, endDate: ISO8601, scopes?: ScopeDto[] ""PromotionDto
PATCH/api/admin/promotions/:idAdmin + Promotions_UPDATEpath: id (int), body: UpdatePromotionDto "" description?, discountType?, discountValue?, maxDiscountCap?, minCartValue?, startDate?, endDate?, isActive?, scopes? ""PromotionDtoAdmin updates promotion; fails if has usage
DELETE/api/admin/promotions/:idAdmin + Promotions_DELETEpath: id (int)"" deleted: boolean ""Admin deletes promotion; fails if has usage history
POST/api/admin/promotions/:id/disableAdmin + Promotions_UPDATEpath: id (int)PromotionDtoAdmin disables active promotion
POST/api/promotions/applyUser JWTApplyPromotionDto "" code: string, productId?: number ""AppliedPromotionDto or CouponValidationDtoApplies coupon to cart; if productId provided, validates only
POST/api/mobile/promotions/applyUser JWTApplyPromotionDto "" code: string, productId?: number ""AppliedPromotionDto or CouponValidationDtoMobile-composed equivalent of apply
POST/api/promotions/removeUser JWTnoneResponseDto<void>Removes applied coupon from active cart
POST/api/mobile/promotions/removeUser JWTnoneResponseDto<void>Mobile-composed equivalent of remove
GET/api/promotionsUser JWTQueryActivePromotionsDto "" page?, size?, pagination?, discountType?, sort?, order? ""ActivePromotionDto[] paginatedLists all currently active promotions
GET/api/mobile/promotionsUser JWTQueryActivePromotionsDto "" page?, size?, pagination?, discountType?, sort?, order? ""ActivePromotionDto[] paginatedMobile-composed equivalent of active list
GET/api/promotions/my-usageUser JWTQueryMyUsageDto "" page?, size?, pagination?, sort? ""PromotionUsageDto[] paginatedLists current user's redemption history
GET/api/mobile/promotions/my-usageUser JWTQueryMyUsageDto "" page?, size?, pagination?, sort? ""PromotionUsageDto[] paginatedMobile-composed equivalent of usage list

11.1.1 Admin Promotions Endpoint Query Variations

MethodPathQuery ParametersNotes
GET/api/admin/promotions(no params)Default: page=1, size=20, sort=createdAt, order=desc
GET/api/admin/promotions?page=2&size=50Custom pagination
GET/api/admin/promotions?status=activeFilter by active status
GET/api/admin/promotions?status=disabledFilter by disabled status
GET/api/admin/promotions?status=expiredFilter by expired status
GET/api/admin/promotions?search=SUMMERSearch by code/description
GET/api/admin/promotions?sort=startDate&order=ascSort by start date ascending

11.1.2 Customer Active Promotions Query Variations

MethodPathQuery ParametersNotes
GET/api/promotions(no params)Default: page=1, size=20, discountType optional, sort=startDate, order=asc
GET/api/promotions?discountType=percentageFilter percentage discounts only
GET/api/promotions?discountType=flatFilter flat discounts only
GET/api/promotions?sort=endDate&order=ascSort by expiration date
GET/api/promotions?pagination=falseReturns all active records

11.1.3 Customer Usage History Query Variations

MethodPathQuery ParametersNotes
GET/api/promotions/my-usage(no params)Default: page=1, size=20, sort=createdAt, order=desc
GET/api/promotions/my-usage?sort=discountAppliedSort by discount amount
GET/api/promotions/my-usage?page=1&size=50Custom pagination

11.1.4 Apply Coupon Request Variations

MethodPathRequest BodyNotes
POST/api/promotions/apply"" "code": "SUMMER2026" ""Apply to cart (productId omitted)
POST/api/promotions/apply"" "code": "SUMMER2026", "productId": 101 ""Validate for specific product
POST/api/mobile/promotions/apply"" "code": "FLAT500" ""Mobile equivalent - apply to cart
POST/api/mobile/promotions/apply"" "code": "FLAT500", "productId": 205 ""Mobile equivalent - product validation

11.1.5 Admin Create Promotion Request Variations

MethodPathRequest BodyNotes
POST/api/admin/promotions"" "code": "SUMMER2026", "discountType": "percentage", "discountValue": 10, "maxDiscountCap": 5000, "startDate": "2026-04-01T00:00:00.000Z", "endDate": "2026-04-30T23:59:59.000Z" ""Percentage discount with cap
POST/api/admin/promotions"" "code": "FLAT500", "discountType": "flat", "discountValue": 500, "minCartValue": 2000, "startDate": "2026-04-01T00:00:00.000Z", "endDate": "2026-06-30T23:59:59.000Z" ""Flat discount with min cart
POST/api/admin/promotions"" "code": "PRODUCT50", "discountType": "percentage", "discountValue": 50, "startDate": "2026-04-01T00:00:00.000Z", "endDate": "2026-04-07T23:59:59.000Z", "scopes": ["" "scopeType": "product", "scopeId": 101 ""] ""Product-specific promotion with scope

11.1.6 Promotion Scope Variations

scopeTypescopeIdTarget
productnumberSpecific product ID
categorynumberSpecific category ID
tagnumberSpecific tag ID

11.1.7 Promotion Response Field Variations

FieldTypeNotes
idnumberUnique promotion ID
codestringNormalized (uppercase) coupon code
descriptionstring | nullPromotion description
discountTypestringflat or percentage
discountValuenumberDiscount amount or percentage
maxDiscountCapnumber | nullMaximum discount cap in paisa
minCartValuenumber | nullMinimum cart value requirement
cartTypeEligibilitystringCurrently only "digital"
paymentMethodEligibilitystringCurrently only "online"
startDateISO8601Promotion start timestamp
endDateISO8601Promotion end timestamp
statusstringactive/disabled/expired

11.1.8 Coupon Validation Response Variations

FieldTypeNotes
validbooleanWhether coupon applies to product
discountnumberCalculated discount in paisa
promotionIdnumberPromotion ID
promotionCodestringNormalized code

11.1.9 Error Response Variations

HTTPerrorCodeTrigger
400PROMOTION_NOT_APPLICABLECoupon expired/inactive/ineligible
400PROMOTION_INVALID_DATE_RANGEStart date after end date
400PROMOTION_DISCOUNT_INVALIDInvalid discount value/cap
400PROMOTION_SCOPE_INVALIDInvalid scope IDs
400PROMOTION_USAGE_CONFLICTHas usage, cannot modify/delete
404PROMOTION_NOT_FOUNDPromotion ID does not exist
409PROMOTION_CODE_EXISTSDuplicate code on create
429RATE_LIMIT_EXCEEDEDToo many requests

11.1.10 Cache Invalidation Variations

TriggerInvalidated Key Pattern
POST /admin/promotionspromotions:admin:list:*
PATCH /admin/promotions/:idpromotions:admin:list:*, promotions:admin:detail:*
POST /admin/promotions/:id/disablepromotions:admin:list:*, promotions:customer:list:*
DELETE /admin/promotions/:idpromotions:admin:list:*, promotions:admin:detail:*
POST /promotions/applycart:user:userId:*
POST /promotions/removecart:user:userId:*

11.1.11 Enums Reference

EnumValues
promotion_statusactive, disabled, expired
discount_typeflat, percentage
cart_type_eligibilitydigital
payment_method_eligibilityonline
scope_typeproduct, category, tag

12. Testing Scenarios

12.1 Suggested Test Scenarios

  • Admin creates promotion with valid date range, publishes it, and promotion appears in customer active list (GET /api/promotions).
  • Admin creates promotion with product scope (scopes["" scopeType: "product", scopeId: N ""]), applies coupon, and discount applies only to that product.
  • User applies valid coupon code to cart without productId, and cart discount is applied successfully via cart mutation.
  • User applies coupon code with productId (validation-only mode), receives "" valid: true/false "" without modifying cart.
  • User applies expired/inactive coupon and receives 400 PROMOTION_NOT_APPLICABLE with detailed reason.
  • User applies coupon without meeting minimum cart value and receives 400 PROMOTION_NOT_APPLICABLE for minCartValue.
  • User removes applied coupon from cart and cart reverts to original pricing.
  • Admin deletes promotion with usage history and receives 400 PROMOTION_USAGE_CONFLICT; disable succeeds.

See Also

  • Feature Guide: See Promotion - Feature List Section 6 (State Models) for promotion lifecycle and scope diagrams.
  • Backend Reference: See Promotion - Backend Documentation Section 11 for system architecture diagram.
  • Apply endpoint returns 429 RATE_LIMIT_EXCEEDED after sliding window threshold exceeded.