SMS marketing boasts incredible 98% open rates and 45% response rates. But success requires more than just blasting messages to your list. This guide covers proven strategies that drive engagement while respecting subscribers and staying compliant.
Building Your SMS List
Permission-Based Growth
Never buy SMS lists. Always obtain explicit permission:
// lib/sms-consent.ts
export async function captureSMSConsent(userId: string, phoneNumber: string) {
// Record consent with timestamp and IP
await db.smsConsent.create({
data: {
userId,
phoneNumber,
consentedAt: new Date(),
ipAddress: getClientIP(),
method: 'web_form', // or 'checkout', 'competition', etc.
},
});
// Send confirmation
await sendSMS(
phoneNumber,
'You're now subscribed to our SMS updates. Reply STOP anytime to opt out.'
);
}Incentivized Signups
Offer compelling reasons to subscribe:
- Exclusive discounts
- Early access to sales
- Product updates
- Insider information
SMS Segmentation
Demographic Segmentation
interface SMSSegment {
name: string;
condition: (user: User) => boolean;
}
const segments: SMSSegment[] = [
{
name: 'VIP Customers',
condition: (user) => user.totalPurchases > 1000,
},
{
name: 'Recent Purchasers',
condition: (user) => {
const lastPurchase = new Date(user.lastPurchaseDate);
const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);
return lastPurchase > thirtyDaysAgo;
},
},
{
name: 'Cart Abandoners',
condition: (user) => user.hasAbandonedCart,
},
];Behavioral Triggers
Send based on user actions:
export async function triggerBehavioralSMS(userId: string, event: string) {
const user = await db.users.findUnique({ where: { id: userId } });
switch (event) {
case 'cart_abandoned':
await sendCartReminder(user);
break;
case 'purchase_completed':
await sendPurchaseConfirmation(user);
break;
case 'order_shipped':
await sendShippingNotification(user);
break;
case 'product_viewed_multiple_times':
await sendProductRecommendation(user);
break;
}
}Personalization Strategies
Dynamic Content
export async function personalizeMessage(
userId: string,
template: string
): Promise<string> {
const user = await db.users.findUnique({
where: { id: userId },
include: {
preferences: true,
recentPurchases: true,
},
});
let personalized = template;
// Replace basic placeholders
personalized = personalized.replace(/\{firstName\}/g, user.firstName);
personalized = personalized.replace(/\{lastName\}/g, user.lastName);
// Replace dynamic content
personalized = personalized.replace(
/\{recommendedProduct\}/g,
user.recentPurchases[0]?.product.name || 'our latest product'
);
return personalized;
}Time Zone Optimization
export async function optimizeSendTime(phoneNumber: string, message: string) {
const timeZone = await detectTimeZone(phoneNumber);
const localTime = getCurrentTimeInZone(timeZone);
// Optimal send times: 9 AM - 12 PM local time
if (localTime.getHours() >= 9 && localTime.getHours() < 12) {
return await sendSMS(phoneNumber, message);
}
// Schedule for optimal time
const optimalSendTime = calculateOptimalSendTime(localTime);
return await scheduleSMS(phoneNumber, message, optimalSendTime);
}Campaign Types
Flash Sales
export async function sendFlashSale(
segment: string,
discount: number,
endTime: Date
) {
const users = await getUsersBySegment(segment);
for (const user of users) {
const message = `🚨 FLASH SALE: ${discount}% off everything! Ends at ${formatTime(endTime)}. Shop now: ${shortenUrl(shopUrl)}`;
await sendSMS(user.phoneNumber, message);
}
}Exclusive Offers
export async function sendExclusiveOffer(
userId: string,
offerCode: string,
discount: number
) {
const message = `🎁 Exclusive offer just for you: Use code ${offerCode} for ${discount}% off your next order!`;
return await sendSMS(userId, message);
}Event Reminders
export async function sendEventReminder(
userId: string,
event: Event
) {
const message = `Reminder: ${event.name} is tomorrow at ${formatTime(event.startTime)}. See you there!`;
return await sendSMS(userId, message);
}Compliance Best Practices
TCPA Compliance
Follow Telephone Consumer Protection Act rules:
- Get explicit written consent
- Include clear opt-out instructions
- Honor opt-out requests immediately
- Send only during reasonable hours (8 AM - 9 PM)
export async function ensureCompliance(phoneNumber: string, message: string) {
// Check if user has consented
const consent = await db.smsConsent.findFirst({
where: { phoneNumber, active: true },
});
if (!consent) {
throw new Error('No consent found for this number');
}
// Check if user has opted out
const optOut = await db.smsOptOut.findFirst({
where: { phoneNumber },
});
if (optOut) {
throw new Error('User has opted out');
}
// Add opt-out instructions
const compliantMessage = `${message}\
\
Reply STOP to opt out`;
return await sendSMS(phoneNumber, compliantMessage);
}Measurement and Analytics
Track Key Metrics
export async function trackSMSMetrics(campaignId: string) {
const messages = await db.sMSMessages.findMany({
where: { campaignId },
});
const sent = messages.length;
const delivered = messages.filter(m => m.status === 'delivered').length;
const failed = messages.filter(m => m.status === 'failed').length;
// Track conversions
const conversions = await db.conversions.count({
where: {
campaignId,
},
});
return {
sent,
delivered,
deliveryRate: (delivered / sent) * 100,
failed,
failureRate: (failed / sent) * 100,
conversions,
conversionRate: (conversions / delivered) * 100,
};
}A/B Testing
Test Messages
export async function abTestSMS(
segment: string,
messageA: string,
messageB: string
) {
const users = await getUsersBySegment(segment);
// Split 50/50
const midpoint = Math.floor(users.length / 2);
const groupA = users.slice(0, midpoint);
const groupB = users.slice(midpoint);
// Send messages
for (const user of groupA) {
await sendSMS(user.phoneNumber, messageA);
}
for (const user of groupB) {
await sendSMS(user.phoneNumber, messageB);
}
// Track results
return {
groupASize: groupA.length,
groupBSize: groupB.length,
};
}Best Practices
1. Keep It Short
SMS has a 160-character limit. Be concise:
- ✅ "SALE: 50% off everything today only!"
- ❌ "We're having a massive sale with discounts up to 50% off everything in our store, but only for today..."
2. Create Urgency
Use time-sensitive language:
- "Ends tonight"
- "Last chance"
- "Only 2 hours left"
3. Use Emojis
Emojis increase engagement by 25%:
- 🎉 for celebrations
- ⚡ for urgency
- 🎁 for offers
4. Include Clear CTAs
Tell recipients exactly what to do:
- "Shop now: [link]"
- "Reply YES to confirm"
- "Call 555-123-4567"
5. Timing Matters
Send when users are most likely to read:
- 9 AM - 12 PM: Highest engagement
- Avoid late nights and early mornings
Conclusion
SMS marketing drives incredible engagement when done right. Focus on:
- Permission-based lists for compliance
- Segmentation for relevance
- Personalization for connection
- Optimal timing for impact
- Compliance for trust
Remember: SMS is personal. Respect your subscribers, provide value, and they'll reward you with engagement and loyalty.
The key is balance: frequent enough to stay top-of-mind, but not so frequent that you become annoying. Monitor your metrics and adjust accordingly.