Cluster Service API Documentation
Overview
The Cluster Service provides a comprehensive membership tier, badge, and interest management system with support for:
- Tiers: Membership tiers with levels, requirements, benefits, and member assignments
- Tier Projections: Current tier assignments and history tracking for members
- Badges: Achievement badges with complex rules and progress tracking
- Badge Member Progress: Track member progress toward badge achievements
- Interests: Member interest categories for personalization
Base URL
baseUrl/cluster/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 tier/badge browsing may not require authentication.
Tiers API
GET baseUrl/cluster/api/tiers
Get all tiers with pagination and filtering.
Request
Endpoint: GET baseUrl/cluster/api/tiers
Query Parameters:
{
page?: number; // Page number (default: 1, min: 1)
limit?: number; // Items per page (default: 10, min: 1, max: 100)
search?: string; // Search in tier name/description
includeInActive?: boolean; // Include inactive tiers (default: false)
includeDeletedAt?: boolean; // Include soft-deleted tiers (default: false)
locale?: "en" | "th" | "cn"; // Localize response content
}
Example Request
GET baseUrl/cluster/api/tiers?page=1&limit=20&locale=en
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"message": "Tiers retrieved successfully",
"data": {
"meta": {
"page": 1,
"limit": 20,
"total": 5,
"totalPages": 1
},
"data": [
{
"id": "tier-gold-id",
"name_en": "Gold",
"name_th": "ทอง",
"name_cn": "黄金",
"description_en": "Gold tier membership with exclusive benefits",
"description_th": "สมาชิกระดับทองพร้อมสิทธิประโยชน์พิเศษ",
"description_cn": "黄金会员等级,享有专属福利",
"imageUrl": "https://storage.example.com/tiers/gold.png",
"background": "#FFD700",
"level": 3,
"requirement": {
"purchaseAmountHit": 50000
},
"benefit": [
{
"name_en": "10% Bonus Points",
"name_th": "คะแนนโบนัส 10%",
"name_cn": "10%奖励积分",
"description_en": "Get 10% bonus on all points earned",
"description_th": "รับโบนัส 10% สำหรับคะแนนทั้งหมดที่ได้รับ",
"description_cn": "所有获得的积分获得10%奖励",
"condition_en": "Valid for all purchases",
"condition_th": "ใช้ได้สำหรับการซื้อทั้งหมด",
"condition_cn": "适用于所有购买",
"type": "point_bonus",
"imageUrl": "https://storage.example.com/benefits/point-bonus.png"
}
],
"isActive": true,
"isSpecial": false,
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-15T10:30:00.000Z"
}
]
}
}
}
GET baseUrl/cluster/api/tiers/bulk
Get multiple tiers by their IDs (bulk query).
Request
Endpoint: POST baseUrl/cluster/api/tiers/bulk
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
Request Body:
{
ids: string[]; // Array of tier UUIDs (minimum 1)
}
Example Request
{
"ids": ["tier-gold-id", "tier-silver-id", "tier-bronze-id"]
}
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"message": "Bulk tiers retrieved successfully",
"data": [
{
"id": "tier-gold-id",
"name_en": "Gold",
"level": 3
},
{
"id": "tier-silver-id",
"name_en": "Silver",
"level": 2
}
]
}
}
GET baseUrl/cluster/api/tiers/level/:level
Get tier by level.
Request
Endpoint: GET baseUrl/cluster/api/tiers/level/:level
Path Parameters:
{
level: number; // Tier level
}
Query Parameters:
{
locale?: "en" | "th" | "cn"; // Localize response content
}
Example Request
GET baseUrl/cluster/api/tiers/level/3?locale=en
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"message": "Tier retrieved successfully",
"data": {
"id": "tier-gold-id",
"name_en": "Gold",
"level": 3,
"requirement": {
"purchaseAmountHit": 50000
},
"benefit": [...]
}
}
}
GET baseUrl/cluster/api/tiers/:id
Get a specific tier by ID.
Request
Endpoint: GET baseUrl/cluster/api/tiers/:id
Path Parameters:
{
id: string; // UUID of the tier
}
Query Parameters:
{
locale?: "en" | "th" | "cn"; // Localize response content
}
Example Request
GET baseUrl/cluster/api/tiers/tier-gold-id?locale=en
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"message": "Tier retrieved successfully",
"data": {
"id": "tier-gold-id",
"name_en": "Gold",
"name_th": "ทอง",
"name_cn": "黄金",
"description_en": "Gold tier membership",
"level": 3,
"requirement": {
"purchaseAmountHit": 50000
},
"benefit": [...],
"isActive": true,
"isSpecial": false
}
}
}
POST baseUrl/cluster/api/tiers
Create a new tier.
Request
Endpoint: POST baseUrl/cluster/api/tiers
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: multipart/form-data
Request Body (multipart/form-data):
{
name_en: string; // Required, max 256 chars
name_th?: string; // Optional, max 256 chars
name_cn?: string; // Optional, max 256 chars
description_en: string; // Required
description_th?: string; // Optional
description_cn?: string; // Optional
imageUrl?: string; // Optional URL
background?: string; // Optional color/background
level?: string; // Optional, will be converted to number (min: 0)
requirement: { // Required
purchaseAmountHit?: string; // Optional, will be converted to number
};
benefit?: Array<{ // Optional array of benefits
name_en: string;
name_th?: string;
name_cn?: string;
description_en: string;
description_th?: string;
description_cn?: string;
condition_en: string;
condition_th?: string;
condition_cn?: string;
type: string;
imageUrl?: string;
}>;
isActive?: string; // Optional, "true" or "false"
isSpecial?: string; // Optional, "true" or "false"
tierLogo: File; // Required image file
benefitLogos?: File[]; // Optional array of benefit logo files
}
Example Request
{
"name_en": "Gold",
"name_th": "ทอง",
"name_cn": "黄金",
"description_en": "Gold tier membership",
"description_th": "สมาชิกระดับทอง",
"description_cn": "黄金会员等级",
"level": "3",
"requirement": "{\"purchaseAmountHit\":\"50000\"}",
"benefit": "[{\"name_en\":\"10% Bonus Points\",\"description_en\":\"Get 10% bonus\",\"condition_en\":\"Valid for all\",\"type\":\"point_bonus\"}]",
"isActive": "true",
"isSpecial": "false"
}
Note: When using multipart/form-data, JSON objects/arrays should be stringified.
Response
Success Response (201 Created):
{
"statusCode": 201,
"data": {
"message": "Tier created successfully",
"data": {
"id": "tier-gold-id",
"name_en": "Gold",
"level": 3,
"isActive": true
}
}
}
PUT baseUrl/cluster/api/tiers/:id
Update an existing tier.
Request
Endpoint: PUT baseUrl/cluster/api/tiers/:id
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: multipart/form-data
Path Parameters:
{
id: string; // UUID of the tier
}
Request Body (multipart/form-data): All fields optional (same structure as POST)
Example Request
{
"name_en": "Gold Premium",
"level": "4",
"isActive": "true"
}
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"message": "Tier updated successfully",
"data": {
"id": "tier-gold-id",
"name_en": "Gold Premium",
"level": 4
}
}
}
DELETE baseUrl/cluster/api/tiers/:id
Delete (soft delete) a tier.
Request
Endpoint: DELETE baseUrl/cluster/api/tiers/:id
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Path Parameters:
{
id: string; // UUID of the tier
}
Example Request
DELETE baseUrl/cluster/api/tiers/tier-gold-id
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"message": "Tier deleted successfully"
}
}
PATCH baseUrl/cluster/api/tiers/:id/set-active
Set the active status of a tier.
Request
Endpoint: PATCH baseUrl/cluster/api/tiers/:id/set-active
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
Path Parameters:
{
id: string; // UUID of the tier
}
Request Body:
{
isActive: boolean; // Required
}
Example Request
{
"isActive": false
}
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"message": "Tier deleted successfully"
}
}
GET baseUrl/cluster/api/tiers/:id/members/count
Get the number of members in a specific tier.
Request
Endpoint: GET baseUrl/cluster/api/tiers/:id/members/count
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Path Parameters:
{
id: string; // UUID of the tier
}
Example Request
GET baseUrl/cluster/api/tiers/tier-gold-id/members/count
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"message": "Tier member count retrieved successfully",
"data": {
"count": 1250
}
}
}
GET baseUrl/cluster/api/tiers/members/counts
Get member counts for all tiers.
Request
Endpoint: GET baseUrl/cluster/api/tiers/members/counts
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Example Request
GET baseUrl/cluster/api/tiers/members/counts
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"message": "All tier member counts retrieved successfully",
"data": [
{
"tierId": "tier-gold-id",
"count": 1250
},
{
"tierId": "tier-silver-id",
"count": 3500
}
]
}
}
POST baseUrl/cluster/api/tiers/assign
Assign a tier to a member.
Request
Endpoint: POST baseUrl/cluster/api/tiers/assign
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
Request Body:
{
memberId: string; // Required, UUID
tierId: string; // Required, UUID
tierEndAt?: string; // Optional, ISO 8601 datetime
}
Example Request
{
"memberId": "550e8400-e29b-41d4-a716-446655440000",
"tierId": "tier-gold-id",
"tierEndAt": "2024-12-31T23:59:59.000Z"
}
Response
Success Response (201 Created):
{
"statusCode": 201,
"data": {
"message": "Tier assigned to member successfully",
"data": {
"id": "projection-id",
"memberId": "550e8400-e29b-41d4-a716-446655440000",
"tierId": "tier-gold-id",
"tierEndAt": "2024-12-31T23:59:59.000Z",
"createdAt": "2024-01-15T10:30:00.000Z"
}
}
}
Tier Projections API
GET baseUrl/cluster/api/tier-projections/me
Get the current authenticated member's tier projection.
Request
Endpoint: GET baseUrl/cluster/api/tier-projections/me
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Query Parameters:
{
locale?: "en" | "th" | "cn"; // Localize response content
}
Example Request
GET baseUrl/cluster/api/tier-projections/me?locale=en
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"id": "projection-id",
"memberId": "550e8400-e29b-41d4-a716-446655440000",
"tierId": "tier-gold-id",
"tier": {
"id": "tier-gold-id",
"name_en": "Gold",
"level": 3
},
"tierEndAt": "2024-12-31T23:59:59.000Z",
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-15T10:30:00.000Z"
}
}
GET baseUrl/cluster/api/tier-projections/:memberId
Get tier projection for a specific member.
Request
Endpoint: GET baseUrl/cluster/api/tier-projections/:memberId
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Path Parameters:
{
memberId: string; // UUID of the member
}
Query Parameters:
{
locale?: "en" | "th" | "cn"; // Localize response content
}
Example Request
GET baseUrl/cluster/api/tier-projections/550e8400-e29b-41d4-a716-446655440000?locale=en
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"id": "projection-id",
"memberId": "550e8400-e29b-41d4-a716-446655440000",
"tierId": "tier-gold-id",
"tierEndAt": "2024-12-31T23:59:59.000Z"
}
}
GET baseUrl/cluster/api/tier-projections
Get all tier projections with filtering and pagination.
Request
Endpoint: GET baseUrl/cluster/api/tier-projections
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)
search?: string; // Search
includeDeletedAt?: boolean; // Include soft-deleted (default: false)
includeInActive?: boolean; // Include inactive (default: false)
tierId?: string; // Filter by tier ID (UUID)
includeMember?: boolean; // Include member information (default: false)
locale?: "en" | "th" | "cn"; // Localize response content
}
Example Request
GET baseUrl/cluster/api/tier-projections?tierId=tier-gold-id&includeMember=true&page=1&limit=20
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"meta": {
"page": 1,
"limit": 20,
"total": 1250,
"totalPages": 63
},
"data": [
{
"id": "projection-id-1",
"memberId": "550e8400-e29b-41d4-a716-446655440000",
"tierId": "tier-gold-id",
"tierEndAt": "2024-12-31T23:59:59.000Z",
"member": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "user@example.com"
}
}
]
}
}
PUT baseUrl/cluster/api/tier-projections/:memberId
Update a member's tier projection.
Request
Endpoint: PUT baseUrl/cluster/api/tier-projections/:memberId
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
Path Parameters:
{
memberId: string; // UUID of the member
}
Request Body:
{
tierEndAt: string; // Required, ISO 8601 datetime
newTierTypeId: string; // Required, UUID of new tier
changeType: "upgrade" | "downgrade" | "expiry"; // Required
snapshot: { // Required
points: number; // Required, must be >= 0
};
}
Example Request
{
"tierEndAt": "2024-12-31T23:59:59.000Z",
"newTierTypeId": "tier-platinum-id",
"changeType": "upgrade",
"snapshot": {
"points": 75000
}
}
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"message": "Tier projection updated successfully",
"data": {
"success": true
}
}
}
GET baseUrl/cluster/api/tier-projections/history/me
Get tier projection history for the authenticated member.
Request
Endpoint: GET baseUrl/cluster/api/tier-projections/history/me
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)
start?: string; // Start date (ISO 8601 or date string)
end?: string; // End date (ISO 8601 or date string)
actionType?: string; // Filter by action type
locale?: "en" | "th" | "cn"; // Localize response content
}
Example Request
GET baseUrl/cluster/api/tier-projections/history/me?page=1&limit=20&locale=en
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"message": "Tier projection history retrieved successfully",
"data": {
"meta": {
"page": 1,
"limit": 20,
"total": 3,
"totalPages": 1
},
"data": [
{
"id": "history-id-1",
"changeType": "upgrade",
"snapshot": {
"points": 50000
},
"memberId": "550e8400-e29b-41d4-a716-446655440000",
"fromTier": {
"id": "tier-silver-id",
"name_en": "Silver"
},
"toTier": {
"id": "tier-gold-id",
"name_en": "Gold"
},
"createdAt": "2024-01-15T10:30:00.000Z"
}
]
}
}
}
GET baseUrl/cluster/api/tier-projections/history/:memberId
Get tier projection history for a specific member.
Request
Endpoint: GET baseUrl/cluster/api/tier-projections/history/:memberId
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Path Parameters:
{
memberId: string; // UUID of the member
}
Query Parameters: Same as GET baseUrl/cluster/api/tier-projections/history/me
Example Request
GET baseUrl/cluster/api/tier-projections/history/550e8400-e29b-41d4-a716-446655440000?page=1&limit=20
Response
Success Response (200 OK):
Same structure as GET baseUrl/cluster/api/tier-projections/history/me
GET baseUrl/cluster/api/tier-projections/summary/yearly
Get yearly tier summary statistics.
Request
Endpoint: GET baseUrl/cluster/api/tier-projections/summary/yearly
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Query Parameters:
{
year?: number; // Year (default: current year)
locale?: "en" | "th" | "cn"; // Localize response content
}
Example Request
GET baseUrl/cluster/api/tier-projections/summary/yearly?year=2024&locale=en
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"message": "Tier summary yearly retrieved successfully",
"data": {
"year": 2024,
"tierDistribution": [
{
"tierId": "tier-gold-id",
"tierName_en": "Gold",
"memberCount": 1250
},
{
"tierId": "tier-silver-id",
"tierName_en": "Silver",
"memberCount": 3500
}
],
"upgrades": 450,
"downgrades": 120
}
}
}
Badges API
GET baseUrl/cluster/api/badges
Get all badges with pagination and filtering.
Request
Endpoint: GET baseUrl/cluster/api/badges
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)
search?: string; // Search in badge name/description
includeInActive?: boolean; // Include inactive badges (default: false)
includeDeletedAt?: boolean; // Include soft-deleted badges (default: false)
includeMemberCount?: boolean; // Include member count (default: false)
locale?: "en" | "th" | "cn"; // Localize response content
}
Example Request
GET baseUrl/cluster/api/badges?page=1&limit=20&locale=en&includeMemberCount=true
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"message": "Badges retrieved successfully",
"data": {
"meta": {
"page": 1,
"limit": 20,
"total": 10,
"totalPages": 1
},
"data": [
{
"id": "badge-first-purchase-id",
"name_en": "First Purchase",
"name_th": "ซื้อครั้งแรก",
"name_cn": "首次购买",
"description_en": "Complete your first purchase",
"description_th": "ทำการซื้อครั้งแรกของคุณ",
"description_cn": "完成您的首次购买",
"imageUrl": "https://storage.example.com/badges/first-purchase.png",
"rule": {
"type": "firstTime"
},
"isActive": true,
"memberCount": 1250,
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-15T10:30:00.000Z"
}
]
}
}
}
GET baseUrl/cluster/api/badges/:id
Get a specific badge by ID.
Request
Endpoint: GET baseUrl/cluster/api/badges/:id
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Path Parameters:
{
id: string; // UUID of the badge
}
Query Parameters:
{
locale?: "en" | "th" | "cn"; // Localize response content
includeTierInfo?: boolean; // Include tier information (default: false)
includeTotalAchievers?: boolean; // Include total achievers count (default: false)
}
Example Request
GET baseUrl/cluster/api/badges/badge-first-purchase-id?locale=en&includeTierInfo=true&includeTotalAchievers=true
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"message": "Badge retrieved successfully",
"data": {
"id": "badge-first-purchase-id",
"name_en": "First Purchase",
"name_th": "ซื้อครั้งแรก",
"name_cn": "首次购买",
"description_en": "Complete your first purchase",
"description_th": "ทำการซื้อครั้งแรกของคุณ",
"description_cn": "完成您的首次购买",
"imageUrl": "https://storage.example.com/badges/first-purchase.png",
"rule": {
"type": "firstTime"
},
"isActive": true,
"totalAchievers": 1250,
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-15T10:30:00.000Z"
}
}
}
POST baseUrl/cluster/api/badges
Create a new badge.
Request
Endpoint: POST baseUrl/cluster/api/badges
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: multipart/form-data
Request Body (multipart/form-data):
{
name_en: string; // Required, max 256 chars
name_th?: string; // Optional, max 256 chars
name_cn?: string; // Optional, max 256 chars
description_en: string; // Required
description_th?: string; // Optional
description_cn?: string; // Optional
imageUrl?: string; // Optional URL
rule: { // Required badge rule (JSON string or object)
type: "totalPurchaseAmount" | "purchaseCount" | "categoryPurchase" | "shopPurchaseAmount" | "firstTime" | "specialDay";
purchaseAmount?: number;
purchaseCount?: number;
pointsAmount?: number;
minSpend?: number;
categoryIdsIncluded?: string[];
categoryIdsExcluded?: string[];
shopIdsIncluded?: string[];
shopIdsExcluded?: string[];
withinDays?: number;
anniversaryYear?: number;
window?: {
start: string; // ISO 8601 datetime
end: string; // ISO 8601 datetime
};
tierCondition?: {
type: "include" | "exclude";
tierIds: string[];
};
};
isActive?: boolean | string; // Optional, default: true
image?: File; // Optional image file
}
Example Request
{
"name_en": "Big Spender",
"name_th": "นักซื้อตัวยง",
"name_cn": "大买家",
"description_en": "Spend 10,000 THB in a single purchase",
"description_th": "ใช้จ่าย 10,000 บาทในการซื้อครั้งเดียว",
"description_cn": "单次购买消费10,000泰铢",
"rule": "{\"type\":\"totalPurchaseAmount\",\"purchaseAmount\":10000}",
"isActive": "true"
}
Response
Success Response (201 Created):
{
"statusCode": 201,
"data": {
"message": "Badge created",
"data": {
"id": "badge-big-spender-id",
"name_en": "Big Spender",
"rule": {
"type": "totalPurchaseAmount",
"purchaseAmount": 10000
},
"isActive": true
}
}
}
PUT baseUrl/cluster/api/badges/:id
Update an existing badge.
Request
Endpoint: PUT baseUrl/cluster/api/badges/:id
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: multipart/form-data
Path Parameters:
{
id: string; // UUID of the badge
}
Request Body (multipart/form-data): All fields optional (same structure as POST)
Example Request
{
"description_en": "Spend 15,000 THB in a single purchase",
"rule": "{\"type\":\"totalPurchaseAmount\",\"purchaseAmount\":15000}"
}
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"message": "Badge updated",
"data": {
"id": "badge-big-spender-id",
"name_en": "Big Spender",
"rule": {
"type": "totalPurchaseAmount",
"purchaseAmount": 15000
}
}
}
}
DELETE baseUrl/cluster/api/badges/:id
Delete (soft delete) a badge.
Request
Endpoint: DELETE baseUrl/cluster/api/badges/:id
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Path Parameters:
{
id: string; // UUID of the badge
}
Example Request
DELETE baseUrl/cluster/api/badges/badge-big-spender-id
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"message": "Badge deleted"
}
}
PATCH baseUrl/cluster/api/badges/:id/set-active
Set the active status of a badge.
Request
Endpoint: PATCH baseUrl/cluster/api/badges/:id/set-active
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
Path Parameters:
{
id: string; // UUID of the badge
}
Request Body:
{
isActive: boolean; // Required
}
Example Request
{
"isActive": false
}
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"message": "Badge active state updated"
}
}
Badge Member Progress API
GET baseUrl/cluster/api/badge-member-progress/members/me
Get badge progress for the authenticated member.
Request
Endpoint: GET baseUrl/cluster/api/badge-member-progress/members/me
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Query Parameters:
{
page?: number; // Page number (default: 0, min: 0)
limit?: number; // Items per page (default: 10, min: 0)
badgeId?: string; // Filter by badge ID (UUID)
isCompleted?: boolean; // Filter by completion status
includeBadge?: boolean; // Include badge information (default: false)
includeMember?: boolean; // Include member information (default: false)
locale?: "en" | "th" | "cn"; // Localize response content
}
Example Request
GET baseUrl/cluster/api/badge-member-progress/members/me?isCompleted=false&includeBadge=true&locale=en
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"meta": {
"page": 0,
"limit": 10,
"total": 5,
"totalPages": 1
},
"data": [
{
"id": "progress-id-1",
"memberId": "550e8400-e29b-41d4-a716-446655440000",
"badgeId": "badge-big-spender-id",
"currentValue": 7500,
"goalValue": 10000,
"isCompleted": false,
"completedAt": null,
"badge": {
"id": "badge-big-spender-id",
"name_en": "Big Spender",
"description_en": "Spend 10,000 THB in a single purchase",
"imageUrl": "https://storage.example.com/badges/big-spender.png"
},
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-15T10:30:00.000Z"
}
]
}
}
GET baseUrl/cluster/api/badge-member-progress/members/:memberId
Get badge progress for a specific member.
Request
Endpoint: GET baseUrl/cluster/api/badge-member-progress/members/:memberId
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Path Parameters:
{
memberId: string; // UUID of the member
}
Query Parameters: Same as GET baseUrl/cluster/api/badge-member-progress/members/me
Example Request
GET baseUrl/cluster/api/badge-member-progress/members/550e8400-e29b-41d4-a716-446655440000?includeBadge=true
Response
Success Response (200 OK):
Same structure as GET baseUrl/cluster/api/badge-member-progress/members/me
GET baseUrl/cluster/api/badge-member-progress
Get all badge member progress (admin endpoint).
Request
Endpoint: GET baseUrl/cluster/api/badge-member-progress
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Query Parameters: Same as GET baseUrl/cluster/api/badge-member-progress/members/me (plus can filter by any memberId)
Example Request
GET baseUrl/cluster/api/badge-member-progress?memberId=550e8400-e29b-41d4-a716-446655440000&badgeId=badge-big-spender-id
Response
Success Response (200 OK):
Same structure as GET baseUrl/cluster/api/badge-member-progress/members/me
GET baseUrl/cluster/api/badge-member-progress/:id
Get a specific badge member progress by ID.
Request
Endpoint: GET baseUrl/cluster/api/badge-member-progress/:id
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Path Parameters:
{
id: string; // UUID of the badge member progress
}
Example Request
GET baseUrl/cluster/api/badge-member-progress/progress-id-1
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"id": "progress-id-1",
"memberId": "550e8400-e29b-41d4-a716-446655440000",
"badgeId": "badge-big-spender-id",
"currentValue": 7500,
"goalValue": 10000,
"isCompleted": false,
"completedAt": null,
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-15T10:30:00.000Z"
}
}
POST baseUrl/cluster/api/badge-member-progress/progress
Update badge member progress (typically triggered by events).
Request
Endpoint: POST baseUrl/cluster/api/badge-member-progress/progress
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
Request Body:
{
type: "totalPurchaseAmount" | "purchaseCount" | "categoryPurchase" | "shopPurchaseAmount" | "firstTime" | "specialDay"; // Required
memberId: string; // Required, UUID
memberTierId?: string; // Optional, UUID
purchaseAmount?: number; // Optional, must be positive
purchaseCount?: number; // Optional, must be positive integer
pointsAmount?: number; // Optional, must be positive
minSpend?: number; // Optional, must be positive
categoryId?: string; // Optional, UUID
shopId?: string; // Optional, UUID
date?: string; // Optional, ISO 8601 date
memberCreatedAt?: string; // Optional, ISO 8601 date
}
Example Request
{
"type": "totalPurchaseAmount",
"memberId": "550e8400-e29b-41d4-a716-446655440000",
"memberTierId": "tier-gold-id",
"purchaseAmount": 5000,
"date": "2024-01-15T10:30:00.000Z"
}
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"message": "Badge member progress updated successfully",
"data": {
"updated": 3,
"completed": 1
}
}
}
POST baseUrl/cluster/api/badge-member-progress/ensure-all/me
Ensure all eligible badges have progress tracking for the authenticated member.
Request
Endpoint: POST baseUrl/cluster/api/badge-member-progress/ensure-all/me
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Example Request
POST baseUrl/cluster/api/badge-member-progress/ensure-all/me
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"message": "All badge member progress ensured"
}
}
POST baseUrl/cluster/api/badge-member-progress/ensure-all/:memberId
Ensure all eligible badges have progress tracking for a specific member.
Request
Endpoint: POST baseUrl/cluster/api/badge-member-progress/ensure-all/:memberId
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Path Parameters:
{
memberId: string; // UUID of the member
}
Example Request
POST baseUrl/cluster/api/badge-member-progress/ensure-all/550e8400-e29b-41d4-a716-446655440000
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"message": "All badge member progress ensured"
}
}
Interests API
GET baseUrl/cluster/api/interests/settings
Get all interest settings (categories).
Request
Endpoint: GET baseUrl/cluster/api/interests/settings
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)
includeInactive?: boolean; // Include inactive interests (default: false)
locale?: "en" | "th" | "cn"; // Localize response content
}
Example Request
GET baseUrl/cluster/api/interests/settings?page=1&limit=20&locale=en
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"message": "Interests retrieved successfully",
"data": {
"meta": {
"page": 1,
"limit": 20,
"total": 10,
"totalPages": 1
},
"data": [
{
"id": "interest-fashion-id",
"name_en": "Fashion",
"name_th": "แฟชั่น",
"name_cn": "时尚",
"description_en": "Fashion and clothing",
"description_th": "แฟชั่นและเสื้อผ้า",
"description_cn": "时尚与服装",
"imageUrl": "https://storage.example.com/interests/fashion.png",
"isActive": true,
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-15T10:30:00.000Z"
}
]
}
}
}
GET baseUrl/cluster/api/interests/settings/:id
Get a specific interest setting by ID.
Request
Endpoint: GET baseUrl/cluster/api/interests/settings/:id
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Path Parameters:
{
id: string; // UUID of the interest
}
Query Parameters:
{
locale?: "en" | "th" | "cn"; // Localize response content
}
Example Request
GET baseUrl/cluster/api/interests/settings/interest-fashion-id?locale=en
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"message": "Interest retrieved successfully",
"data": {
"id": "interest-fashion-id",
"name_en": "Fashion",
"name_th": "แฟชั่น",
"name_cn": "时尚",
"description_en": "Fashion and clothing",
"description_th": "แฟชั่นและเสื้อผ้า",
"description_cn": "时尚与服装",
"imageUrl": "https://storage.example.com/interests/fashion.png",
"isActive": true
}
}
}
POST baseUrl/cluster/api/interests/settings
Create a new interest setting.
Request
Endpoint: POST baseUrl/cluster/api/interests/settings
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: multipart/form-data
Request Body (multipart/form-data):
{
name_en: string; // Required, max 256 chars
name_th?: string; // Optional, max 256 chars
name_cn?: string; // Optional, max 256 chars
description_en: string; // Required
description_th?: string; // Optional
description_cn?: string; // Optional
imageUrl?: string; // Optional URL
isActive?: boolean; // Optional, default: true
image?: File; // Optional image file
}
Example Request
{
"name_en": "Technology",
"name_th": "เทคโนโลยี",
"name_cn": "科技",
"description_en": "Technology and gadgets",
"description_th": "เทคโนโลยีและอุปกรณ์",
"description_cn": "科技与电子产品",
"isActive": "true"
}
Response
Success Response (201 Created):
{
"statusCode": 201,
"data": {
"message": "Interest created",
"data": {
"id": "interest-tech-id",
"name_en": "Technology",
"isActive": true
}
}
}
PUT baseUrl/cluster/api/interests/settings/:id
Update an existing interest setting.
Request
Endpoint: PUT baseUrl/cluster/api/interests/settings/:id
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: multipart/form-data
Path Parameters:
{
id: string; // UUID of the interest
}
Request Body (multipart/form-data): All fields optional (same structure as POST)
Example Request
{
"description_en": "Updated description",
"isActive": "false"
}
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"message": "Interest updated",
"data": {
"id": "interest-tech-id",
"description_en": "Updated description",
"isActive": false
}
}
}
DELETE baseUrl/cluster/api/interests/settings/:id
Delete an interest setting.
Request
Endpoint: DELETE baseUrl/cluster/api/interests/settings/:id
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Path Parameters:
{
id: string; // UUID of the interest
}
Example Request
DELETE baseUrl/cluster/api/interests/settings/interest-tech-id
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"message": "Interest deleted"
}
}
PATCH baseUrl/cluster/api/interests/settings/:id/set-active
Set the active status of an interest setting.
Request
Endpoint: PATCH baseUrl/cluster/api/interests/settings/:id/set-active
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
Path Parameters:
{
id: string; // UUID of the interest
}
Request Body:
{
isActive: boolean; // Required
}
Example Request
{
"isActive": false
}
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"message": "Interest active state updated"
}
}
POST baseUrl/cluster/api/interests
Submit member interests (for authenticated member).
Request
Endpoint: POST baseUrl/cluster/api/interests
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
Request Body:
{
interestIds: string[]; // Array of interest UUIDs (default: empty array)
}
Example Request
{
"interestIds": ["interest-fashion-id", "interest-tech-id"]
}
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"message": "Member interests updated successfully"
}
}
POST baseUrl/cluster/api/interests/:memberId
Submit member interests for a specific member (admin).
Request
Endpoint: POST baseUrl/cluster/api/interests/:memberId
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
Path Parameters:
{
memberId?: string; // Optional UUID of the member (if omitted, uses authenticated member)
}
Request Body: Same as POST baseUrl/cluster/api/interests
Example Request
{
"interestIds": ["interest-fashion-id"]
}
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"message": "Member interests updated successfully"
}
}
GET baseUrl/cluster/api/interests/me
Get interests for the authenticated member.
Request
Endpoint: GET baseUrl/cluster/api/interests/me
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Query Parameters:
{
locale?: "en" | "th" | "cn"; // Localize response content
}
Example Request
GET baseUrl/cluster/api/interests/me?locale=en
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"interests": [
{
"id": "interest-fashion-id",
"name_en": "Fashion",
"name_th": "แฟชั่น",
"name_cn": "时尚"
}
]
}
}
GET baseUrl/cluster/api/interests/:memberId
Get interests for a specific member.
Request
Endpoint: GET baseUrl/cluster/api/interests/:memberId
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Path Parameters:
{
memberId: string; // UUID of the member
}
Query Parameters:
{
locale?: "en" | "th" | "cn"; // Localize response content
}
Example Request
GET baseUrl/cluster/api/interests/550e8400-e29b-41d4-a716-446655440000?locale=en
Response
Success Response (200 OK):
Same structure as GET baseUrl/cluster/api/interests/me
Data Types & Enums
Badge Rule Types
totalPurchaseAmount- Total purchase amount thresholdpurchaseCount- Number of purchases thresholdcategoryPurchase- Purchase from specific categoriesshopPurchaseAmount- Purchase amount from specific shopsfirstTime- First-time achievementspecialDay- Special day/anniversary achievement
Change Types (Tier Projections)
upgrade- Tier upgradeddowngrade- Tier downgradedexpiry- Tier expired
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 uploading images (tier logos, badge images, interest images), use
multipart/form-datacontent type. JSON objects/arrays in form fields 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.
-
Tier Management:
- Tiers have levels (numeric, higher = better tier)
- Requirements define what members need to achieve the tier (e.g., purchase amount)
- Benefits define what perks members get at that tier
- Special tiers can have different treatment
-
Tier Projections:
- Represent current tier assignment for members
- Track tier end dates for time-limited tiers
- History is maintained for all tier changes
-
Badge Rules:
- Complex rules can be defined with multiple conditions
- Supports tier-based eligibility
- Supports time windows for limited-time badges
- Progress is tracked automatically based on member actions
-
Badge Member Progress:
- Automatically created when eligible
- Progress updates when relevant events occur (purchases, etc.)
- Completion triggers badge unlock notification
-
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.
Support
For issues or questions, please contact the development team or refer to the main project documentation.