Skip to main content

Notification Service API Documentation

Overview​

The Notification Service provides a comprehensive in-app notification system with support for multiple notification types, multilingual content, and real-time updates via Server-Sent Events (SSE).

Base URL​

baseUrl/notification/api

Authentication​

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


In-App Notification API​

POST baseUrl/notification/api/in-apps​

Create a new in-app notification. This is the primary endpoint for creating custom notifications with full control over content, format, and metadata.

Request​

Endpoint: POST baseUrl/notification/api/in-apps

Headers:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json

Request Body Schema:

{
// Localized title fields (optional, but recommended to provide at least title_en)
title_en?: string; // Max 255 characters
title_th?: string; // Max 255 characters
title_cn?: string; // Max 255 characters

// Localized content fields (optional)
content_en?: string;
content_th?: string;
content_cn?: string;

// Format of the content
format?: 'raw' | 'html'; // Default: 'raw'

// Type of notification
type?: 'promotion' | 'points' | 'tier' | 'news' | 'badge'; // Default: 'news'

// Point notification subtype (only relevant when type = 'points')
pointSubtype?: 'point_received' | 'receipt_error' | 'receipt_processing' | 'point_nearly_expire' | 'point_expired';

// Tier notification subtype (only relevant when type = 'tier')
tierSubtype?: 'tier_upgraded' | 'tier_adjusted' | 'tier_renewal_reminder';

// Metadata for notifications (JSON object) - structure varies by type/subtype
metadata?: {
// For point_received type:
points?: number;
source?: string; // e.g., "purchase", "bonus", "referral"
transactionId?: string;
receiptImageUrl?: string; // Must be valid URL

// For receipt_error type:
reason_en?: string;
reason_th?: string;
reason_cn?: string;
resolution_en?: string;
resolution_th?: string;
resolution_cn?: string;
receiptImageUrl?: string;
canRetry?: boolean;
transactionId?: string;
errorCode?: string;

// For receipt_processing type:
transactionId?: string;
receiptImageUrl?: string;
estimatedProcessingTime?: string; // e.g., "2-3 minutes"
status?: 'processing' | 'validating' | 'analyzing'; // Default: 'processing'
statusMessage_en?: string;
statusMessage_th?: string;
statusMessage_cn?: string;
description_en?: string;
description_th?: string;
description_cn?: string;

// For point_nearly_expire type:
points?: number;
expiryDate?: string; // ISO date string
daysRemaining?: number;
source?: string;

// For point_expired type:
points?: number;
expiryDate?: string; // ISO date string
source?: string;

// For tier_upgraded type:
validUntil?: string; // ISO date string
newTier_en?: string;
newTier_th?: string;
newTier_cn?: string;
previousTier_en?: string;
previousTier_th?: string;
previousTier_cn?: string;
benefits_en?: string[];
benefits_th?: string[];
benefits_cn?: string[];
tierLogoUrl?: string;

// For tier_adjusted type:
validUntil?: string;
newTier_en?: string;
newTier_th?: string;
newTier_cn?: string;
previousTier_en?: string;
previousTier_th?: string;
previousTier_cn?: string;
reason_en?: string;
reason_th?: string;
reason_cn?: string;
requirements_en?: string[];
requirements_th?: string[];
requirements_cn?: string[];
tierLogoUrl?: string;

// For tier_renewal_reminder type:
expiryDate?: string; // ISO date string (required)
daysRemaining?: number; // Required
currentTier_en?: string;
currentTier_th?: string;
currentTier_cn?: string;
requirements_en?: string[];
requirements_th?: string[];
requirements_cn?: string[];
benefits_en?: string[];
benefits_th?: string[];
benefits_cn?: string[];
tierLogoUrl?: string;

// For promotion type:
promotionId?: string;
discount?: number;
validUntil?: string;
terms?: string;
rewardName_en?: string;
rewardName_th?: string;
rewardName_cn?: string;
eventType?: string;

// For news type:
newsId?: string;
category?: string;
priority?: 'low' | 'medium' | 'high';
tags?: string[];

// For badge type:
badgeId?: string;
badgeCategory?: string;
earnedAt?: string; // ISO date string
rarity?: 'common' | 'rare' | 'epic' | 'legendary';
progressPercentage?: number; // 0-100
badgeName?: string;
currentValue?: number;
goalValue?: number;
daysLeft?: number | null;
unlockDescription?: {
en?: string;
th?: string;
cn?: string;
};
badgeRule?: {
type?: string;
purchaseAmount?: number;
purchaseCount?: number;
pointsAmount?: number;
minSpend?: number;
withinDays?: number;
window?: {
start?: string;
end?: string;
};
};
badgeNames?: {
en?: string;
th?: string;
cn?: string;
};
badgeImageUrl?: string;
badgeRarity?: 'common' | 'rare' | 'epic' | 'legendary';
unlockPercentage?: number;
};

// Member/User ID who will receive this notification (required, must be valid UUID)
memberId: string; // UUID format
}

Example Request 1: Simple News Notification​

{
"title_en": "Welcome to Happy Mall!",
"title_th": "ยินดีต้อนรับสู่ Happy Mall!",
"title_cn": "欢迎来到 Happy Mall!",
"content_en": "Thank you for joining us. Start earning points with your first purchase!",
"content_th": "ขอบคุณที่เข้าร่วมกับเรา เริ่มรับคะแนนจากการซื้อครั้งแรกของคุณ!",
"content_cn": "感谢您的加入。从您的第一次购买开始赚取积分!",
"format": "raw",
"type": "news",
"memberId": "550e8400-e29b-41d4-a716-446655440000"
}

