Understanding SMS fundamentals is crucial for building reliable messaging applications. This guide covers the core concepts, limitations, and best practices for SMS development with Relay.
What is SMS?
SMS (Short Message Service) is a text messaging service that allows sending messages of up to 160 characters between mobile devices. Despite being developed in the 1980s, SMS remains one of the most reliable and universally supported communication channels.
Key Characteristics
Universal Support: Works on virtually all mobile devices
High Open Rates: 98% of SMS messages are read within 3 minutes
No Internet Required: Uses cellular network infrastructure
Reliable Delivery: Built-in delivery confirmation system
Cost Effective: Typically $0.0075 per message in the US
SMS vs Other Messaging
SMS vs MMS
Feature
SMS
MMS
Content
Text only
Text, images, audio, video
Size Limit
160/70 characters
Up to 5MB
Cost
~$0.0075
~$0.02-0.05
Delivery
Nearly 100%
85-95% (device dependent)
Character Set
GSM 7-bit or Unicode
Unicode
SMS vs App-Based Messaging
Aspect
SMS
WhatsApp/iMessage
Setup Required
None
App installation
Internet Dependency
No
Yes
Rich Media
Limited (MMS)
Full support
Read Receipts
Delivery reports
Read receipts
Group Messaging
Limited
Full featured
Business Features
Basic
Advanced
Phone Number Formats
E.164 Format
E.164 is the international standard for phone numbers. All Relay APIs require E.164 format.
Format: +[country code][area code][local number]
Code
// ✅ Correct E.164 format (US/Canada supported)"+15551234567" // US number"+14165551234" // Canada number// ❌ Incorrect formats"555-123-4567" // Missing country code and + prefix"(555) 123-4567" // Formatting characters"15551234567" // Missing + prefix"+1 555 123 4567" // Spaces
Validation
Always validate phone numbers before sending:
Code
function isValidE164(phoneNumber) { // E.164 regex: + followed by 1-15 digits return /^\+[1-9]\d{1,14}$/.test(phoneNumber);}// Relay-specific validation for US/Canadafunction isValidRelayNumber(phoneNumber) { // Must be US or Canada (+1 country code) return /^\+1\d{10}$/.test(phoneNumber);}// Usageif (!isValidRelayNumber(phoneNumber)) { throw new Error('Phone number must be US/Canada format (e.g., +15551234567 or +14165551234)');}
Supported Regions
Relay currently supports United States and Canada:
Country
Code
Example
Status
United States
+1
+15551234567
Available
Canada
+1
+14165551234
Available
International Expansion
While E.164 is the international standard for phone numbers, Relay currently validates and delivers only to US and Canada numbers. International expansion is planned based on customer demand.
Character Limits and Encoding
Single SMS Limits
SMS character limits depend on the character encoding used:
GSM 7-bit Encoding (160 characters)
Used for basic Latin characters, numbers, and common symbols:
Code
// Characters that use GSM 7-bit encodingconst gsmCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 !\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~";// This message uses 45 characters in GSM 7-bitconst message = "Hello! Your order #12345 is ready for pickup.";
Unicode Encoding (70 characters)
Required for emojis, accented characters, and non-Latin scripts:
Code
// These characters require Unicode encodingconst unicodeMessage = "¡Hola! Tu pedido está listo. 🚚"; // 32 characters in Unicodeconst emojiMessage = "Your order is ready! 📦✅🎉"; // 28 characters in Unicodeconst accentedMessage = "Café, résumé, naïve"; // 19 characters in Unicode
Message Segmentation
Messages longer than the single SMS limit are automatically split into segments:
GSM 7-bit Segmentation
Single SMS: 160 characters
Multi-part SMS: 153 characters per segment (7 characters reserved for headers)
Unicode Segmentation
Single SMS: 70 characters
Multi-part SMS: 67 characters per segment (3 characters reserved for headers)
Code
// Example of message segmentationconst longMessage = `Welcome to our service! Your account has been created successfully and you now have access to all premium features. If you have any questions, please contact our support team at support@example.com or call us at 1-800-555-0123. Thank you for choosing us and we look forward to serving you!`.trim();// This message (~280 characters) would be split into:// - Segment 1: 153 characters// - Segment 2: 127 characters// Total cost: 2 SMS messages
Understanding the SMS delivery process helps with troubleshooting and setting user expectations.
Delivery States
Code
graph LR A[Message Sent] --> B[Queued] B --> C[Sent to Carrier] C --> D[Delivered] C --> E[Failed] D --> F[Delivery Confirmed] E --> G[Retry or Permanent Failure]
Status Definitions
Status
Description
What It Means
queued
Message accepted by Relay
Ready to send
sent
Sent to mobile carrier
In transit
delivered
Delivered to device
Successfully received
failed
Delivery failed
Permanent failure
Delivery Times
Typical SMS delivery times for US/Canada:
Standard delivery: 1-30 seconds
Peak hours/holidays: Up to 10 minutes
Network congestion: Up to 30 minutes
Delivery times shown are for US and Canada destinations only.
Delivery Failure Reasons
Common failure reasons and solutions:
Code
// Handle delivery failures in webhooksfunction handleMessageFailed(messageData) { const { failure_reason, error_code } = messageData; switch (error_code) { case '30003': // Unreachable destination handset console.log('Phone may be turned off or out of service'); // Don't retry immediately break; case '30006': // Landline or unreachable carrier console.log('Number may be a landline or invalid'); // Mark as permanently failed markAsInvalid(messageData.to); break; case '30007': // Carrier violation or spam filtering console.log('Message blocked by carrier spam filter'); // Review message content reviewForSpam(messageData.message); break; case '30008': // Unknown error console.log('Unknown delivery error, may retry'); // Implement retry logic scheduleRetry(messageData); break; }}
Regional Considerations
United States & Canada (Currently Supported)
Relay is optimized for US and Canada messaging with the following compliance requirements:
United States:
10DLC Registration: Required for business messaging (handled automatically for Starter tier)
Carrier Filtering: Major carriers apply spam filtering to promotional content
Opt-out Required: Must provide STOP/UNSUBSCRIBE options
Time Restrictions: Respect 8 AM - 9 PM recipient local time
TCPA Compliance: Prior express consent required for marketing messages
Canada:
CASL Compliance: Canadian Anti-Spam Legislation applies
Consent Requirements: Express or implied consent needed
Time Restrictions: Similar to US, respect local hours
Code
// US/Canada-compliant message formatconst compliantMessage = `Hi John! Your appointment is confirmed for tomorrow at 2 PM.Reply STOP to opt out. Questions? Call 555-123-4567.`.trim();
International Expansion
Relay is currently focused on US and Canada markets. International expansion is planned based on customer demand, with the same compliance and quality standards applied to each new region.
When sending to unsupported regions, the API will return an error indicating the destination is not yet supported.
Message Types and Use Cases
Transactional Messages
High-priority, individual messages triggered by user actions:
Code
// Examples of transactional messagesconst transactionalExamples = { verification: "Your verification code is: 123456. Valid for 10 minutes.", orderConfirmation: "Order #12345 confirmed! Est. delivery: Jan 15. Track: link.co/abc123", appointment: "Reminder: Your appointment with Dr. Smith is tomorrow at 2 PM.", alert: "Security alert: New login detected from New York. If this wasn't you, secure your account.", receipt: "Payment of $29.99 processed successfully. Receipt: receipt.co/xyz789"};
Promotional Messages
Marketing and promotional content:
Code
// Promotional message best practicesconst promotionalMessage = `🎉 FLASH SALE: 50% off everything! Use code FLASH50. Shop now: shop.example.comReply STOP to opt out.`.trim();// Requirements for promotional SMS:// 1. Clear opt-out instructions// 2. Brand identification // 3. Respect time zones// 4. Honor opt-out requests immediately
Two-Way Conversations
Interactive messaging with customer responses:
Code
// Conversation flow exampleconst conversationFlow = { initial: "Hi! I'm your virtual assistant. How can I help? Reply 1 for hours, 2 for locations.", responses: { '1': "We're open Mon-Fri 9 AM-6 PM, Sat 10 AM-4 PM. Anything else?", '2': "We have locations in downtown (123 Main St) and mall (456 Oak Ave). Need directions?", 'hours': "We're open Mon-Fri 9 AM-6 PM, Sat 10 AM-4 PM. Anything else?", 'stop': "You've been unsubscribed. Reply START to opt back in." }};
Opt-Out Management
Legal Requirements
SMS opt-out is legally required in most jurisdictions:
Code
// Standard opt-out keywords (case-insensitive)const optOutKeywords = [ 'STOP', 'STOPALL', 'UNSUBSCRIBE', 'CANCEL', 'END', 'QUIT'];// Standard opt-in keywordsconst optInKeywords = [ 'START', 'YES', 'UNSTOP'];function handleOptOut(fromNumber, message) { const normalizedMessage = message.trim().toUpperCase(); if (optOutKeywords.includes(normalizedMessage)) { // Add to opt-out list immediately addToOptOutList(fromNumber); // Send confirmation (required) sendSMS(fromNumber, "You have been unsubscribed. Reply START to opt back in."); return true; // Message handled } if (optInKeywords.includes(normalizedMessage)) { // Remove from opt-out list removeFromOptOutList(fromNumber); // Send welcome back message sendSMS(fromNumber, "Welcome back! You're now subscribed to our updates."); return true; // Message handled } return false; // Not an opt-out/in message}
Compliance Best Practices
Code
// Check opt-out status before sendingasync function sendCompliantSMS(to, message) { // 1. Check opt-out list if (await isOptedOut(to)) { throw new Error('Recipient has opted out'); } // 2. Check time restrictions (8 AM - 9 PM local time) if (!isValidSendTime(to)) { throw new Error('Outside allowed sending hours'); } // 3. Add opt-out instructions for promotional messages if (isPromotionalMessage(message)) { message += "\n\nReply STOP to opt out."; } // 4. Send message return await sendSMS(to, message);}
Cost Optimization
Message Optimization
Code
// Optimize message length to avoid extra segmentsfunction optimizeMessage(message) { const segmentInfo = calculateSMSSegments(message); if (segmentInfo.segments > 1) { console.warn(`Message will use ${segmentInfo.segments} segments ($${segmentInfo.segments * 0.0075})`); // Suggest optimization const maxLength = segmentInfo.encoding === 'Unicode' ? 70 : 160; if (message.length > maxLength) { console.log(`Consider shortening to ${maxLength} characters to use 1 segment`); } } return segmentInfo;}// URL shortening to save charactersfunction shortenUrls(message) { const urlRegex = /(https?:\/\/[^\s]+)/g; return message.replace(urlRegex, (url) => { // Use a URL shortener service return shortenUrl(url); // Returns something like "bit.ly/abc123" });}
Batching and Timing
Code
// Batch messages to avoid rate limitsasync function sendBatchSMS(recipients, message) { const batchSize = 10; // Respect rate limits const results = []; for (let i = 0; i < recipients.length; i += batchSize) { const batch = recipients.slice(i, i + batchSize); // Send batch in parallel const batchPromises = batch.map(async (recipient) => { try { return await sendCompliantSMS(recipient, message); } catch (error) { return { error: error.message, recipient }; } }); const batchResults = await Promise.all(batchPromises); results.push(...batchResults); // Rate limiting delay between batches if (i + batchSize < recipients.length) { await new Promise(resolve => setTimeout(resolve, 1000)); } } return results;}