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.