Reward Service API Documentation
Overview
The Reward Service provides a comprehensive reward management system with support for:
- Rewards: Base reward items (vouchers, points, products, etc.) with multilingual content, stock management, and conditional usage rules
- Reward Codes: Generated codes for rewards that can be redeemed and used by members
- Reward Catalogs: Catalog items that group rewards with redemption rules, quotas, tier conditions, and availability windows
Base URL
baseUrl/reward/api
Authentication
Most endpoints require authentication via JWT token in the Authorization header:
Authorization: Bearer <token>
The token should contain a sub field with the member/user ID.
Note: Some read endpoints (GET) for public catalog browsing may not require authentication.
Rewards API
GET baseUrl/reward/api/rewards
Get all rewards with pagination and filtering options.
Request
Endpoint: GET baseUrl/reward/api/rewards
Headers:
Content-Type: application/json
Query Parameters:
{
page?: number; // Page number (default: 1, min: 1)
limit?: number; // Items per page (default: 10, min: 1, max: 100)
includeInActive?: boolean; // Include inactive rewards (default: false)
includeDeletedAt?: boolean; // Include soft-deleted rewards (default: false)
includeStats?: boolean; // Include redemption statistics (default: false)
includeLink?: boolean; // Include linked reward catalog info (default: false)
type?: "VOUCHER" | "POINT" | "PRODUCT" | "OTHER"; // Filter by reward type
locale?: "en" | "th" | "cn"; // Localize response content (default: all languages)
}
Example Request 1: Get Active Rewards (Page 1)
GET baseUrl/reward/api/rewards?page=1&limit=20&locale=en
Example Request 2: Get All Rewards with Statistics
GET baseUrl/reward/api/rewards?includeInActive=true&includeStats=true&type=VOUCHER
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"meta": {
"page": 1,
"limit": 20,
"total": 45,
"totalPages": 3
},
"data": [
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name_en": "20% Off Discount Voucher",
"name_th": "คูปองส่วนลด 20%",
"name_cn": "8折优惠券",
"description_en": "Get 20% off on your next purchase. Valid for purchases above 500 THB.",
"description_th": "รับส่วนลด 20% สำหรับการซื้อครั้งถัดไป ใช้ได้สำหรับการซื้อมากกว่า 500 บาท",
"description_cn": "下次购买享受8折优惠。适用于500泰铢以上的购买。",
"discountType": "PERCENTAGE",
"benefitValue": 20,
"imageUrl": "https://storage.example.com/rewards/voucher-20percent.jpg",
"stock": 150,
"stockInfinite": false,
"stockType": "LIMITED",
"type": "VOUCHER",
"rewardCatalogId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"storeIds": ["store-001", "store-002"],
"condition": {
"minimumSpend": 500,
"timeUseWithIn": 30,
"window": {
"start": "2024-01-01T00:00:00.000Z",
"end": "2024-12-31T23:59:59.000Z"
}
},
"isActive": true,
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-15T10:30:00.000Z",
"stats": {
"redeemed": 23,
"used": 15,
"total": 150,
"remaining": 127
}
},
{
"id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
"name_en": "100 Bonus Points",
"name_th": "คะแนนโบนัส 100 แต้ม",
"name_cn": "100奖励积分",
"description_en": "Receive 100 bonus points instantly",
"description_th": "รับคะแนนโบนัส 100 แต้มทันที",
"description_cn": "立即获得100奖励积分",
"benefitValue": 100,
"imageUrl": "https://storage.example.com/rewards/points-100.jpg",
"stock": 0,
"stockInfinite": true,
"stockType": "INFINITE",
"type": "POINT",
"isActive": true,
"createdAt": "2024-01-20T08:00:00.000Z",
"updatedAt": "2024-01-20T08:00:00.000Z"
}
]
}
}
Error Response (400 Bad Request):
{
"statusCode": 400,
"data": {
"message": "Validation error",
"errors": [
{
"field": "limit",
"message": "Limit must be between 1 and 100"
}
]
}
}
GET baseUrl/reward/api/rewards/:id
Get a specific reward by ID.
Request
Endpoint: GET baseUrl/reward/api/rewards/:id
Path Parameters:
{
id: string; // UUID of the reward
}
Query Parameters:
{
locale?: "en" | "th" | "cn"; // Localize response content
includeStats?: boolean; // Include redemption statistics
}
Example Request
GET baseUrl/reward/api/rewards/a1b2c3d4-e5f6-7890-abcd-ef1234567890?locale=en&includeStats=true
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name_en": "20% Off Discount Voucher",
"name_th": "คูปองส่วนลด 20%",
"name_cn": "8折优惠券",
"description_en": "Get 20% off on your next purchase. Valid for purchases above 500 THB.",
"description_th": "รับส่วนลด 20% สำหรับการซื้อครั้งถัดไป ใช้ได้สำหรับการซื้อมากกว่า 500 บาท",
"description_cn": "下次购买享受8折优惠。适用于500泰铢以上的购买。",
"discountType": "PERCENTAGE",
"benefitValue": 20,
"imageUrl": "https://storage.example.com/rewards/voucher-20percent.jpg",
"stock": 150,
"stockInfinite": false,
"stockType": "LIMITED",
"type": "VOUCHER",
"rewardCatalogId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"storeIds": ["store-001", "store-002"],
"condition": {
"minimumSpend": 500,
"timeUseWithIn": 30,
"window": {
"start": "2024-01-01T00:00:00.000Z",
"end": "2024-12-31T23:59:59.000Z"
}
},
"isActive": true,
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-15T10:30:00.000Z",
"stats": {
"redeemed": 23,
"used": 15,
"total": 150,
"remaining": 127
}
}
}
Error Response (404 Not Found):
{
"statusCode": 404,
"data": {
"message": "Reward not found"
}
}
POST baseUrl/reward/api/rewards
Create a new reward. Requires authentication and supports image upload.
Request
Endpoint: POST baseUrl/reward/api/rewards
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: multipart/form-data
Request Body (multipart/form-data):
For VOUCHER type:
{
name_en: string; // Required, max 100 chars
name_th?: string; // Optional, max 100 chars
name_cn?: string; // Optional, max 100 chars
description_en: string; // Required
description_th?: string; // Optional
description_cn?: string; // Optional
discountType?: "PERCENTAGE" | "AMOUNT"; // Required for VOUCHER type
benefitValue?: string; // Optional, will be converted to number
stock?: string; // Optional, will be converted to number (min: 0)
stockType?: "LIMITED" | "INFINITE"; // Optional, default: "LIMITED"
type: "VOUCHER" | "POINT" | "PRODUCT" | "OTHER"; // Required
condition?: {
timeUseWithIn?: string; // Optional, days (will be converted to number)
minimumSpend?: string; // Required for VOUCHER (will be converted to number)
window?: {
start: string; // ISO 8601 datetime format
end: string; // ISO 8601 datetime format
};
};
rewardCatalogId?: string; // Optional UUID
storeIds?: string[]; // Optional array of store IDs
image?: File; // Optional image file upload
}
Note: For VOUCHER type, discountType, benefitValue, and condition.minimumSpend are required.
For non-POINT types, condition.timeUseWithIn and condition.window are required.
Example Request 1: Create Voucher Reward (multipart/form-data)
{
"name_en": "20% Off Discount Voucher",
"name_th": "คูปองส่วนลด 20%",
"name_cn": "8折优惠券",
"description_en": "Get 20% off on your next purchase. Valid for purchases above 500 THB.",
"description_th": "รับส่วนลด 20% สำหรับการซื้อครั้งถัดไป ใช้ได้สำหรับการซื้อมากกว่า 500 บาท",
"description_cn": "下次购买享受8折优惠。适用于500泰铢以上的购买。",
"discountType": "PERCENTAGE",
"benefitValue": "20",
"stock": "150",
"stockType": "LIMITED",
"type": "VOUCHER",
"condition": "{\"minimumSpend\":\"500\",\"timeUseWithIn\":\"30\",\"window\":{\"start\":\"2024-01-01T00:00:00.000Z\",\"end\":\"2024-12-31T23:59:59.000Z\"}}",
"storeIds": "[\"store-001\",\"store-002\"]"
}
Note: When using multipart/form-data, JSON objects in condition and arrays in storeIds should be stringified JSON strings.
Example Request 2: Create Point Reward
{
"name_en": "100 Bonus Points",
"name_th": "คะแนนโบนัส 100 แต้ม",
"name_cn": "100奖励积分",
"description_en": "Receive 100 bonus points instantly",
"description_th": "รับคะแนนโบนัส 100 แต้มทันที",
"description_cn": "立即获得100奖励积分",
"benefitValue": "100",
"stock": "0",
"stockType": "INFINITE",
"type": "POINT"
}
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name_en": "20% Off Discount Voucher",
"name_th": "คูปองส่วนลด 20%",
"name_cn": "8折优惠券",
"description_en": "Get 20% off on your next purchase. Valid for purchases above 500 THB.",
"description_th": "รับส่วนลด 20% สำหรับการซื้อครั้งถัดไป ใช้ได้สำหรับการซื้อมากกว่า 500 บาท",
"description_cn": "下次购买享受8折优惠。适用于500泰铢以上的购买。",
"discountType": "PERCENTAGE",
"benefitValue": 20,
"imageUrl": "https://storage.example.com/rewards/a1b2c3d4-e5f6-7890-abcd-ef1234567890.jpg",
"stock": 150,
"stockInfinite": false,
"stockType": "LIMITED",
"type": "VOUCHER",
"rewardCatalogId": null,
"storeIds": ["store-001", "store-002"],
"condition": {
"minimumSpend": 500,
"timeUseWithIn": 30,
"window": {
"start": "2024-01-01T00:00:00.000Z",
"end": "2024-12-31T23:59:59.000Z"
}
},
"isActive": true,
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-15T10:30:00.000Z"
},
"message": "Reward created successfully"
}
Error Response (400 Bad Request):
{
"statusCode": 400,
"data": {
"message": "Discount type and is required for voucher type"
}
}
PUT baseUrl/reward/api/rewards/:id
Update an existing reward. Requires authentication and supports image upload.
Request
Endpoint: PUT baseUrl/reward/api/rewards/:id
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: multipart/form-data
Path Parameters:
{
id: string; // UUID of the reward
}
Request Body (multipart/form-data): All fields are optional (same structure as POST baseUrl/reward/api/rewards).
Example Request
{
"name_en": "25% Off Discount Voucher",
"stock": "200",
"condition": "{\"minimumSpend\":\"600\"}"
}
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name_en": "25% Off Discount Voucher",
"name_th": "คูปองส่วนลด 20%",
"name_cn": "8折优惠券",
"description_en": "Get 20% off on your next purchase. Valid for purchases above 500 THB.",
"description_th": "รับส่วนลด 20% สำหรับการซื้อครั้งถัดไป ใช้ได้สำหรับการซื้อมากกว่า 500 บาท",
"description_cn": "下次购买享受8折优惠。适用于500泰铢以上的购买。",
"discountType": "PERCENTAGE",
"benefitValue": 20,
"imageUrl": "https://storage.example.com/rewards/a1b2c3d4-e5f6-7890-abcd-ef1234567890.jpg",
"stock": 200,
"stockInfinite": false,
"stockType": "LIMITED",
"type": "VOUCHER",
"rewardCatalogId": null,
"storeIds": ["store-001", "store-002"],
"condition": {
"minimumSpend": 600,
"timeUseWithIn": 30,
"window": {
"start": "2024-01-01T00:00:00.000Z",
"end": "2024-12-31T23:59:59.000Z"
}
},
"isActive": true,
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-20T14:30:00.000Z"
},
"message": "Reward updated successfully"
}
DELETE baseUrl/reward/api/rewards/:id
Delete (soft delete) a reward. Requires authentication.
Request
Endpoint: DELETE baseUrl/reward/api/rewards/:id
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Path Parameters:
{
id: string; // UUID of the reward
}
Example Request
DELETE baseUrl/reward/api/rewards/a1b2c3d4-e5f6-7890-abcd-ef1234567890
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
},
"message": "Reward deleted successfully"
}
POST baseUrl/reward/api/rewards/:id/increase-stock
Increase the stock of a reward. Requires authentication.
Request
Endpoint: POST baseUrl/reward/api/rewards/:id/increase-stock
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
Path Parameters:
{
id: string; // UUID of the reward
}
Request Body:
{
amount: number; // Required, minimum: 1
}
Example Request
{
"amount": 50
}
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"stock": 200,
"stockInfinite": false,
"stockType": "LIMITED"
},
"message": "Stock increased successfully"
}
POST baseUrl/reward/api/rewards/:id/decrease-stock
Decrease the stock of a reward. Requires authentication.
Request
Endpoint: POST baseUrl/reward/api/rewards/:id/decrease-stock
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
Path Parameters:
{
id: string; // UUID of the reward
}
Request Body:
{
amount: number; // Required, minimum: 1
}
Example Request
{
"amount": 25
}
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"stock": 175,
"stockInfinite": false,
"stockType": "LIMITED"
},
"message": "Stock decreased successfully"
}
Error Response (400 Bad Request):
{
"statusCode": 400,
"data": {
"message": "Stock is not enough"
}
}
PATCH baseUrl/reward/api/rewards/:id/set-active
Set the active status of a reward. Requires authentication.
Request
Endpoint: PATCH baseUrl/reward/api/rewards/:id/set-active
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
Path Parameters:
{
id: string; // UUID of the reward
}
Request Body:
{
isActive: boolean; // Required
}
Example Request
{
"isActive": false
}
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"isActive": false
},
"message": "Reward deactivated successfully"
}
Reward Codes API
GET /reward-codes
Get all reward codes with pagination and filtering. Requires authentication.
Request
Endpoint: GET /reward-codes
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Query Parameters:
{
page?: number; // Page number (default: 1, min: 1)
limit?: number; // Items per page (default: 10, min: 1, max: 100)
rewardId?: string; // Filter by reward ID (UUID)
rewardCatalogId?: string; // Filter by reward catalog ID (UUID)
memberId?: string; // Filter by member ID (UUID)
search?: string; // Search by code
includeMember?: boolean; // Include member information
includeLink?: boolean; // Include linked reward catalog info
locale?: "en" | "th" | "cn"; // Localize response content
staffId?: string; // Filter by staff ID (UUID)
start?: string; // Filter by start date (ISO 8601 datetime)
end?: string; // Filter by end date (ISO 8601 datetime)
status?: "AVAILABLE" | "USED" | "EXPIRED"; // Filter by status
}
Example Request
GET /reward-codes?page=1&limit=20&status=AVAILABLE&memberId=550e8400-e29b-41d4-a716-446655440000
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"meta": {
"page": 1,
"limit": 20,
"total": 5,
"totalPages": 1
},
"data": [
{
"id": "d4e5f6a7-b8c9-0123-def4-567890abcdef",
"code": "ABC123",
"rewardId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"memberId": "550e8400-e29b-41d4-a716-446655440000",
"staffId": null,
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-15T10:30:00.000Z",
"usedAt": null,
"expiredAt": "2024-02-15T10:30:00.000Z",
"expirationNotifiedAt": null,
"rewardCatalogId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"reward": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name_en": "20% Off Discount Voucher",
"name_th": "คูปองส่วนลด 20%",
"name_cn": "8折优惠券",
"type": "VOUCHER",
"discountType": "PERCENTAGE",
"benefitValue": 20
},
"member": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "user@example.com",
"firstname": "John",
"lastname": "Doe"
}
}
]
}
}
GET /reward-codes/me
Get all reward codes for the authenticated member. Requires authentication.
Request
Endpoint: GET /reward-codes/me
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Query Parameters:
Same as GET baseUrl/reward/api/reward-codes (excluding memberId as it's automatically set from the token)
Example Request
GET /reward-codes/me?status=AVAILABLE&locale=en
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"meta": {
"page": 1,
"limit": 20,
"total": 3,
"totalPages": 1
},
"data": [
{
"id": "d4e5f6a7-b8c9-0123-def4-567890abcdef",
"code": "ABC123",
"rewardId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"memberId": "550e8400-e29b-41d4-a716-446655440000",
"staffId": null,
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-15T10:30:00.000Z",
"usedAt": null,
"expiredAt": "2024-02-15T10:30:00.000Z",
"expirationNotifiedAt": null,
"rewardCatalogId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"reward": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name_en": "20% Off Discount Voucher",
"name_th": "คูปองส่วนลด 20%",
"name_cn": "8折优惠券",
"description_en": "Get 20% off on your next purchase",
"discountType": "PERCENTAGE",
"benefitValue": 20,
"imageUrl": "https://storage.example.com/rewards/voucher-20percent.jpg",
"type": "VOUCHER"
}
}
]
}
}
GET /reward-codes/:id
Get a specific reward code by ID. Requires authentication.
Request
Endpoint: GET /reward-codes/:id
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Path Parameters:
{
id: string; // UUID of the reward code
}
Example Request
GET /reward-codes/d4e5f6a7-b8c9-0123-def4-567890abcdef
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"id": "d4e5f6a7-b8c9-0123-def4-567890abcdef",
"code": "ABC123",
"rewardId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"memberId": "550e8400-e29b-41d4-a716-446655440000",
"staffId": null,
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-15T10:30:00.000Z",
"usedAt": null,
"expiredAt": "2024-02-15T10:30:00.000Z",
"expirationNotifiedAt": null,
"rewardCatalogId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"reward": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name_en": "20% Off Discount Voucher",
"type": "VOUCHER"
}
}
}
POST baseUrl/reward/api/reward-codes/:id/use
Mark a reward code as used. Requires authentication.
Request
Endpoint: POST baseUrl/reward/api/reward-codes/:id/use
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
Path Parameters:
{
id: string; // UUID of the reward code
}
Request Body:
{
code: string; // Required, the 6-character reward code (min: 1, max: 6)
}
Example Request
{
"code": "ABC123"
}
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"id": "d4e5f6a7-b8c9-0123-def4-567890abcdef",
"code": "ABC123",
"rewardId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"memberId": "550e8400-e29b-41d4-a716-446655440000",
"staffId": "staff-123",
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-20T14:30:00.000Z",
"usedAt": "2024-01-20T14:30:00.000Z",
"expiredAt": "2024-02-15T10:30:00.000Z",
"expirationNotifiedAt": null,
"rewardCatalogId": "b2c3d4e5-f6a7-8901-bcde-f12345678901"
},
"message": "Reward code used successfully"
}
Error Response (400 Bad Request):
{
"statusCode": 400,
"data": {
"message": "Reward code already used"
}
}
Error Response (400 Bad Request - Expired):
{
"statusCode": 400,
"data": {
"message": "Reward code expired"
}
}
POST baseUrl/reward/api/reward-codes/check
Check if a reward code is valid and can be used. Requires authentication.
Request
Endpoint: POST baseUrl/reward/api/reward-codes/check
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
Request Body:
{
code: string; // Required, the 6-character reward code (min: 1, max: 6)
}
Example Request
{
"code": "ABC123"
}
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"valid": true,
"rewardCode": {
"id": "d4e5f6a7-b8c9-0123-def4-567890abcdef",
"code": "ABC123",
"rewardId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"expiredAt": "2024-02-15T10:30:00.000Z"
},
"reward": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name_en": "20% Off Discount Voucher",
"type": "VOUCHER"
}
},
"message": "Reward code checked successfully"
}
Error Response (400 Bad Request - Invalid Code):
{
"statusCode": 400,
"data": {
"message": "Invalid reward code"
}
}
Reward Catalogs API
GET /reward-catalogs
Get all reward catalogs with pagination and advanced filtering. Does not require authentication for public browsing.
Request
Endpoint: GET /reward-catalogs
Query Parameters:
{
page?: number; // Page number (default: 1, min: 1)
limit?: number; // Items per page (default: 10, min: 1, max: 100)
includeInActive?: boolean; // Include inactive catalogs (default: false)
includeDeletedAt?: boolean; // Include soft-deleted catalogs (default: false)
includeStats?: boolean; // Include redemption statistics (default: false)
search?: string; // Search in name and description
status?: "ACTIVE" | "EXPIRED" | "UPCOMING" | "OUT_OF_STOCK"; // Filter by status
advancedRules?: "ALL" | "CUSTOM"; // Filter by tier condition rules
dateRangeStart?: string; // Filter by start date (ISO 8601 datetime)
dateRangeEnd?: string; // Filter by end date (ISO 8601 datetime)
locale?: "en" | "th" | "cn"; // Localize response content
tierRequirementIds?: string; // Comma-separated tier IDs (UUID)
tierCondition?: "INCLUDE" | "EXCLUDE"; // Tier condition type
sortBy?: "createdAt" | "pointUsage"; // Sort field
sortOrder?: "asc" | "desc"; // Sort order
pointUsageMin?: number; // Minimum point usage (min: 0)
pointUsageMax?: number; // Maximum point usage (min: 0)
storeIds?: string; // Comma-separated store IDs
}
Example Request 1: Get Active Catalogs
GET /reward-catalogs?status=ACTIVE&locale=en&includeStats=true
Example Request 2: Advanced Filtering
GET /reward-catalogs?pointUsageMin=100&pointUsageMax=500&tierRequirementIds=tier-gold-id,tier-silver-id&sortBy=pointUsage&sortOrder=asc
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"meta": {
"page": 1,
"limit": 20,
"total": 12,
"totalPages": 1
},
"data": [
{
"id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"name_en": "Summer Sale Rewards",
"name_th": "รางวัลลดราคาฤดูร้อน",
"name_cn": "夏季促销奖励",
"description_en": "Special summer rewards collection with exclusive discounts",
"description_th": "คอลเลกชันรางวัลพิเศษฤดูร้อนพร้อมส่วนลดพิเศษ",
"description_cn": "特殊夏季奖励系列,提供独家折扣",
"imageUrl": "https://storage.example.com/catalogs/summer-sale.jpg",
"rewardId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"redeemRule": {
"pointUsage": 500,
"limitPerUser": 1
},
"quota": {
"type": "month",
"numberInPeriod": 1000,
"limitPerPeriod": 2
},
"window": {
"start": "2024-06-01T00:00:00.000Z",
"end": "2024-08-31T23:59:59.000Z"
},
"tierCondition": "INCLUDE",
"tierRequirement": ["tier-gold-id", "tier-silver-id"],
"isActive": true,
"createdAt": "2024-05-15T10:30:00.000Z",
"updatedAt": "2024-05-15T10:30:00.000Z",
"stats": {
"redeemed": 245,
"used": 180,
"total": 1000,
"remaining": 755
}
}
]
}
}
GET /reward-catalogs/:id
Get a specific reward catalog by ID.
Request
Endpoint: GET /reward-catalogs/:id
Path Parameters:
{
id: string; // UUID of the reward catalog
}
Query Parameters:
{
locale?: "en" | "th" | "cn"; // Localize response content
includeStats?: boolean; // Include redemption statistics
}
Example Request
GET /reward-catalogs/b2c3d4e5-f6a7-8901-bcde-f12345678901?locale=en&includeStats=true
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"name_en": "Summer Sale Rewards",
"name_th": "รางวัลลดราคาฤดูร้อน",
"name_cn": "夏季促销奖励",
"description_en": "Special summer rewards collection with exclusive discounts",
"description_th": "คอลเลกชันรางวัลพิเศษฤดูร้อนพร้อมส่วนลดพิเศษ",
"description_cn": "特殊夏季奖励系列,提供独家折扣",
"imageUrl": "https://storage.example.com/catalogs/summer-sale.jpg",
"rewardId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"redeemRule": {
"pointUsage": 500,
"limitPerUser": 1
},
"quota": {
"type": "month",
"numberInPeriod": 1000,
"limitPerPeriod": 2
},
"window": {
"start": "2024-06-01T00:00:00.000Z",
"end": "2024-08-31T23:59:59.000Z"
},
"tierCondition": "INCLUDE",
"tierRequirement": ["tier-gold-id", "tier-silver-id"],
"isActive": true,
"createdAt": "2024-05-15T10:30:00.000Z",
"updatedAt": "2024-05-15T10:30:00.000Z",
"stats": {
"redeemed": 245,
"used": 180,
"total": 1000,
"remaining": 755
}
}
}
GET /reward-catalogs/:id/can-redeem
Check if the authenticated member can redeem a specific reward catalog. Requires authentication.
Request
Endpoint: GET /reward-catalogs/:id/can-redeem
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Path Parameters:
{
id: string; // UUID of the reward catalog
}
Example Request
GET /reward-catalogs/b2c3d4e5-f6a7-8901-bcde-f12345678901/can-redeem
Response
Success Response (200 OK - Can Redeem):
{
"statusCode": 200,
"data": {
"canRedeem": true
},
"message": "Reward catalog can redeem"
}
Success Response (200 OK - Cannot Redeem):
{
"statusCode": 200,
"data": {
"canRedeem": false
},
"message": "Reward catalog cannot redeem: insufficient_points"
}
Possible reasons for cannot redeem:
inactive- Catalog is not activeout_of_window- Current time is outside the availability windowtier_not_match- Member's tier doesn't match the tier requirementsinsufficient_points- Member doesn't have enough pointsuser_limit_exceeded- Member has exceeded the per-user redemption limitperiod_quota_exceeded- Member has exceeded the per-user period quotatotal_quota_exceeded- Total redemption quota for the period has been exceeded
POST baseUrl/reward/api/reward-catalogs
Create a new reward catalog. Requires authentication and supports image upload.
Request
Endpoint: POST baseUrl/reward/api/reward-catalogs
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: multipart/form-data
Request Body (multipart/form-data):
{
name_en: string; // Required, max 100 chars
name_th?: string; // Optional, max 100 chars
name_cn?: string; // Optional, max 100 chars
description_en: string; // Required, max 500 chars
description_th?: string; // Optional, max 500 chars
description_cn?: string; // Optional, max 500 chars
rewardId?: string; // Optional UUID of existing reward
reward?: { // Optional: create reward inline
name_en: string; // Required, max 100 chars
name_th?: string; // Optional, max 100 chars
name_cn?: string; // Optional, max 100 chars
imageUrl: string; // Required, valid URL
stock?: number | string; // Optional
};
redeemRule?: { // Optional
pointUsage?: number | string; // Points required to redeem (min: 0)
limitPerUser?: number | string; // Limit per user across all time (min: 1)
} | null;
quota?: { // Optional
type: "day" | "week" | "month" | "all"; // Period type
numberInPeriod: number | string; // Total limit for all members in period (min: 1)
limitPerPeriod?: number | string; // Limit per user per period (min: 1)
} | null;
window?: { // Optional
start: string; // ISO 8601 datetime format
end: string; // ISO 8601 datetime format
} | null;
tierCondition?: "INCLUDE" | "EXCLUDE"; // Optional tier condition
tierRequirement?: string[]; // Optional array of tier IDs
isActive?: boolean | string; // Optional, default: true
image: File; // Required image file upload
}
Example Request 1: Create Catalog with Existing Reward
{
"name_en": "Summer Sale Rewards",
"name_th": "รางวัลลดราคาฤดูร้อน",
"name_cn": "夏季促销奖励",
"description_en": "Special summer rewards collection with exclusive discounts",
"description_th": "คอลเลกชันรางวัลพิเศษฤดูร้อนพร้อมส่วนลดพิเศษ",
"description_cn": "特殊夏季奖励系列,提供独家折扣",
"rewardId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"redeemRule": "{\"pointUsage\":500,\"limitPerUser\":1}",
"quota": "{\"type\":\"month\",\"numberInPeriod\":1000,\"limitPerPeriod\":2}",
"window": "{\"start\":\"2024-06-01T00:00:00.000Z\",\"end\":\"2024-08-31T23:59:59.000Z\"}",
"tierCondition": "INCLUDE",
"tierRequirement": "[\"tier-gold-id\",\"tier-silver-id\"]",
"isActive": "true"
}
Note: When using multipart/form-data, JSON objects should be stringified.
Example Request 2: Create Catalog with Inline Reward
{
"name_en": "Winter Special",
"description_en": "Winter season special rewards",
"reward": "{\"name_en\":\"50% Off Voucher\",\"name_th\":\"คูปองลด 50%\",\"imageUrl\":\"https://example.com/reward.jpg\",\"stock\":\"100\"}",
"redeemRule": "{\"pointUsage\":1000}",
"window": "{\"start\":\"2024-12-01T00:00:00.000Z\",\"end\":\"2024-12-31T23:59:59.000Z\"}",
"isActive": "true"
}
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"name_en": "Summer Sale Rewards",
"name_th": "รางวัลลดราคาฤดูร้อน",
"name_cn": "夏季促销奖励",
"description_en": "Special summer rewards collection with exclusive discounts",
"description_th": "คอลเลกชันรางวัลพิเศษฤดูร้อนพร้อมส่วนลดพิเศษ",
"description_cn": "特殊夏季奖励系列,提供独家折扣",
"imageUrl": "https://storage.example.com/catalogs/b2c3d4e5-f6a7-8901-bcde-f12345678901.jpg",
"rewardId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"redeemRule": {
"pointUsage": 500,
"limitPerUser": 1
},
"quota": {
"type": "month",
"numberInPeriod": 1000,
"limitPerPeriod": 2
},
"window": {
"start": "2024-06-01T00:00:00.000Z",
"end": "2024-08-31T23:59:59.000Z"
},
"tierCondition": "INCLUDE",
"tierRequirement": ["tier-gold-id", "tier-silver-id"],
"isActive": true,
"createdAt": "2024-05-15T10:30:00.000Z",
"updatedAt": "2024-05-15T10:30:00.000Z"
},
"message": "Reward catalog created successfully"
}
Error Response (400 Bad Request):
{
"statusCode": 400,
"data": {
"message": "Image is required"
}
}
PUT baseUrl/reward/api/reward-catalogs/:id
Update an existing reward catalog. Requires authentication and supports image upload.
Request
Endpoint: PUT baseUrl/reward/api/reward-catalogs/:id
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: multipart/form-data
Path Parameters:
{
id: string; // UUID of the reward catalog
}
Request Body (multipart/form-data):
All fields are optional (same structure as POST baseUrl/reward/api/reward-catalogs, but image is optional for updates).
Example Request
{
"name_en": "Summer Sale Rewards Updated",
"redeemRule": "{\"pointUsage\":600}",
"isActive": "false"
}
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"name_en": "Summer Sale Rewards Updated",
"name_th": "รางวัลลดราคาฤดูร้อน",
"name_cn": "夏季促销奖励",
"description_en": "Special summer rewards collection with exclusive discounts",
"description_th": "คอลเลกชันรางวัลพิเศษฤดูร้อนพร้อมส่วนลดพิเศษ",
"description_cn": "特殊夏季奖励系列,提供独家折扣",
"imageUrl": "https://storage.example.com/catalogs/b2c3d4e5-f6a7-8901-bcde-f12345678901.jpg",
"rewardId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"redeemRule": {
"pointUsage": 600,
"limitPerUser": 1
},
"quota": {
"type": "month",
"numberInPeriod": 1000,
"limitPerPeriod": 2
},
"window": {
"start": "2024-06-01T00:00:00.000Z",
"end": "2024-08-31T23:59:59.000Z"
},
"tierCondition": "INCLUDE",
"tierRequirement": ["tier-gold-id", "tier-silver-id"],
"isActive": false,
"createdAt": "2024-05-15T10:30:00.000Z",
"updatedAt": "2024-05-20T14:30:00.000Z"
},
"message": "Reward catalog updated successfully"
}
DELETE baseUrl/reward/api/reward-catalogs/:id
Delete (soft delete) a reward catalog. Requires authentication.
Request
Endpoint: DELETE baseUrl/reward/api/reward-catalogs/:id
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Path Parameters:
{
id: string; // UUID of the reward catalog
}
Example Request
DELETE baseUrl/reward/api/reward-catalogs/b2c3d4e5-f6a7-8901-bcde-f12345678901
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"id": "b2c3d4e5-f6a7-8901-bcde-f12345678901"
},
"message": "Reward catalog deleted successfully"
}
PATCH baseUrl/reward/api/reward-catalogs/:id/set-active
Set the active status of a reward catalog. Requires authentication.
Request
Endpoint: PATCH baseUrl/reward/api/reward-catalogs/:id/set-active
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
Path Parameters:
{
id: string; // UUID of the reward catalog
}
Request Body:
{
isActive: boolean | string; // Required (can be boolean or "true"/"false" string)
}
Example Request
{
"isActive": true
}
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"isActive": true
},
"message": "Reward catalog activated successfully"
}
POST baseUrl/reward/api/reward-catalogs/:id/redeem
Redeem a reward catalog for the authenticated member. Requires authentication.
Request
Endpoint: POST baseUrl/reward/api/reward-catalogs/:id/redeem
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Path Parameters:
{
id: string; // UUID of the reward catalog
}
Note: The member ID is automatically extracted from the JWT token (sub field).
Example Request
POST baseUrl/reward/api/reward-catalogs/b2c3d4e5-f6a7-8901-bcde-f12345678901/redeem
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"id": "d4e5f6a7-b8c9-0123-def4-567890abcdef",
"code": "XYZ789",
"rewardId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"memberId": "550e8400-e29b-41d4-a716-446655440000",
"staffId": null,
"createdAt": "2024-01-20T14:30:00.000Z",
"updatedAt": "2024-01-20T14:30:00.000Z",
"usedAt": null,
"expiredAt": "2024-02-20T14:30:00.000Z",
"expirationNotifiedAt": null,
"rewardCatalogId": "b2c3d4e5-f6a7-8901-bcde-f12345678901"
},
"message": "Reward catalog redeemed successfully"
}
Error Response (400 Bad Request):
{
"statusCode": 400,
"data": {
"message": "Reward catalog cannot redeem: insufficient_points"
}
}
Data Types & Enums
Reward Types
VOUCHER- Discount vouchers (requiresdiscountType,benefitValue,condition.minimumSpend)POINT- Point rewards (requiresbenefitValue)PRODUCT- Physical or digital productsOTHER- Other reward types
Stock Types
LIMITED- Limited stockINFINITE- Unlimited stock
Discount Types
PERCENTAGE- Percentage-based discount (e.g., 20% off)AMOUNT- Fixed amount discount (e.g., 100 THB off)
Reward Code Status
AVAILABLE- Code is available for useUSED- Code has been usedEXPIRED- Code has expired
Reward Catalog Status
ACTIVE- Catalog is currently activeEXPIRED- Catalog's window has passedUPCOMING- Catalog's window hasn't started yetOUT_OF_STOCK- All quota has been redeemed
Tier Condition
INCLUDE- Member's tier must be in thetierRequirementlistEXCLUDE- Member's tier must not be in thetierRequirementlist
Quota Period Types
day- Daily quotaweek- Weekly quotamonth- Monthly quotaall- All-time quota
Locales
en- Englishth- Thaicn- Chinese (Simplified)
Error Responses
All error responses follow this structure:
{
"statusCode": <HTTP_STATUS_CODE>,
"data": {
"message": "<Error message>",
"errors": [ // Optional, for validation errors
{
"field": "<field_name>",
"message": "<validation message>"
}
]
}
}
Common HTTP Status Codes
200 OK- Request succeeded201 Created- Resource created successfully400 Bad Request- Invalid request data or business rule violation401 Unauthorized- Missing or invalid authentication token403 Forbidden- Insufficient permissions404 Not Found- Resource not found500 Internal Server Error- Server error
Notes
-
Authentication: Most endpoints require a valid JWT token in the
Authorizationheader. The token'ssubfield should contain the member/user ID. -
Multipart Form Data: When creating or updating rewards/reward catalogs with images, use
multipart/form-datacontent type. JSON objects in form fields (likecondition,redeemRule,quota) should be stringified JSON strings. -
Localization: Use the
localequery parameter to get localized content. If not specified, all language fields (name_en,name_th,name_cn, etc.) will be returned. -
Image URLs: Image URLs are automatically generated and mapped from the uploaded files. They are returned as full URLs in the response.
-
Stock Management:
- For
INFINITEstock type, stock value is ignored - Stock can be increased/decreased using dedicated endpoints
- Stock decreases automatically when rewards are redeemed
- For
-
Reward Conditions:
minimumSpend: Minimum purchase amount required (for vouchers)timeUseWithIn: Number of days the reward code is valid after redemptionwindow: Availability window (start and end dates)
-
Reward Catalog Redemption Rules:
redeemRule.pointUsage: Points required to redeem (0 means free)redeemRule.limitPerUser: Total redemptions allowed per user (across all time)quota.limitPerPeriod: Redemptions allowed per user per periodquota.numberInPeriod: Total redemptions allowed for all users in the period
-
Date Formats: All date fields use ISO 8601 format (e.g.,
2024-12-31T23:59:59.000Z). -
Pagination: Pagination defaults to page 1 with 10 items per page. Maximum limit is 100 items per page.
-
Soft Delete: Delete operations are soft deletes (sets
deletedAttimestamp). UseincludeDeletedAt=trueto include soft-deleted items in queries.
Support
For issues or questions, please contact the development team or refer to the main project documentation.