| name | sms-adapters |
| description | Taiwan SMS integration (台灣簡訊整合). Use when working with Every8D (每日簡訊 / 互動資通) or other Taiwan SMS service providers. Covers single SMS (單則簡訊), batch messaging (批次發送), multi-target messaging (多目標發送), Taiwan mobile number validation (台灣手機號碼驗證), delivery tracking (發送追蹤), and NestJS integration. Keywords - SMS, 簡訊, Every8D, 每日簡訊, 互動資通, Interactive Communications, mobile number, 手機號碼, batch send, 批次發送, message delivery, 訊息發送 |
Taiwan SMS Adapters
This skill provides comprehensive guidance for using @rytass/sms-adapter-* packages to integrate Taiwan SMS service providers.
Overview
All adapters implement the SMSService interface from @rytass/sms, providing a unified API across different providers:
| Package | Provider | Description |
|---|
@rytass/sms-adapter-every8d | Every8D (每日簡訊 / 互動資通) | Taiwan SMS gateway by Interactive Communications |
Base Interface (@rytass/sms)
All adapters share these core concepts:
SMSService - Main interface for SMS operations:
interface SMSService<
Request extends SMSRequest,
SendResponse extends SMSSendResponse,
MultiTarget extends MultiTargetRequest,
> {
send(request: Request[]): Promise<SendResponse[]>;
send(request: Request): Promise<SendResponse>;
send(request: MultiTarget): Promise<SendResponse[]>;
}
Request Types:
SMSRequest - Single SMS message to one recipient
MultiTargetRequest - Same message to multiple recipients
Response Status:
SUCCESS - Message sent successfully
FAILED - Message delivery failed
Taiwan Phone Number Helpers (from @rytass/sms):
const TAIWAN_PHONE_NUMBER_RE = /^(0|\+?886-?)9\d{2}-?\d{3}-?\d{3}$/;
function normalizedTaiwanMobilePhoneNumber(mobile: string): string;
Installation
npm install @rytass/sms
npm install @rytass/sms-adapter-every8d
Quick Start
Every8D (每日簡訊 / 互動資通)
import { SMSServiceEvery8D } from '@rytass/sms-adapter-every8d';
const smsService = new SMSServiceEvery8D({
username: process.env.EVERY8D_USERNAME!,
password: process.env.EVERY8D_PASSWORD!,
baseUrl: 'https://api.e8d.tw',
onlyTaiwanMobileNumber: true,
});
const result = await smsService.send({
mobile: '0987654321',
text: 'Hello! This is a test message.',
});
console.log('Message ID:', result.messageId);
console.log('Status:', result.status);
console.log('Mobile:', result.mobile);
Common Usage Patterns
Single SMS Delivery
Send a message to a single recipient:
const result = await smsService.send({
mobile: '0987654321',
text: 'Your verification code is 123456',
});
if (result.status === SMSRequestResult.SUCCESS) {
console.log('SMS sent successfully!');
console.log('Message ID:', result.messageId);
console.log('Recipient:', result.mobile);
} else {
console.error('SMS delivery failed');
console.error('Error Code:', result.errorCode);
console.error('Error Message:', result.errorMessage);
}
Batch Messaging (Same Message to Multiple Recipients)
Send the same message to multiple recipients efficiently:
const results = await smsService.send({
mobileList: [
'0987654321',
'0912345678',
'0923456789',
'0934567890'
],
text: 'Important notification: Your order has been shipped!',
});
console.log(`Total sent: ${results.length}`);
const successful = results.filter(r => r.status === SMSRequestResult.SUCCESS);
const failed = results.filter(r => r.status === SMSRequestResult.FAILED);
console.log(`Successful: ${successful.length}`);
console.log(`Failed: ${failed.length}`);
failed.forEach(result => {
console.error(`Failed to send to ${result.mobile}:`, result.errorMessage);
});
Multi-Target Messaging (Different Messages)
Send different messages to different recipients:
const results = await smsService.send([
{
mobile: '0987654321',
text: 'Dear John, your appointment is confirmed for tomorrow at 10 AM.',
},
{
mobile: '0912345678',
text: 'Hi Mary, your package will arrive today between 2-4 PM.',
},
{
mobile: '0923456789',
text: 'Hello Mike, thank you for your purchase! Your receipt is attached.',
},
]);
results.forEach((result, index) => {
console.log(`Message ${index + 1} to ${result.mobile}: ${result.status}`);
if (result.status === SMSRequestResult.SUCCESS) {
console.log(` Message ID: ${result.messageId}`);
} else {
console.error(` Error: ${result.errorMessage}`);
}
});
Taiwan Mobile Number Handling
Automatic Number Normalization
The adapter automatically normalizes Taiwan mobile numbers:
const validFormats = [
'0987654321',
'0987-654-321',
'+886987654321',
'886987654321',
'+886-987654321',
];
for (const number of validFormats) {
const result = await smsService.send({
mobile: number,
text: 'Test message',
});
console.log('Normalized number:', result.mobile);
}
Number Validation
Validate Taiwan mobile numbers with strict mode:
const strictService = new SMSServiceEvery8D({
username: process.env.EVERY8D_USERNAME!,
password: process.env.EVERY8D_PASSWORD!,
onlyTaiwanMobileNumber: true,
});
try {
await strictService.send({
mobile: '0987654321',
text: 'Valid Taiwan number',
});
await strictService.send({
mobile: '+1234567890',
text: 'Invalid for Taiwan-only mode',
});
} catch (error) {
console.error('Number validation error:', error.message);
}
International Numbers
Send to international numbers when validation is disabled:
const globalService = new SMSServiceEvery8D({
username: process.env.EVERY8D_USERNAME!,
password: process.env.EVERY8D_PASSWORD!,
onlyTaiwanMobileNumber: false,
});
const results = await globalService.send([
{ mobile: '0987654321', text: 'Taiwan message' },
{ mobile: '+85298765432', text: 'Hong Kong message' },
{ mobile: '+60123456789', text: 'Malaysia message' },
{ mobile: '+6591234567', text: 'Singapore message' },
]);
Integration Examples
E-commerce Order Notifications
import { SMSServiceEvery8D } from '@rytass/sms-adapter-every8d';
import { SMSRequestResult } from '@rytass/sms';
class OrderNotificationService {
private smsService: SMSServiceEvery8D;
constructor() {
this.smsService = new SMSServiceEvery8D({
username: process.env.EVERY8D_USERNAME!,
password: process.env.EVERY8D_PASSWORD!,
onlyTaiwanMobileNumber: true,
});
}
async sendOrderConfirmation(order: {
id: string;
customerPhone: string;
total: number;
deliveryDate: string;
trackingUrl: string;
}): Promise<boolean> {
const message = `
訂單已確認!
訂單編號:${order.id}
金額:NT$${order.total}
預計送達:${order.deliveryDate}
追蹤連結:${order.trackingUrl}
`.trim();
const result = await this.smsService.send({
mobile: order.customerPhone,
text: message,
});
return result.status === SMSRequestResult.SUCCESS;
}
async sendShippingNotification(order: {
id: string;
customerPhone: string;
trackingNumber: string;
}): Promise<boolean> {
const message = `您的訂單 #${order.id} 已出貨!追蹤號碼:${order.trackingNumber}`;
const result = await this.smsService.send({
mobile: order.customerPhone,
text: message,
});
if (result.status === SMSRequestResult.FAILED) {
console.error('Failed to send shipping notification:', {
orderId: order.id,
errorCode: result.errorCode,
errorMessage: result.errorMessage,
});
}
return result.status === SMSRequestResult.SUCCESS;
}
async sendBulkDeliveryNotifications(orders: Array<{
id: string;
customerPhone: string;
}>): Promise<{
total: number;
successful: number;
failed: number;
}> {
const results = await this.smsService.send(
orders.map(order => ({
mobile: order.customerPhone,
text: `您的訂單 #${order.id} 將於今日送達,請留意收件。`,
}))
);
const successful = results.filter(r => r.status === SMSRequestResult.SUCCESS).length;
const failed = results.filter(r => r.status === SMSRequestResult.FAILED).length;
return {
total: results.length,
successful,
failed,
};
}
}
Authentication & Verification
import { SMSServiceEvery8D } from '@rytass/sms-adapter-every8d';
import { SMSRequestResult } from '@rytass/sms';
class AuthSMSService {
private smsService: SMSServiceEvery8D;
constructor() {
this.smsService = new SMSServiceEvery8D({
username: process.env.EVERY8D_USERNAME!,
password: process.env.EVERY8D_PASSWORD!,
onlyTaiwanMobileNumber: true,
});
}
async sendVerificationCode(phoneNumber: string): Promise<string> {
const code = Math.random().toString().slice(-6);
const message = `您的驗證碼是 ${code},10 分鐘內有效。請勿將此驗證碼告知他人。`;
const result = await this.smsService.send({
mobile: phoneNumber,
text: message,
});
if (result.status === SMSRequestResult.SUCCESS) {
await this.storeVerificationCode(phoneNumber, code, 600);
console.log('Verification code sent:', result.messageId);
return code;
} else {
throw new Error(`SMS delivery failed: ${result.errorMessage}`);
}
}
async send2FACode(
phoneNumber: string,
serviceName: string
): Promise<void> {
const code = Math.random().toString().slice(-6);
const message = `${serviceName} 登入驗證碼:${code},5 分鐘內有效。`;
const result = await this.smsService.send({
mobile: phoneNumber,
text: message,
});
if (result.status === SMSRequestResult.SUCCESS) {
await this.store2FACode(phoneNumber, code, 300);
} else {
throw new Error(`Failed to send 2FA code: ${result.errorMessage}`);
}
}
async sendPasswordResetLink(
phoneNumber: string,
resetLink: string
): Promise<boolean> {
const message = `密碼重設連結:${resetLink}。此連結將在 30 分鐘後失效。`;
const result = await this.smsService.send({
mobile: phoneNumber,
text: message,
});
return result.status === SMSRequestResult.SUCCESS;
}
private async storeVerificationCode(
phone: string,
code: string,
expirationSeconds: number
): Promise<void> {
}
private async store2FACode(
phone: string,
code: string,
expirationSeconds: number
): Promise<void> {
}
}
Marketing Campaign Service
import { SMSServiceEvery8D } from '@rytass/sms-adapter-every8d';
import { SMSRequestResult } from '@rytass/sms';
class MarketingCampaignService {
private smsService: SMSServiceEvery8D;
constructor() {
this.smsService = new SMSServiceEvery8D({
username: process.env.EVERY8D_USERNAME!,
password: process.env.EVERY8D_PASSWORD!,
onlyTaiwanMobileNumber: true,
});
}
async sendPromotionalCampaign(
customers: Array<{ phoneNumber: string; name: string }>,
campaignMessage: string
): Promise<{
total: number;
successful: number;
failed: number;
successRate: number;
}> {
const batchSize = 100;
const batches: Array<typeof customers> = [];
for (let i = 0; i < customers.length; i += batchSize) {
batches.push(customers.slice(i, i + batchSize));
}
const allResults = [];
for (const batch of batches) {
const phoneNumbers = batch.map(customer => customer.phoneNumber);
try {
const batchResult = await this.smsService.send({
mobileList: phoneNumbers,
text: campaignMessage,
});
allResults.push(...batchResult);
await this.delay(1000);
} catch (error) {
console.error('Batch failed:', error);
phoneNumbers.forEach(phone => {
allResults.push({
mobile: phone,
status: SMSRequestResult.FAILED,
errorMessage: 'Batch processing error',
});
});
}
}
return this.analyzeCampaignResults(allResults);
}
async sendPersonalizedCampaign(
customers: Array<{
phoneNumber: string;
name: string;
customMessage: string;
}>
): Promise<{
total: number;
successful: number;
failed: number;
successRate: number;
}> {
const results = await this.smsService.send(
customers.map(customer => ({
mobile: customer.phoneNumber,
text: customer.customMessage,
}))
);
return this.analyzeCampaignResults(results);
}
private delay(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
private analyzeCampaignResults(
results: Array<{ status: SMSRequestResult; mobile: string }>
): {
total: number;
successful: number;
failed: number;
successRate: number;
} {
const successful = results.filter(
r => r.status === SMSRequestResult.SUCCESS
).length;
const failed = results.filter(
r => r.status === SMSRequestResult.FAILED
).length;
return {
total: results.length,
successful,
failed,
successRate: (successful / results.length) * 100,
};
}
}
Appointment Reminders
import { SMSServiceEvery8D } from '@rytass/sms-adapter-every8d';
import { SMSRequestResult } from '@rytass/sms';
class AppointmentReminderService {
private smsService: SMSServiceEvery8D;
constructor() {
this.smsService = new SMSServiceEvery8D({
username: process.env.EVERY8D_USERNAME!,
password: process.env.EVERY8D_PASSWORD!,
onlyTaiwanMobileNumber: true,
});
}
async sendAppointmentReminder(appointment: {
customerPhone: string;
customerName: string;
date: string;
time: string;
location: string;
doctorName?: string;
}): Promise<boolean> {
const message = appointment.doctorName
? `親愛的 ${appointment.customerName},提醒您與 ${appointment.doctorName} 醫師的預約:${appointment.date} ${appointment.time},地點:${appointment.location}。請提前 10 分鐘報到。`
: `親愛的 ${appointment.customerName},提醒您的預約:${appointment.date} ${appointment.time},地點:${appointment.location}。請提前 10 分鐘報到。`;
const result = await this.smsService.send({
mobile: appointment.customerPhone,
text: message,
});
return result.status === SMSRequestResult.SUCCESS;
}
async sendBatchReminders(
appointments: Array<{
customerPhone: string;
date: string;
time: string;
}>
): Promise<{
total: number;
sent: number;
failed: number;
}> {
const results = await this.smsService.send(
appointments.map(apt => ({
mobile: apt.customerPhone,
text: `預約提醒:${apt.date} ${apt.time}。請準時出席,如需取消請提前通知。`,
}))
);
const sent = results.filter(
r => r.status === SMSRequestResult.SUCCESS
).length;
const failed = results.filter(
r => r.status === SMSRequestResult.FAILED
).length;
return {
total: results.length,
sent,
failed,
};
}
}
NestJS Integration
Basic Setup
import { Module, Global } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { SMSServiceEvery8D } from '@rytass/sms-adapter-every8d';
export const SMS_SERVICE = Symbol('SMS_SERVICE');
@Global()
@Module({
imports: [ConfigModule],
providers: [
{
provide: SMS_SERVICE,
useFactory: (config: ConfigService) => {
return new SMSServiceEvery8D({
username: config.get('EVERY8D_USERNAME')!,
password: config.get('EVERY8D_PASSWORD')!,
onlyTaiwanMobileNumber: config.get('EVERY8D_TAIWAN_ONLY') === 'true',
});
},
inject: [ConfigService],
},
],
exports: [SMS_SERVICE],
})
export class SMSModule {}
Using in Services
import { Injectable, Inject, Logger } from '@nestjs/common';
import { SMSServiceEvery8D } from '@rytass/sms-adapter-every8d';
import { SMSRequestResult } from '@rytass/sms';
import { SMS_SERVICE } from './sms.module';
@Injectable()
export class NotificationService {
private readonly logger = new Logger(NotificationService.name);
constructor(
@Inject(SMS_SERVICE)
private readonly smsService: SMSServiceEvery8D,
) {}
async sendVerificationCode(
phoneNumber: string,
code: string,
): Promise<boolean> {
try {
const result = await this.smsService.send({
mobile: phoneNumber,
text: `您的驗證碼是 ${code},10 分鐘內有效。`,
});
if (result.status === SMSRequestResult.SUCCESS) {
this.logger.log(`Verification code sent to ${phoneNumber}`);
return true;
} else {
this.logger.error(
`Failed to send verification code to ${phoneNumber}: ${result.errorMessage}`,
);
return false;
}
} catch (error) {
this.logger.error('SMS sending error:', error);
return false;
}
}
async sendBulkNotification(
phoneNumbers: string[],
message: string,
): Promise<{
total: number;
successful: number;
failed: number;
}> {
try {
const results = await this.smsService.send({
mobileList: phoneNumbers,
text: message,
});
const successful = results.filter(
r => r.status === SMSRequestResult.SUCCESS,
).length;
const failed = results.filter(
r => r.status === SMSRequestResult.FAILED,
).length;
this.logger.log(
`Bulk notification sent: ${successful} successful, ${failed} failed`,
);
return {
total: results.length,
successful,
failed,
};
} catch (error) {
this.logger.error('Bulk SMS sending error:', error);
throw error;
}
}
}
Usage in Controllers
import { Controller, Post, Body } from '@nestjs/common';
import { NotificationService } from './notification.service';
@Controller('notifications')
export class NotificationController {
constructor(private readonly notificationService: NotificationService) {}
@Post('send-verification')
async sendVerification(
@Body() body: { phoneNumber: string; code: string },
): Promise<{ success: boolean }> {
const success = await this.notificationService.sendVerificationCode(
body.phoneNumber,
body.code,
);
return { success };
}
@Post('send-bulk')
async sendBulk(
@Body() body: { phoneNumbers: string[]; message: string },
): Promise<{
total: number;
successful: number;
failed: number;
}> {
return await this.notificationService.sendBulkNotification(
body.phoneNumbers,
body.message,
);
}
}
Configuration
Environment Variables
EVERY8D_USERNAME=your_every8d_username
EVERY8D_PASSWORD=your_every8d_password
EVERY8D_BASE_URL=https://api.e8d.tw
EVERY8D_TAIWAN_ONLY=true
TypeScript Configuration
export interface SMSConfig {
username: string;
password: string;
baseUrl?: string;
onlyTaiwanMobileNumber?: boolean;
}
export const smsConfig: SMSConfig = {
username: process.env.EVERY8D_USERNAME!,
password: process.env.EVERY8D_PASSWORD!,
baseUrl: process.env.EVERY8D_BASE_URL || 'https://api.e8d.tw',
onlyTaiwanMobileNumber: process.env.EVERY8D_TAIWAN_ONLY === 'true',
};
Error Handling
Handling Delivery Failures
import { SMSServiceEvery8D } from '@rytass/sms-adapter-every8d';
import { SMSRequestResult } from '@rytass/sms';
import { Every8DError } from '@rytass/sms-adapter-every8d';
async function sendWithRetry(
smsService: SMSServiceEvery8D,
mobile: string,
text: string,
maxRetries: number = 3
): Promise<boolean> {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const result = await smsService.send({ mobile, text });
if (result.status === SMSRequestResult.SUCCESS) {
console.log(`SMS sent successfully on attempt ${attempt}`);
return true;
}
if (result.errorCode === Every8DError.FORMAT_ERROR) {
console.error('Invalid format - no retry needed');
return false;
}
console.warn(`Attempt ${attempt} failed:`, result.errorMessage);
if (attempt < maxRetries) {
await new Promise(resolve =>
setTimeout(resolve, Math.pow(2, attempt) * 1000)
);
}
} catch (error) {
console.error(`Attempt ${attempt} error:`, error);
if (attempt === maxRetries) {
throw error;
}
}
}
return false;
}
Batch Error Handling
async function sendBatchWithErrorHandling(
smsService: SMSServiceEvery8D,
phoneNumbers: string[],
message: string
): Promise<{
successful: string[];
failed: Array<{ phone: string; error: string }>;
}> {
const results = await smsService.send({
mobileList: phoneNumbers,
text: message,
});
const successful: string[] = [];
const failed: Array<{ phone: string; error: string }> = [];
results.forEach(result => {
if (result.status === SMSRequestResult.SUCCESS) {
successful.push(result.mobile);
} else {
failed.push({
phone: result.mobile,
error: result.errorMessage || 'Unknown error',
});
}
});
if (failed.length > 0) {
console.error('Failed deliveries:', failed);
}
return { successful, failed };
}
Best Practices
Message Content
class MessageTemplates {
static verification(code: string): string {
return `您的驗證碼是 ${code},10 分鐘內有效。請勿將此驗證碼告知他人。`;
}
static orderConfirmation(orderNumber: string, amount: number): string {
return `訂單 ${orderNumber} 已確認。金額:NT$${amount}。感謝您的購買!`;
}
static appointmentReminder(date: string, time: string): string {
return `預約提醒:${date} ${time}。請提前 10 分鐘報到。如需取消請來電。`;
}
static promotion(discount: string): string {
return `限時優惠!${discount} 折扣活動進行中。查看詳情:[連結]。回 N 取消訂閱。`;
}
}
Security
const badService = new SMSServiceEvery8D({
username: 'myusername',
password: 'mypassword',
});
const goodService = new SMSServiceEvery8D({
username: process.env.EVERY8D_USERNAME!,
password: process.env.EVERY8D_PASSWORD!,
});
function isValidTaiwanMobile(phone: string): boolean {
const cleaned = phone.replace(/[^0-9]/g, '').replace(/^886/, '0');
return /^09\d{8}$/.test(cleaned);
}
class RateLimitedSMSService {
private lastSendTime = 0;
private minInterval = 1000;
async send(smsService: SMSServiceEvery8D, data: any) {
const now = Date.now();
const timeSinceLastSend = now - this.lastSendTime;
if (timeSinceLastSend < this.minInterval) {
await new Promise(resolve =>
setTimeout(resolve, this.minInterval - timeSinceLastSend)
);
}
const result = await smsService.send(data);
this.lastSendTime = Date.now();
return result;
}
}
async function sendWithAuditLog(
smsService: SMSServiceEvery8D,
mobile: string,
text: string,
userId?: string
): Promise<void> {
const auditEntry = {
timestamp: new Date(),
userId,
recipient: mobile,
messageLength: text.length,
action: 'SMS_SEND',
};
try {
const result = await smsService.send({ mobile, text });
await logAudit({
...auditEntry,
status: result.status,
messageId: result.messageId,
});
} catch (error) {
await logAudit({
...auditEntry,
status: 'ERROR',
error: error.message,
});
throw error;
}
}
async function logAudit(entry: any): Promise<void> {
}
Performance
async function inefficientSend(phones: string[], message: string) {
for (const phone of phones) {
await smsService.send({ mobile: phone, text: message });
}
}
async function efficientSend(phones: string[], message: string) {
await smsService.send({ mobileList: phones, text: message });
}
async function sendLargeBatch(
smsService: SMSServiceEvery8D,
phones: string[],
message: string,
chunkSize: number = 100
): Promise<void> {
for (let i = 0; i < phones.length; i += chunkSize) {
const chunk = phones.slice(i, i + chunkSize);
await smsService.send({
mobileList: chunk,
text: message,
});
if (i + chunkSize < phones.length) {
await new Promise(resolve => setTimeout(resolve, 1000));
}
}
}
Compliance
interface UserSMSPreference {
phoneNumber: string;
optedOut: boolean;
marketingConsent: boolean;
transactionalOnly: boolean;
}
async function sendRespectingPreferences(
smsService: SMSServiceEvery8D,
user: UserSMSPreference,
message: string,
messageType: 'marketing' | 'transactional'
): Promise<boolean> {
if (user.optedOut) {
console.log('User opted out - skipping SMS');
return false;
}
if (messageType === 'marketing' && !user.marketingConsent) {
console.log('No marketing consent - skipping SMS');
return false;
}
const result = await smsService.send({
mobile: user.phoneNumber,
text: message,
});
return result.status === SMSRequestResult.SUCCESS;
}
function createMarketingMessage(content: string): string {
return `${content}\n\n回覆 N 取消訂閱簡訊通知。`;
}
class DoNotContactList {
private blockedNumbers = new Set<string>();
async add(phoneNumber: string): Promise<void> {
this.blockedNumbers.add(phoneNumber);
}
async remove(phoneNumber: string): Promise<void> {
this.blockedNumbers.delete(phoneNumber);
}
isBlocked(phoneNumber: string): boolean {
return this.blockedNumbers.has(phoneNumber);
}
async filterAllowed(phoneNumbers: string[]): Promise<string[]> {
return phoneNumbers.filter(phone => !this.isBlocked(phone));
}
}
Internal Implementation Details
Empty Target Handling
傳入空陣列或空 mobileList 會拋出錯誤:
await smsService.send([]);
await smsService.send({ mobileList: [], text: 'Test' });
Batch Message Optimization
相同訊息內容的多個請求會自動合併為單一 API 呼叫,提高發送效率:
const results = await smsService.send([
{ mobile: '0912345678', text: 'Hello' },
{ mobile: '0923456789', text: 'Hello' },
{ mobile: '0934567890', text: 'Hello' },
]);
Partial Delivery Failure Handling
當部分發送失敗時,API 回傳 unsend 數量,系統會從列表末端開始標記失敗:
const results = await smsService.send({
mobileList: ['0911...', '0922...', '0933...', '0944...', '0955...'],
text: 'Test',
});
Troubleshooting
Common Issues
1. Authentication Failure
Symptoms:
- All messages fail to send
- Error message about credentials
Solutions:
console.log('Username:', process.env.EVERY8D_USERNAME);
console.log('Password length:', process.env.EVERY8D_PASSWORD?.length);
EVERY8D_USERNAME=your_username # ❌ Trailing space
EVERY8D_USERNAME=your_username # ✅ No space
const testService = new SMSServiceEvery8D({
username: 'YOUR_USERNAME',
password: 'YOUR_PASSWORD',
});
const result = await testService.send({
mobile: '0987654321',
text: 'Test',
});
console.log('Result:', result);
2. Invalid Phone Number Format
Symptoms:
- FORMAT_ERROR (-306) error code
- Message fails for specific numbers
Solutions:
const service = new SMSServiceEvery8D({
username: process.env.EVERY8D_USERNAME!,
password: process.env.EVERY8D_PASSWORD!,
onlyTaiwanMobileNumber: true,
});
function validateTaiwanMobile(phone: string): boolean {
const normalized = phone.replace(/[^0-9]/g, '').replace(/^886/, '0');
return /^09\d{8}$/.test(normalized);
}
if (!validateTaiwanMobile(phoneNumber)) {
console.error('Invalid Taiwan mobile number:', phoneNumber);
}
3. Message Not Delivered
Symptoms:
- Status shows SUCCESS but message not received
- Delays in delivery
Solutions:
const result = await smsService.send({ mobile, text });
console.log('Message ID for tracking:', result.messageId);
API Reference
SMSServiceEvery8D
Constructor Options:
| Property | Type | Required | Default | Description |
|---|
username | string | Yes | - | Every8D account username |
password | string | Yes | - | Every8D account password |
baseUrl | string | No | 'https://api.e8d.tw' | API endpoint URL |
onlyTaiwanMobileNumber | boolean | No | false | Restrict to Taiwan mobile numbers only |
Methods:
send(request: Every8DSMSRequest): Promise<Every8DSMSSendResponse>
send(requests: Every8DSMSRequest[]): Promise<Every8DSMSSendResponse[]>
send(request: Every8DSMSMultiTargetRequest): Promise<Every8DSMSSendResponse[]>
Request Types
interface Every8DSMSRequest {
mobile: string;
text: string;
}
interface Every8DSMSMultiTargetRequest {
mobileList: string[];
text: string;
}
Response Types
interface Every8DSMSSendResponse {
messageId?: string;
status: SMSRequestResult;
mobile: string;
errorMessage?: string;
errorCode?: Every8DError;
}
enum SMSRequestResult {
SUCCESS = 'SUCCESS',
FAILED = 'FAILED',
}
enum Every8DError {
FORMAT_ERROR = -306,
UNKNOWN = -99,
}
Advanced Topics
For creating new SMS adapters or extending functionality, see the SMS Development Guide.