Example Request 2: Point Received Notification with Metadata​

{
"title_en": "🎉 150 Points Received",
"title_th": "🎉 ได้รับ 150 คะแนน",
"title_cn": "🎉 获得150积分",
"content_en": "You have received 150 points from purchase! Your recent purchase has earned you 150 Points! Keep snapping those receipts to climb your tier and grab more Happy Mall perks.",
"content_th": "คุณได้รับ 150 คะแนนจากการซื้อ! ซื้อสินค้าล่าสุดของคุณได้รับคะแนน 150 คะแนน! รักษาความสะอาดของคุณเพื่อเลื่อนขั้นเป็นระดับของคุณและรับสิทธิประโยชน์จาก Happy Mall อีกมากมาย",
"content_cn": "您已从购买获得 150 积分!您的最近购买已获得 150 积分!保持拍照那些收据以提升您的等级并获得更多 Happy Mall 福利。",
"format": "raw",
"type": "points",
"pointSubtype": "point_received",
"metadata": {
"points": 150,
"source": "purchase",
"transactionId": "txn_1234567890",
"receiptImageUrl": "https://storage.example.com/receipts/txn_1234567890.jpg"
},
"memberId": "550e8400-e29b-41d4-a716-446655440000"
}

Example Request 3: Tier Upgraded Notification​

{
"title_en": "🎊 Tier Upgrade!",
"title_th": "🎊 เลื่อนระดับ!",
"title_cn": "🎊 等级升级!",
"content_en": "Congratulations! You've been upgraded to Gold Tier. Enjoy exclusive benefits!",
"content_th": "ยินดีด้วย! คุณได้รับการอัพเกรดเป็นระดับ Gold รับสิทธิประโยชน์พิเศษ!",
"content_cn": "恭喜!您已升级为黄金等级。享受专属福利!",
"format": "raw",
"type": "tier",
"tierSubtype": "tier_upgraded",
"metadata": {
"validUntil": "2025-12-31T23:59:59Z",
"newTier_en": "Gold",
"newTier_th": "ทอง",
"newTier_cn": "黄金",
"previousTier_en": "Silver",
"previousTier_th": "เงิน",
"previousTier_cn": "白银",
"benefits_en": [
"10% bonus points on all purchases",
"Free shipping on orders over 500 THB",
"Early access to sales"
],
"benefits_th": [
"คะแนนโบนัส 10% สำหรับการซื้อทั้งหมด",
"จัดส่งฟรีสำหรับคำสั่งซื้อมากกว่า 500 บาท",
"เข้าถึงการขายก่อนใคร"
],
"benefits_cn": [
"所有购买获得10%奖励积分",
"订单超过500泰铢免费送货",
"提前获得销售机会"
],
"tierLogoUrl": "https://cdn.example.com/tiers/gold.png"
},
"memberId": "550e8400-e29b-41d4-a716-446655440000"
}

Example Request 4: Receipt Error Notification​

{
"title_en": "Receipt Issue: Processing Error",
"title_th": "ปัญหาใบเสร็จ: ข้อผิดพลาดในการประมวลผล",
"title_cn": "收据问题:处理错误",
"content_en": "Something's wrong with your receipt. The image quality is too low. Please try uploading a clearer image. You can retry",
"content_th": "มีปัญหากับใบเสร็จของคุณ คุณภาพของภาพต่ำเกินไป กรุณาลองอัปโหลดภาพที่ชัดเจนขึ้น คุณสามารถลองใหม่ได้",
"content_cn": "您的收据有问题。图像质量太低。请尝试上传更清晰的图像。您可以重试",
"format": "raw",
"type": "points",
"pointSubtype": "receipt_error",
"metadata": {
"reason_en": "The image quality is too low",
"reason_th": "คุณภาพของภาพต่ำเกินไป",
"reason_cn": "图像质量太低",
"resolution_en": "Please try uploading a clearer image",
"resolution_th": "กรุณาลองอัปโหลดภาพที่ชัดเจนขึ้น",
"resolution_cn": "请尝试上传更清晰的图像",
"receiptImageUrl": "https://storage.example.com/receipts/txn_1234567890.jpg",
"canRetry": true,
"transactionId": "txn_1234567890",
"errorCode": "IMAGE_QUALITY_LOW"
},
"memberId": "550e8400-e29b-41d4-a716-446655440000"
}

Example Request 5: HTML Format Promotion​

{
"title_en": "Special Promotion: 20% Off All Items",
"title_th": "โปรโมชั่นพิเศษ: ลด 20% ทุกสินค้า",
"title_cn": "特别促销:所有商品8折",
"content_en": "<h2>Limited Time Offer!</h2><p>Get <strong>20% off</strong> on all items. Use code <code>SAVE20</code> at checkout.</p><a href='https://app.example.com/promotions/summer2024'>Shop Now</a>",
"content_th": "<h2>ข้อเสนอจำกัดเวลา!</h2><p>รับส่วนลด <strong>20%</strong> สำหรับสินค้าทั้งหมด ใช้รหัส <code>SAVE20</code> เมื่อชำระเงิน</p><a href='https://app.example.com/promotions/summer2024'>ซื้อเลย</a>",
"content_cn": "<h2>限时优惠!</h2><p>所有商品享受<strong>8折</strong>优惠。结账时使用代码<code>SAVE20</code>。</p><a href='https://app.example.com/promotions/summer2024'>立即购买</a>",
"format": "html",
"type": "promotion",
"metadata": {
"promotionId": "promo_summer2024",
"discount": 20,
"validUntil": "2024-08-31T23:59:59Z",
"terms": "Valid for all items. Cannot be combined with other offers.",
"eventType": "summer_sale"
},
"memberId": "550e8400-e29b-41d4-a716-446655440000"
}

