Skip to main content

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 active
  • out_of_window - Current time is outside the availability window
  • tier_not_match - Member's tier doesn't match the tier requirements
  • insufficient_points - Member doesn't have enough points
  • user_limit_exceeded - Member has exceeded the per-user redemption limit
  • period_quota_exceeded - Member has exceeded the per-user period quota
  • total_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 (requires discountType, benefitValue, condition.minimumSpend)
  • POINT - Point rewards (requires benefitValue)
  • PRODUCT - Physical or digital products
  • OTHER - Other reward types

Stock Types

  • LIMITED - Limited stock
  • INFINITE - 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 use
  • USED - Code has been used
  • EXPIRED - Code has expired

Reward Catalog Status

  • ACTIVE - Catalog is currently active
  • EXPIRED - Catalog's window has passed
  • UPCOMING - Catalog's window hasn't started yet
  • OUT_OF_STOCK - All quota has been redeemed

Tier Condition

  • INCLUDE - Member's tier must be in the tierRequirement list
  • EXCLUDE - Member's tier must not be in the tierRequirement list

Quota Period Types

  • day - Daily quota
  • week - Weekly quota
  • month - Monthly quota
  • all - All-time quota

Locales

  • en - English
  • th - Thai
  • cn - 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 succeeded
  • 201 Created - Resource created successfully
  • 400 Bad Request - Invalid request data or business rule violation
  • 401 Unauthorized - Missing or invalid authentication token
  • 403 Forbidden - Insufficient permissions
  • 404 Not Found - Resource not found
  • 500 Internal Server Error - Server error

Notes

  1. Authentication: Most endpoints require a valid JWT token in the Authorization header. The token's sub field should contain the member/user ID.

  2. Multipart Form Data: When creating or updating rewards/reward catalogs with images, use multipart/form-data content type. JSON objects in form fields (like condition, redeemRule, quota) should be stringified JSON strings.

  3. Localization: Use the locale query parameter to get localized content. If not specified, all language fields (name_en, name_th, name_cn, etc.) will be returned.

  4. Image URLs: Image URLs are automatically generated and mapped from the uploaded files. They are returned as full URLs in the response.

  5. Stock Management:

    • For INFINITE stock type, stock value is ignored
    • Stock can be increased/decreased using dedicated endpoints
    • Stock decreases automatically when rewards are redeemed
  6. Reward Conditions:

    • minimumSpend: Minimum purchase amount required (for vouchers)
    • timeUseWithIn: Number of days the reward code is valid after redemption
    • window: Availability window (start and end dates)
  7. 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 period
    • quota.numberInPeriod: Total redemptions allowed for all users in the period
  8. Date Formats: All date fields use ISO 8601 format (e.g., 2024-12-31T23:59:59.000Z).

  9. Pagination: Pagination defaults to page 1 with 10 items per page. Maximum limit is 100 items per page.

  10. Soft Delete: Delete operations are soft deletes (sets deletedAt timestamp). Use includeDeletedAt=true to include soft-deleted items in queries.


Support

For issues or questions, please contact the development team or refer to the main project documentation.