Skip to main content

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 threshold
  • purchaseCount - Number of purchases threshold
  • categoryPurchase - Purchase from specific categories
  • shopPurchaseAmount - Purchase amount from specific shops
  • firstTime - First-time achievement
  • specialDay - Special day/anniversary achievement

Change Types (Tier Projections)

  • upgrade - Tier upgraded
  • downgrade - Tier downgraded
  • expiry - Tier expired

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 uploading images (tier logos, badge images, interest images), use multipart/form-data content type. JSON objects/arrays in form fields 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. 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
  6. Tier Projections:

    • Represent current tier assignment for members
    • Track tier end dates for time-limited tiers
    • History is maintained for all tier changes
  7. 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
  8. Badge Member Progress:

    • Automatically created when eligible
    • Progress updates when relevant events occur (purchases, etc.)
    • Completion triggers badge unlock notification
  9. Date Formats: All date fields use ISO 8601 format (e.g., 2024-12-31T23:59:59.000Z).

  10. 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.