Response​

Success Response (201 Created):

{
"statusCode": 201,
"data": {
"message": "Notification created successfully",
"data": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"title_en": "🎉 150 Points Received",
"title_th": "🎉 ได้รับ 150 คะแนน",
"title_cn": "🎉 获得150积分",
"content_en": "You have received 150 points from purchase! Your recent purchase has earned you 150 Points! Keep snapping those receipts to climb your tier and grab more Happy Mall perks.",
"content_th": "คุณได้รับ 150 คะแนนจากการซื้อ! ซื้อสินค้าล่าสุดของคุณได้รับคะแนน 150 คะแนน! รักษาความสะอาดของคุณเพื่อเลื่อนขั้นเป็นระดับของคุณและรับสิทธิประโยชน์จาก Happy Mall อีกมากมาย",
"content_cn": "您已从购买获得 150 积分!您的最近购买已获得 150 积分!保持拍照那些收据以提升您的等级并获得更多 Happy Mall 福利。",
"format": "raw",
"type": "points",
"pointSubtype": "point_received",
"metadata": {
"points": 150,
"source": "purchase",
"transactionId": "txn_1234567890",
"receiptImageUrl": "https://storage.example.com/receipts/txn_1234567890.jpg"
},
"memberId": "550e8400-e29b-41d4-a716-446655440000",
"readAt": null,
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-15T10:30:00.000Z"
}
}
}

Error Response (400 Bad Request):

{
"statusCode": 400,
"data": {
"message": "Validation error",
"errors": [
{
"field": "memberId",
"message": "Invalid UUID format"
},
{
"field": "title_en",
"message": "String must contain at most 255 character(s)"
}
]
}
}

Error Response (401 Unauthorized):

{
"statusCode": 401,
"data": {
"message": "Unauthorized - Invalid or missing token"
}
}

Auto-Translation Feature​

If you only provide English content (title_en and/or content_en), the service will automatically translate it to Thai (title_th, content_th) and Chinese (title_cn, content_cn) using the translation service. However, it's recommended to provide all translations manually for better accuracy.

Example Request with Auto-Translation:

{
"title_en": "New Feature Available",
"content_en": "Check out our new feature!",
"format": "raw",
"type": "news",
"memberId": "550e8400-e29b-41d4-a716-446655440000"
}

The service will automatically generate title_th, title_cn, content_th, and content_cn.

Real-Time Notifications​

When a notification is created, it automatically emits a Server-Sent Event (SSE) to the recipient member. The notification will be available in real-time if the member is connected to the SSE endpoint (GET baseUrl/notification/api/in-apps/me/events).

Notes​

  1. Member ID: Must be a valid UUID v4 format
  2. Title Length: Maximum 255 characters per language
  3. Content Format:
    • raw: Plain text content
    • html: HTML formatted content (will be rendered in the app)
  4. Metadata: The structure of metadata varies based on the type and subtype fields. Ensure the metadata matches the expected structure for your notification type.
  5. URLs: Any URL fields (like receiptImageUrl, tierLogoUrl) must be valid URLs
  6. Dates: Date fields should be in ISO 8601 format (e.g., 2024-12-31T23:59:59Z)

Additional In-App Notification Endpoints​

GET baseUrl/notification/api/in-apps​

Get all notifications with pagination and filtering.

Request​

Endpoint: GET baseUrl/notification/api/in-apps

Headers:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Query Parameters:

{
page?: number; // Page number (default: 1, min: 1)
limit?: number; // Items per page (default: 20, min: 1, max: 100)
memberId?: string; // Filter by member ID (UUID)
read?: "true" | "false" | "all"; // Filter by read status
format?: "raw" | "html"; // Filter by format
type?: "promotion" | "points" | "tier" | "news" | "badge"; // Filter by type
locale?: "en" | "th" | "cn"; // Localize response content
search?: string; // Search in title/content
dateFrom?: string; // Start date (ISO 8601 or date string)
dateTo?: string; // End date (ISO 8601 or date string)
dateField?: "createdAt" | "updatedAt"; // Date field to filter by (default: "createdAt")
}

Example Request​

GET baseUrl/notification/api/in-apps?page=1&limit=20&read=false&type=points&locale=en

Response​

Success Response (200 OK):

{
"statusCode": 200,
"data": {
"message": "Notifications retrieved successfully",
"data": {
"meta": {
"page": 1,
"limit": 20,
"total": 45,
"totalPages": 3
},
"data": [
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"title_en": "🎉 150 Points Received",
"title_th": "🎉 ได้รับ 150 คะแนน",
"title_cn": "🎉 获得150积分",
"content_en": "You have received 150 points from purchase!",
"format": "raw",
"type": "points",
"pointSubtype": "point_received",
"memberId": "550e8400-e29b-41d4-a716-446655440000",
"readAt": null,
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-15T10:30:00.000Z"
}
]
}
}
}

