Channels
Multi-Channel Messaging
Send SMS, Email, and WhatsApp messages through NotifyHub's unified API. This guide covers all three channels with detailed examples and best practices.
Overview
NotifyHub provides a single endpoint for sending messages across all channels:
POST https://api.notifyhub.com/notifications/send
Key Features:
- Unified interface for all channels
- Single or bulk sending
- Automatic recipient validation
- Delivery tracking and logging
- Per-organization credential overrides
SMS via Lancola
Send text messages to mobile phones worldwide.
Basic SMS
Request:
{
"type": "sms",
"to": "+254712345678",
"message": "Your OTP code is 483920. Valid for 10 minutes."
}Response:
{
"recipient": "+254712345678",
"status": "success"
}Phone Number Format
Requirements:
- International format with country code
- Can include
+prefix (will be removed automatically) - No spaces, dashes, or parentheses
Valid Formats:
+254712345678 ✅
254712345678 ✅
+1-555-123-4567 ❌ (will be normalized to +15551234567)
Country Code Examples:
Kenya: +254
USA: +1
UK: +44
India: +91
Nigeria: +234
Bulk SMS
Send to multiple recipients:
Request:
{
"type": "sms",
"to": ["+254712345678", "+254723456789", "+254734567890"],
"message": "Flash sale! 50% off all items today only. Shop now: https://example.com"
}Response:
[
{ "recipient": "+254712345678", "status": "success" },
{ "recipient": "+254723456789", "status": "success" },
{ "recipient": "+254734567890", "status": "failed", "error": "Invalid number" }
]Message Length
Standard SMS: 160 characters Long SMS: Automatically split into multiple parts
- 1 part: 160 characters
- 2 parts: 306 characters (153 × 2)
- 3 parts: 459 characters (153 × 3)
Cost Calculation:
1-160 chars = 1 SMS unit
161-306 chars = 2 SMS units
307-459 chars = 3 SMS units
Best Practice: Keep messages under 160 characters to minimize costs.
Character Encoding
GSM-7 Alphabet (160 chars/SMS):
- Standard ASCII letters (A-Z, a-z)
- Numbers (0-9)
- Common symbols (. , ! ? @ - _)
Unicode (70 chars/SMS):
- Emojis 😊
- Special characters (€, £, ¥)
- Non-Latin scripts (Arabic, Chinese, etc.)
Example:
// GSM-7 (160 chars)
"Your code is 123456. Valid for 10 min." // ✅ 40 chars
// Unicode (70 chars)
"Your code is 123456 😊" // ⚠️ Uses Unicode due to emojiSMS Use Cases
OTP/Verification Codes:
{
"type": "sms",
"to": "+254712345678",
"message": "Your verification code is 483920. Valid for 10 minutes. Do not share this code."
}Order Confirmations:
{
"type": "sms",
"to": "+254712345678",
"message": "Order #12345 confirmed! Estimated delivery: Jan 30. Track: https://track.example.com/12345"
}Appointment Reminders:
{
"type": "sms",
"to": "+254712345678",
"message": "Reminder: Your appointment with Dr. Smith is tomorrow at 2:00 PM. Reply CONFIRM or CANCEL."
}Promotional Messages:
{
"type": "sms",
"to": "+254712345678",
"message": "Flash Sale! 50% off all items today only. Use code FLASH50. Shop now: https://example.com"
}Complete SMS Example
async function sendSMS(to, message, token) {
try {
const response = await fetch('https://api.notifyhub.com/notifications/send', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
type: 'sms',
to,
message
})
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${await response.text()}`);
}
const result = await response.json();
console.log('SMS sent:', result);
return result;
} catch (error) {
console.error('SMS error:', error);
throw error;
}
}
// Usage
await sendSMS('+254712345678', 'Hello from NotifyHub!', token);Email via SMTP
Send professional emails with HTML formatting and attachments.
Basic Email
Request:
{
"type": "email",
"to": "user@example.com",
"subject": "Welcome to NotifyHub",
"message": "<h1>Welcome!</h1><p>Thank you for signing up.</p>"
}Response:
{
"recipient": "user@example.com",
"status": "success"
}HTML vs Plain Text
HTML Email (recommended for newsletters, marketing):
{
"type": "email",
"to": "user@example.com",
"subject": "Monthly Newsletter",
"message": "<html><body><h1>January Newsletter</h1><p>Check out our latest updates...</p></body></html>"
}Plain Text (detected automatically if no HTML tags):
{
"type": "email",
"to": "user@example.com",
"subject": "Password Reset",
"message": "Click this link to reset your password: https://example.com/reset?token=abc123"
}Email with Attachments
Base64-encoded attachments:
{
"type": "email",
"to": "user@example.com",
"subject": "Invoice #12345",
"message": "<p>Please find your invoice attached.</p>",
"attachments": [
{
"filename": "invoice-12345.pdf",
"content": "JVBERi0xLjQKJeLjz9MKMyAwIG9iago8PC9...",
"contentType": "application/pdf"
}
]
}Field Descriptions:
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| filename | string | Yes | Attachment filename with extension |
| content | string | Yes | Base64-encoded file content |
| contentType | string | Yes | MIME type (e.g., "application/pdf", "image/png") |
Common MIME Types:
PDF: application/pdf
PNG: image/png
JPEG: image/jpeg
Word: application/vnd.openxmlformats-officedocument.wordprocessingml.document
Excel: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
CSV: text/csv
Text: text/plain
Creating Base64 Attachments
JavaScript (Browser):
function fileToBase64(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => {
const base64 = reader.result.split(',')[1]; // Remove data:mime;base64, prefix
resolve(base64);
};
reader.onerror = reject;
reader.readAsDataURL(file);
});
}
// Usage
const fileInput = document.getElementById('attachment');
const file = fileInput.files[0];
const base64Content = await fileToBase64(file);
await fetch('https://api.notifyhub.com/notifications/send', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
type: 'email',
to: 'user@example.com',
subject: 'Document Attached',
message: '<p>Please review the attached document.</p>',
attachments: [{
filename: file.name,
content: base64Content,
contentType: file.type
}]
})
});Node.js:
const fs = require('fs');
function fileToBase64(filePath) {
const file = fs.readFileSync(filePath);
return file.toString('base64');
}
// Usage
const base64Content = fileToBase64('./invoice.pdf');
await fetch('https://api.notifyhub.com/notifications/send', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
type: 'email',
to: 'user@example.com',
subject: 'Invoice',
message: '<p>Your invoice is attached.</p>',
attachments: [{
filename: 'invoice.pdf',
content: base64Content,
contentType: 'application/pdf'
}]
})
});Python:
import base64
def file_to_base64(file_path):
with open(file_path, 'rb') as f:
return base64.b64encode(f.read()).decode('utf-8')
# Usage
base64_content = file_to_base64('invoice.pdf')
response = requests.post(
'https://api.notifyhub.com/notifications/send',
headers={'Authorization': f'Bearer {token}'},
json={
'type': 'email',
'to': 'user@example.com',
'subject': 'Invoice',
'message': '<p>Your invoice is attached.</p>',
'attachments': [{
'filename': 'invoice.pdf',
'content': base64_content,
'contentType': 'application/pdf'
}]
}
)Bulk Email
Request:
{
"type": "email",
"to": ["user1@example.com", "user2@example.com", "user3@example.com"],
"subject": "Product Launch Announcement",
"message": "<h1>New Product!</h1><p>We're excited to announce...</p>"
}Response:
[
{ "recipient": "user1@example.com", "status": "success" },
{ "recipient": "user2@example.com", "status": "success" },
{ "recipient": "user3@example.com", "status": "failed", "error": "Invalid email" }
]Email Templates
Transactional Email (Password Reset):
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: Arial, sans-serif; }
.container { max-width: 600px; margin: 0 auto; padding: 20px; }
.button { background: #007bff; color: white; padding: 12px 24px;
text-decoration: none; border-radius: 4px; display: inline-block; }
</style>
</head>
<body>
<div class="container">
<h1>Reset Your Password</h1>
<p>You requested to reset your password. Click the button below:</p>
<a href="https://example.com/reset?token=abc123" class="button">Reset Password</a>
<p>This link expires in 1 hour.</p>
<p>If you didn't request this, please ignore this email.</p>
</div>
</body>
</html>Marketing Email (Newsletter):
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: Arial, sans-serif; background: #f4f4f4; }
.container { max-width: 600px; margin: 20px auto; background: white; }
.header { background: #007bff; color: white; padding: 20px; text-align: center; }
.content { padding: 30px; }
.footer { background: #333; color: white; padding: 20px; text-align: center; font-size: 12px; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>January Newsletter</h1>
</div>
<div class="content">
<h2>What's New This Month</h2>
<p>Check out our latest features and updates...</p>
<img src="https://example.com/banner.jpg" alt="Banner" style="max-width: 100%;">
</div>
<div class="footer">
<p>© 2026 Your Company. All rights reserved.</p>
<p><a href="https://example.com/unsubscribe" style="color: #fff;">Unsubscribe</a></p>
</div>
</div>
</body>
</html>Email Best Practices
-
Subject Lines:
- Keep under 50 characters
- Avoid spam trigger words (FREE, URGENT, CLICK NOW)
- Personalize when possible ("John, your order is ready")
-
HTML Design:
- Use inline CSS (email clients strip
<style>tags) - Test across email clients (Gmail, Outlook, Apple Mail)
- Include alt text for images
- Provide plain text fallback
- Use inline CSS (email clients strip
-
Deliverability:
- Use authenticated sender domains (SPF, DKIM, DMARC)
- Avoid spam words in subject/body
- Include unsubscribe links for marketing emails
- Don't send to purchased lists
-
Accessibility:
- Use semantic HTML (
<h1>,<p>, not just<div>) - Sufficient color contrast
- Descriptive link text ("Read the article" not "Click here")
- Use semantic HTML (
WhatsApp via Meta Cloud API
Send WhatsApp messages to users who have opted in.
Basic WhatsApp Message
Request:
{
"type": "whatsapp",
"to": "+254712345678",
"message": "Your order #12345 has been shipped! Track here: https://example.com/track/12345"
}Response:
{
"recipient": "+254712345678",
"status": "success"
}WhatsApp Requirements
Important Restrictions:
- 24-Hour Window: Free-form messages only allowed within 24 hours of user's last message
- Templates Required: Outside 24-hour window, must use pre-approved templates
- Opt-In Required: Users must explicitly opt-in to receive messages
- No Marketing: Unless using approved marketing templates
Phone Number Format
Same as SMS:
- International format with country code
- Example:
+254712345678
Template Messages (Future)
For messages outside 24-hour window:
{
"type": "whatsapp",
"to": "+254712345678",
"template": {
"name": "order_update",
"language": "en",
"parameters": [
{ "type": "text", "text": "12345" },
{ "type": "text", "text": "January 30" }
]
}
}Note: Templates must be pre-approved by Meta. Contact support to set up templates.
WhatsApp Use Cases
Order Updates:
{
"type": "whatsapp",
"to": "+254712345678",
"message": "Hi John! Your order #12345 has been delivered. Thank you for shopping with us!"
}Customer Support:
{
"type": "whatsapp",
"to": "+254712345678",
"message": "Thank you for contacting support. Your ticket #6789 has been created. We'll respond within 24 hours."
}Appointment Confirmations:
{
"type": "whatsapp",
"to": "+254712345678",
"message": "Your appointment is confirmed for January 30 at 2:00 PM. See you then! Reply CANCEL to cancel."
}Complete WhatsApp Example
async function sendWhatsApp(to, message, token) {
try {
const response = await fetch('https://api.notifyhub.com/notifications/send', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
type: 'whatsapp',
to,
message
})
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${await response.text()}`);
}
const result = await response.json();
console.log('WhatsApp sent:', result);
return result;
} catch (error) {
console.error('WhatsApp error:', error);
throw error;
}
}
// Usage
await sendWhatsApp(
'+254712345678',
'Your order has shipped! Track: https://track.me/12345',
token
);Multi-Channel Messaging
Send the same message across multiple channels (requires separate requests per channel).
Email + SMS Combination
async function sendMultiChannel(recipient, token) {
// Send email
await fetch('https://api.notifyhub.com/notifications/send', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
type: 'email',
to: recipient.email,
subject: 'Order Confirmation',
message: '<h1>Thank you!</h1><p>Your order #12345 is confirmed.</p>'
})
});
// Send SMS
await fetch('https://api.notifyhub.com/notifications/send', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
type: 'sms',
to: recipient.phone,
message: 'Order #12345 confirmed! Check your email for details.'
})
});
}
// Usage
await sendMultiChannel({
email: 'user@example.com',
phone: '+254712345678'
}, token);Note: For advanced multi-channel workflows, use Campaigns.
Send to All Contacts
Send a message to every contact in your organization.
Endpoint: POST https://api.notifyhub.com/notifications/send-to-all
Request:
{
"type": "sms",
"to": "all",
"message": "Important announcement: Our office will be closed on Monday for maintenance."
}Response:
[
{ "recipient": "+254712345678", "status": "success" },
{ "recipient": "+254723456789", "status": "success" },
{ "recipient": "user@example.com", "status": "failed", "error": "No phone number" }
]Note: Only sends to contacts with the required channel (phone for SMS/WhatsApp, email for Email).
Error Handling
Common Errors
Invalid Phone Number:
{
"statusCode": 400,
"message": "Invalid phone number format",
"error": "Bad Request"
}Invalid Email:
{
"statusCode": 400,
"message": "Invalid email address",
"error": "Bad Request"
}Missing Subject (Email):
{
"statusCode": 400,
"message": "Subject is required for email",
"error": "Bad Request"
}Channel Error:
{
"recipient": "+254712345678",
"status": "failed",
"error": "Invalid shortcode"
}Error Handling Example
async function sendWithRetry(payload, token, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
const response = await fetch('https://api.notifyhub.com/notifications/send', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
});
if (response.ok) {
return await response.json();
}
const error = await response.json();
// Don't retry on client errors
if (response.status >= 400 && response.status < 500) {
throw new Error(error.message);
}
// Retry on server errors
console.log(`Attempt ${i + 1} failed, retrying...`);
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
} catch (error) {
if (i === maxRetries - 1) throw error;
}
}
}Best Practices
1. Recipient Validation
Always validate before sending:
function validateRecipient(type, to) {
if (type === 'email') {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(to);
}
if (type === 'sms' || type === 'whatsapp') {
return /^\+?\d{10,15}$/.test(to.replace(/[-\s()]/g, ''));
}
return false;
}2. Rate Limiting
Implement your own rate limiting to avoid overwhelming the API:
async function sendBatch(recipients, message, token, batchSize = 100) {
for (let i = 0; i < recipients.length; i += batchSize) {
const batch = recipients.slice(i, i + batchSize);
await fetch('https://api.notifyhub.com/notifications/send', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
type: 'sms',
to: batch,
message
})
});
// Wait between batches
await new Promise(resolve => setTimeout(resolve, 1000));
}
}3. Message Personalization
Use contact data for personalized messages:
async function sendPersonalized(contacts, token) {
for (const contact of contacts) {
const message = `Hi ${contact.name}, your order #${contact.orderId} is ready!`;
await fetch('https://api.notifyhub.com/notifications/send', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
type: 'sms',
to: contact.phone,
message
})
});
}
}Next Steps:
- Campaigns - Schedule and automate bulk messaging
- Message Logs - Track delivery and analytics
- Credential Management - Configure channel credentials