GET baseUrl/notification/api/in-apps/:id​

Get a specific notification by ID.

Request​

Endpoint: GET baseUrl/notification/api/in-apps/:id

Headers:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Path Parameters:

{
id: string; // UUID of the notification
}

Query Parameters:

{
locale?: "en" | "th" | "cn"; // Localize response content
}

Example Request​

GET baseUrl/notification/api/in-apps/a1b2c3d4-e5f6-7890-abcd-ef1234567890?locale=en

Response​

Success Response (200 OK):

{
"statusCode": 200,
"data": {
"message": "Notification retrieved successfully",
"data": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"title_en": "🎉 150 Points Received",
"title_th": "🎉 ได้รับ 150 คะแนน",
"title_cn": "🎉 获得150积分",
"content_en": "You have received 150 points from purchase!",
"content_th": "คุณได้รับ 150 คะแนนจากการซื้อ!",
"content_cn": "您已从购买获得 150 积分!",
"format": "raw",
"type": "points",
"pointSubtype": "point_received",
"metadata": {
"points": 150,
"source": "purchase",
"transactionId": "txn_1234567890"
},
"memberId": "550e8400-e29b-41d4-a716-446655440000",
"readAt": null,
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-15T10:30:00.000Z"
}
}
}

Error Response (404 Not Found):

{
"statusCode": 404,
"data": {
"message": "Notification not found"
}
}

GET baseUrl/notification/api/in-apps/me​

Get all notifications for the authenticated member.

Request​

Endpoint: GET baseUrl/notification/api/in-apps/me

Headers:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Query Parameters: Same as GET baseUrl/notification/api/in-apps (excluding memberId as it's automatically set from the token)

Example Request​

GET baseUrl/notification/api/in-apps/me?read=false&type=points&locale=en&page=1&limit=20

Response​

Success Response (200 OK):

Same structure as GET baseUrl/notification/api/in-apps


GET baseUrl/notification/api/in-apps/me/recent​

Get recent notifications for the authenticated member.

Request​

Endpoint: GET baseUrl/notification/api/in-apps/me/recent

Headers:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Query Parameters:

{
limit?: number; // Number of notifications (default: 10, min: 1, max: 100)
locale?: "en" | "th" | "cn"; // Localize response content
}

Example Request​

GET baseUrl/notification/api/in-apps/me/recent?limit=5&locale=en

Response​

Success Response (200 OK):

{
"statusCode": 200,
"data": {
"message": "Recent notifications retrieved successfully",
"data": [
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"title_en": "🎉 150 Points Received",
"content_en": "You have received 150 points from purchase!",
"type": "points",
"readAt": null,
"createdAt": "2024-01-15T10:30:00.000Z"
}
]
}
}

GET baseUrl/notification/api/in-apps/me/unread-count​

Get the unread notification count for the authenticated member.

Request​

Endpoint: GET baseUrl/notification/api/in-apps/me/unread-count

Headers:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Example Request​

GET baseUrl/notification/api/in-apps/me/unread-count

Response​

Success Response (200 OK):

{
"statusCode": 200,
"data": {
"message": "Unread count retrieved successfully",
"data": {
"unreadCount": 5
}
}
}

GET baseUrl/notification/api/in-apps/me/events​

Server-Sent Events (SSE) stream for real-time notifications.

Request​

Endpoint: GET baseUrl/notification/api/in-apps/me/events

Headers:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Query Parameters:

{
locale?: "en" | "th" | "cn"; // Localize notification content
}

Example Request​

GET baseUrl/notification/api/in-apps/me/events?locale=en

Response​

Stream Response (200 OK - text/event-stream):

The connection remains open and streams events:

data: {"type":"connected","message":"SSE connection established","timestamp":"2024-01-15T10:30:00.000Z","locale":"en"}

data: {"id":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","title_en":"🎉 150 Points Received","type":"new_notification",...}

data: {"type":"heartbeat","timestamp":"2024-01-15T10:30:30.000Z","locale":"en"}

Event Types:

  • connected - Initial connection message
  • new_notification - New notification received
  • notification_read - Notification marked as read
  • heartbeat - Periodic heartbeat (every 30 seconds)

PUT baseUrl/notification/api/in-apps/:id​

Update a notification.

Request​

Endpoint: PUT baseUrl/notification/api/in-apps/:id

Headers:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json

Path Parameters:

{
id: string; // UUID of the notification
}

Request Body: All fields optional (same structure as POST baseUrl/notification/api/in-apps)

Example Request​

{
"title_en": "Updated Title",
"content_en": "Updated content"
}

Response​

Success Response (200 OK):

{
"statusCode": 200,
"data": {
"message": "Notification updated successfully",
"data": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"title_en": "Updated Title",
"content_en": "Updated content",
"updatedAt": "2024-01-20T14:30:00.000Z"
}
}
}

PATCH baseUrl/notification/api/in-apps/:id/read​

Mark a notification as read or unread.

Request​

Endpoint: PATCH baseUrl/notification/api/in-apps/:id/read

Headers:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json

Path Parameters:

{
id: string; // UUID of the notification
}

Request Body:

{
read: boolean; // true to mark as read, false to mark as unread
}

Example Request​

{
"read": true
}

Response​

Success Response (200 OK):

{
"statusCode": 200,
"data": {
"message": "Notification status updated successfully",
"data": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"readAt": "2024-01-20T14:30:00.000Z",
"updatedAt": "2024-01-20T14:30:00.000Z"
}
}
}

PATCH baseUrl/notification/api/in-apps/me/read-all​

Mark all notifications as read for the authenticated member.

Request​

Endpoint: PATCH baseUrl/notification/api/in-apps/me/read-all

Headers:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Example Request​

PATCH baseUrl/notification/api/in-apps/me/read-all

Response​

Success Response (200 OK):

{
"statusCode": 200,
"data": {
"message": "All notifications marked as read successfully",
"data": {
"updatedCount": 5
}
}
}

DELETE baseUrl/notification/api/in-apps/:id​

Delete a notification.

Request​

Endpoint: DELETE baseUrl/notification/api/in-apps/:id

Headers:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Path Parameters:

{
id: string; // UUID of the notification
}

Example Request​

DELETE baseUrl/notification/api/in-apps/a1b2c3d4-e5f6-7890-abcd-ef1234567890

Response​

Success Response (200 OK):

{
"statusCode": 200,
"data": {
"message": "Notification deleted successfully"
}
}

Specialized Notification Endpoints​

The following endpoints provide convenient helpers for creating specific notification types:

POST baseUrl/notification/api/in-apps/points/received​

Create a point received notification with automatic content generation.

Request​

Endpoint: POST baseUrl/notification/api/in-apps/points/received

Headers:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json

Request Body:

{
memberId: string; // Required, UUID
points: number; // Required, must be positive
source?: string; // Optional, e.g., "purchase", "bonus"
transactionId?: string; // Optional
receiptImageUrl?: string; // Optional, must be valid URL
}

Example Request​

{
"memberId": "550e8400-e29b-41d4-a716-446655440000",
"points": 150,
"source": "purchase",
"transactionId": "txn_1234567890",
"receiptImageUrl": "https://storage.example.com/receipts/txn_1234567890.jpg"
}

Response​

Success Response (201 Created):

{
"statusCode": 201,
"data": {
"message": "Point received notification created successfully",
"data": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"title_en": "🎉 150 Points Received",
"type": "points",
"pointSubtype": "point_received",
"memberId": "550e8400-e29b-41d4-a716-446655440000",
"createdAt": "2024-01-15T10:30:00.000Z"
}
}
}

Similar specialized endpoints exist for:

  • POST baseUrl/notification/api/in-apps/points/receipt-error - Create receipt error notification
  • POST baseUrl/notification/api/in-apps/points/receipt-processing - Create receipt processing notification
  • POST baseUrl/notification/api/in-apps/points/nearly-expire - Create point nearly expire notification
  • POST baseUrl/notification/api/in-apps/points/expired - Create point expired notification
  • POST baseUrl/notification/api/in-apps/tier/upgraded-enhanced - Create tier upgrade notification
  • POST baseUrl/notification/api/in-apps/tier/adjusted - Create tier adjusted notification
  • POST baseUrl/notification/api/in-apps/tier/renewal-reminder - Create tier renewal reminder notification
  • POST baseUrl/notification/api/in-apps/badge/unlocked - Create badge unlocked notification
  • POST baseUrl/notification/api/in-apps/badge/progress - Create badge progress notification
  • POST baseUrl/notification/api/in-apps/promotion/create - Create promotion notification
  • POST baseUrl/notification/api/in-apps/news/create - Create news notification

All follow similar request/response patterns with type-specific body schemas.


Mail Template API​

GET baseUrl/notification/api/mail-templates​

Get all mail templates with pagination and filtering.

Request​

Endpoint: GET baseUrl/notification/api/mail-templates

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)
event?: string; // Filter by event name
version?: string; // Filter by version
search?: string; // Search in name/event
}

Example Request​

GET baseUrl/notification/api/mail-templates?page=1&limit=20&event=welcome

Response​

Success Response (200 OK):

{
"statusCode": 200,
"data": {
"meta": {
"page": 1,
"limit": 20,
"total": 10,
"totalPages": 1
},
"data": [
{
"id": "template-id-123",
"name": "Welcome Email",
"version": "1.0.0",
"event": "welcome",
"subject_en": "Welcome to Happy Mall!",
"subject_th": "ยินดีต้อนรับสู่ Happy Mall!",
"subject_cn": "欢迎来到 Happy Mall!",
"contentType": "html",
"defaultSenderId": "sender-id-123",
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-15T10:30:00.000Z"
}
]
}
}

GET baseUrl/notification/api/mail-templates/:id​

Get a specific mail template by ID.

Request​

Endpoint: GET baseUrl/notification/api/mail-templates/:id

Headers:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Path Parameters:

{
id: string; // UUID of the mail template
}

Example Request​

GET baseUrl/notification/api/mail-templates/template-id-123

Response​

Success Response (200 OK):

{
"statusCode": 200,
"data": {
"id": "template-id-123",
"name": "Welcome Email",
"version": "1.0.0",
"event": "welcome",
"subject_en": "Welcome to Happy Mall!",
"subject_th": "ยินดีต้อนรับสู่ Happy Mall!",
"subject_cn": "欢迎来到 Happy Mall!",
"html_en": "<html><body><h1>Welcome!</h1><p>Thank you for joining us.</p></body></html>",
"html_th": "<html><body><h1>ยินดีต้อนรับ!</h1><p>ขอบคุณที่เข้าร่วมกับเรา</p></body></html>",
"html_cn": "<html><body><h1>欢迎!</h1><p>感谢您的加入</p></body></html>",
"text_en": "Welcome! Thank you for joining us.",
"text_th": "ยินดีต้อนรับ! ขอบคุณที่เข้าร่วมกับเรา",
"text_cn": "欢迎!感谢您的加入",
"contentType": "html",
"defaultSenderId": "sender-id-123",
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-15T10:30:00.000Z"
}
}

GET baseUrl/notification/api/mail-templates/:id/content​

Get localized content for a mail template.

Request​

Endpoint: GET baseUrl/notification/api/mail-templates/:id/content

Headers:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Path Parameters:

{
id: string; // UUID of the mail template
}

Query Parameters:

{
locale?: string; // Optional locale (e.g., "en", "th", "cn")
}

Example Request​

GET baseUrl/notification/api/mail-templates/template-id-123/content?locale=en

Response​

Success Response (200 OK):

{
"statusCode": 200,
"data": {
"subject": "Welcome to Happy Mall!",
"html": "<html><body><h1>Welcome!</h1><p>Thank you for joining us.</p></body></html>",
"text": "Welcome! Thank you for joining us."
}
}

POST baseUrl/notification/api/mail-templates​

Create a new mail template.

Request​

Endpoint: POST baseUrl/notification/api/mail-templates

Headers:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json

Request Body:

{
name: string; // Required, max 100 characters
version: string; // Required, max 20 characters
event: string; // Required, max 100 characters (event name)
subject_en?: string; // Optional
html_en?: string; // Optional
text_en?: string; // Optional
subject_th?: string; // Optional
html_th?: string; // Optional
text_th?: string; // Optional
subject_cn?: string; // Optional
html_cn?: string; // Optional
text_cn?: string; // Optional
contentType?: "html" | "text"; // Optional, default: "html"
defaultSenderId?: string; // Optional, UUID of default sender
}

Example Request​

{
"name": "Welcome Email",
"version": "1.0.0",
"event": "welcome",
"subject_en": "Welcome to Happy Mall!",
"subject_th": "ยินดีต้อนรับสู่ Happy Mall!",
"subject_cn": "欢迎来到 Happy Mall!",
"html_en": "<html><body><h1>Welcome!</h1><p>Thank you for joining us.</p></body></html>",
"html_th": "<html><body><h1>ยินดีต้อนรับ!</h1><p>ขอบคุณที่เข้าร่วมกับเรา</p></body></html>",
"html_cn": "<html><body><h1>欢迎!</h1><p>感谢您的加入</p></body></html>",
"text_en": "Welcome! Thank you for joining us.",
"contentType": "html",
"defaultSenderId": "sender-id-123"
}

Response​

Success Response (201 Created):

{
"statusCode": 201,
"data": {
"id": "template-id-123",
"name": "Welcome Email",
"version": "1.0.0",
"event": "welcome",
"createdAt": "2024-01-15T10:30:00.000Z"
}
}

PUT baseUrl/notification/api/mail-templates/:id​

Update an existing mail template.

Request​

Endpoint: PUT baseUrl/notification/api/mail-templates/:id

Headers:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json

Path Parameters:

{
id: string; // UUID of the mail template
}

Request Body: All fields optional (same structure as POST)

Example Request​

{
"subject_en": "Updated Welcome Subject",
"html_en": "<html><body><h1>Updated Welcome!</h1></body></html>"
}

Response​

Success Response (200 OK):

{
"statusCode": 200,
"data": {
"id": "template-id-123",
"name": "Welcome Email",
"subject_en": "Updated Welcome Subject",
"updatedAt": "2024-01-20T14:30:00.000Z"
}
}

DELETE baseUrl/notification/api/mail-templates/:id​

Delete a mail template.

Request​

Endpoint: DELETE baseUrl/notification/api/mail-templates/:id

Headers:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Path Parameters:

{
id: string; // UUID of the mail template
}

Example Request​

DELETE baseUrl/notification/api/mail-templates/template-id-123

Response​

Success Response (204 No Content):

No response body.


Mail Sender API​

GET baseUrl/notification/api/mail-senders​

Get all mail senders with pagination and filtering.

Request​

Endpoint: GET baseUrl/notification/api/mail-senders

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 name/email
}

Example Request​

GET baseUrl/notification/api/mail-senders?page=1&limit=20&search=no-reply

Response​

Success Response (200 OK):

{
"statusCode": 200,
"data": {
"meta": {
"page": 1,
"limit": 20,
"total": 5,
"totalPages": 1
},
"data": [
{
"id": "sender-id-123",
"name": "No Reply",
"mailAddress": "noreply@example.com",
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-15T10:30:00.000Z"
}
]
}
}

GET baseUrl/notification/api/mail-senders/:id​

Get a specific mail sender by ID.

Request​

Endpoint: GET baseUrl/notification/api/mail-senders/:id

Headers:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Path Parameters:

{
id: string; // UUID of the mail sender
}

Example Request​

GET baseUrl/notification/api/mail-senders/sender-id-123

Response​

Success Response (200 OK):

{
"statusCode": 200,
"data": {
"id": "sender-id-123",
"name": "No Reply",
"mailAddress": "noreply@example.com",
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-15T10:30:00.000Z"
}
}

POST baseUrl/notification/api/mail-senders​

Create a new mail sender.

Request​

Endpoint: POST baseUrl/notification/api/mail-senders

Headers:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json

Request Body:

{
name: string; // Required, max 100 characters
mailAddress: string; // Required, valid email format, max 256 characters
}

Example Request​

{
"name": "Support",
"mailAddress": "support@example.com"
}

Response​

Success Response (201 Created):

{
"statusCode": 201,
"data": {
"id": "sender-id-124",
"name": "Support",
"mailAddress": "support@example.com",
"createdAt": "2024-01-15T10:30:00.000Z"
}
}

PUT baseUrl/notification/api/mail-senders/:id​

Update an existing mail sender.

Request​

Endpoint: PUT baseUrl/notification/api/mail-senders/:id

Headers:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json

Path Parameters:

{
id: string; // UUID of the mail sender
}

Request Body:

{
name?: string; // Optional, max 100 characters
mailAddress?: string; // Optional, valid email format, max 256 characters
}

Example Request​

{
"name": "Support Team",
"mailAddress": "support-team@example.com"
}

Response​

Success Response (200 OK):

{
"statusCode": 200,
"data": {
"id": "sender-id-123",
"name": "Support Team",
"mailAddress": "support-team@example.com",
"updatedAt": "2024-01-20T14:30:00.000Z"
}
}

DELETE baseUrl/notification/api/mail-senders/:id​

Delete a mail sender.

Request​

Endpoint: DELETE baseUrl/notification/api/mail-senders/:id

Headers:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Path Parameters:

{
id: string; // UUID of the mail sender
}

Example Request​

DELETE baseUrl/notification/api/mail-senders/sender-id-123

Response​

Success Response (204 No Content):

No response body.


Mail Action API​

GET baseUrl/notification/api/mail-actions​

Get all mail actions (sent emails) with filtering.

Request​

Endpoint: GET baseUrl/notification/api/mail-actions

Headers:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Query Parameters: Various filtering options (page, limit, status, recipientEmail, etc.)

Example Request​

GET baseUrl/notification/api/mail-actions?page=1&limit=20

Response​

Success Response (200 OK):

{
"statusCode": 200,
"data": {
"meta": {
"page": 1,
"limit": 20,
"total": 100,
"totalPages": 5
},
"data": [
{
"id": "mail-action-id-123",
"recipientEmail": "user@example.com",
"memberId": "550e8400-e29b-41d4-a716-446655440000",
"templateId": "template-id-123",
"status": "sent",
"sentAt": "2024-01-15T10:30:00.000Z",
"createdAt": "2024-01-15T10:30:00.000Z"
}
]
}
}

GET baseUrl/notification/api/mail-actions/:id​

Get a specific mail action by ID.

Request​

Endpoint: GET baseUrl/notification/api/mail-actions/:id

Headers:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Path Parameters:

{
id: string; // UUID of the mail action
}

Example Request​

GET baseUrl/notification/api/mail-actions/mail-action-id-123

Response​

Success Response (200 OK):

{
"statusCode": 200,
"data": {
"id": "mail-action-id-123",
"recipientEmail": "user@example.com",
"memberId": "550e8400-e29b-41d4-a716-446655440000",
"templateId": "template-id-123",
"templateVersion": "1.0.0",
"senderId": "sender-id-123",
"status": "sent",
"sentAt": "2024-01-15T10:30:00.000Z",
"createdAt": "2024-01-15T10:30:00.000Z"
}
}

POST baseUrl/notification/api/mail-actions​

Send an email using a template.

Request​

Endpoint: POST baseUrl/notification/api/mail-actions

Headers:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json

Request Body:

{
recipientEmail: string; // Required, valid email format, max 256 characters
memberId?: string; // Optional, UUID
locale?: string; // Optional, max 10 characters
templateData?: Record<string, any>; // Optional, dynamic data for template variables
templateId: string; // Required, UUID
version?: string; // Optional, max 50 characters
senderId?: string; // Optional, UUID
}

Example Request​

{
"recipientEmail": "user@example.com",
"memberId": "550e8400-e29b-41d4-a716-446655440000",
"locale": "en",
"templateId": "template-id-123",
"templateData": {
"memberName": "John Doe",
"points": 150
},
"senderId": "sender-id-123"
}

Response​

Success Response (200 OK):

{
"statusCode": 200,
"data": {
"messageId": "msg-abc123",
"mailNotifyId": "mail-action-id-123"
}
}

Error Response (400 Bad Request):

{
"statusCode": 400,
"data": {
"message": "Failed to send mail"
}
}

POST baseUrl/notification/api/mail-actions/resend/:id​

Resend a previously sent email.

Request​

Endpoint: POST baseUrl/notification/api/mail-actions/resend/:id

Headers:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Path Parameters:

{
id: string; // UUID of the mail action to resend
}

Example Request​

POST baseUrl/notification/api/mail-actions/resend/mail-action-id-123

Response​

Success Response (200 OK):

{
"statusCode": 200,
"data": {
"messageId": "msg-abc456",
"mailNotifyId": "mail-action-id-123"
}
}

POST baseUrl/notification/api/mail-actions/bulk​

Send bulk emails using a template.

Request​

Endpoint: POST baseUrl/notification/api/mail-actions/bulk

Headers:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json

Request Body:

{
templateId: string; // Required, UUID
recipients: Array<{ // Required
email: string; // Required
memberId?: string; // Optional, UUID
templateData?: Record<string, any>; // Optional
}>;
language?: string; // Optional
dynamicData?: Record<string, any>; // Optional, shared dynamic data
senderId?: string; // Optional, UUID
batchSize?: number; // Optional, batch size for processing
skipWhitelistCheck?: boolean; // Optional, skip whitelist validation
}

Example Request​

{
"templateId": "template-id-123",
"recipients": [
{
"email": "user1@example.com",
"memberId": "550e8400-e29b-41d4-a716-446655440000",
"templateData": {
"memberName": "John Doe"
}
},
{
"email": "user2@example.com",
"memberId": "660e8400-e29b-41d4-a716-446655440001",
"templateData": {
"memberName": "Jane Smith"
}
}
],
"language": "en",
"senderId": "sender-id-123",
"batchSize": 100
}

Response​

Success Response (200 OK):

{
"statusCode": 200,
"data": {
"total": 2,
"success": 2,
"failed": 0,
"results": [
{
"email": "user1@example.com",
"status": "sent",
"messageId": "msg-abc123"
},
{
"email": "user2@example.com",
"status": "sent",
"messageId": "msg-abc124"
}
]
}
}

POST baseUrl/notification/api/mail-actions/preview​

Preview an email without sending it.

Request​

Endpoint: POST baseUrl/notification/api/mail-actions/preview

Headers:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json

Request Body:

{
templateId: string; // Required, UUID
language?: string; // Optional
dynamicData?: Record<string, any>; // Optional, template variables
senderId?: string; // Optional, UUID
}

Example Request​

{
"templateId": "template-id-123",
"language": "en",
"dynamicData": {
"memberName": "John Doe",
"points": 150
},
"senderId": "sender-id-123"
}

Response​

Success Response (200 OK):

{
"statusCode": 200,
"data": {
"subject": "Welcome to Happy Mall!",
"html": "<html><body><h1>Welcome John Doe!</h1><p>You have 150 points.</p></body></html>",
"text": "Welcome John Doe! You have 150 points.",
"from": "noreply@example.com"
}
}

POST baseUrl/notification/api/mail-actions/send-to-all-subscribers​

Send email to all subscribers using a template.

Request​

Endpoint: POST baseUrl/notification/api/mail-actions/send-to-all-subscribers

Headers:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json

Request Body:

{
templateId: string; // Required, UUID
language?: string; // Optional
dynamicData?: Record<string, any>; // Optional, shared dynamic data
batchSize?: number; // Optional, batch size for processing
}

Example Request​

{
"templateId": "template-id-123",
"language": "en",
"dynamicData": {
"promotion": "Summer Sale 2024"
},
"batchSize": 1000
}

Response​

Success Response (200 OK):

{
"statusCode": 200,
"data": {
"totalSubscribers": 5000,
"queued": 5000,
"batchSize": 1000,
"estimatedTime": "5 minutes"
}
}

POST baseUrl/notification/api/mail-actions/validate-emails​

Validate emails against whitelist.

Request​

Endpoint: POST baseUrl/notification/api/mail-actions/validate-emails

Headers:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json

Request Body:

{
emails: string[]; // Required, array of email addresses
}

Example Request​

{
"emails": [
"user1@example.com",
"user2@example.com",
"invalid-email"
]
}

Response​

Success Response (200 OK):

{
"statusCode": 200,
"data": {
"allowed": [
"user1@example.com",
"user2@example.com"
],
"blocked": [
"invalid-email"
]
}
}

Data Types & Enums​

Notification Types​

  • promotion - Promotion notifications
  • points - Point-related notifications
  • tier - Tier-related notifications
  • news - News/announcement notifications
  • badge - Badge-related notifications

Point Subtypes​

  • point_received - Points received
  • receipt_error - Receipt verification error
  • receipt_processing - Receipt being processed
  • point_nearly_expire - Points about to expire
  • point_expired - Points expired

Tier Subtypes​

  • tier_upgraded - Tier upgraded
  • tier_adjusted - Tier adjusted
  • tier_renewal_reminder - Tier renewal reminder

Content Format​

  • raw - Plain text content
  • html - HTML formatted content

Content Type (Mail)​

  • html - HTML email
  • text - Plain text email

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
  • 204 No Content - Request succeeded with no content
  • 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: All endpoints require a valid JWT token in the Authorization header. The token's sub field should contain the member/user ID.

  2. Server-Sent Events (SSE): The /in-apps/me/events endpoint maintains a persistent connection and streams notifications in real-time. The connection sends heartbeat messages every 30 seconds to keep the connection alive.

  3. Localization: Use the locale query parameter to get localized content. For notifications, if not specified, all language fields will be returned.

  4. Mail Templates:

    • Templates support both HTML and text formats
    • Dynamic data can be injected into templates using template variables
    • Templates are versioned for change tracking
  5. Mail Sending:

    • Supports single email, bulk emails, and broadcast to all subscribers
    • Email validation against whitelist is available
    • Preview endpoint allows testing templates before sending
    • Failed emails can be resent using the resend endpoint
  6. Date Formats: All date fields use ISO 8601 format (e.g., 2024-12-31T23:59:59.000Z).

  7. Pagination: Pagination defaults to page 1 with 10-20 items per page (varies by endpoint). Maximum limit is typically 100 items per page.


Support​

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