📦 Node.js SDK
The official Sendly Node.js SDK provides a TypeScript-first, developer-friendly interface for the Sendly SMS API. Featuring automatic retries, comprehensive error handling, and full TypeScript support.
Installation
- npm
 - yarn
 - pnpm
 
npm install @sendly/node
yarn add @sendly/node
pnpm add @sendly/node
Quick Start
import { SendlyClient } from '@sendly/node';
const sendly = new SendlyClient({
  apiKey: 'sl_live_YOUR_API_KEY'
});
// Send your first SMS
const result = await sendly.sms.send({
  to: '+14155552671',
  text: 'Hello from Sendly! 🚀'
});
console.log(`Message sent: ${result.messageId}`);
TypeScript Support
The SDK is built with TypeScript and includes complete type definitions:
import { SendlyClient, SMSResponse, SMSRequest } from '@sendly/node';
const sendly = new SendlyClient({
  apiKey: process.env.SENDLY_API_KEY!
});
const request: SMSRequest = {
  to: '+14155552671',
  text: 'Hello from TypeScript!',
  messageType: 'transactional'
};
const response: SMSResponse = await sendly.sms.send(request);
API Reference
SendlyClient
The main client class for interacting with the Sendly API.
Constructor
new SendlyClient(options: SendlyClientOptions)
Parameters:
| Parameter | Type | Required | Description | 
|---|---|---|---|
apiKey | string | ✅ | Your Sendly API key | 
baseUrl | string | ❌ | API base URL (default: https://sendly.live/api) | 
timeout | number | ❌ | Request timeout in ms (default: 30000) | 
maxRetries | number | ❌ | Max retry attempts (default: 3) | 
userAgent | string | ❌ | Custom user agent | 
Example:
- Basic
 - Advanced
 - Environment
 
const sendly = new SendlyClient({
  apiKey: 'sl_live_YOUR_API_KEY'
});
const sendly = new SendlyClient({
  apiKey: process.env.SENDLY_API_KEY,
  baseUrl: 'https://api.sendly.dev', // For testing
  timeout: 60000, // 60 seconds
  maxRetries: 5,
  userAgent: 'MyApp/1.0.0'
});
// Automatically loads from SENDLY_API_KEY environment variable
const sendly = new SendlyClient();
// With custom environment variable
const sendly = new SendlyClient({
  apiKey: process.env.CUSTOM_SENDLY_KEY
});
SMS Methods
sendly.sms.send()
Send an SMS or MMS message.
send(request: SMSRequest): Promise<SMSResponse>
Parameters:
| Parameter | Type | Required | Description | 
|---|---|---|---|
to | string | ✅ | Destination phone number (E.164 format) | 
text | string | ❌* | Message text | 
from | string | ❌ | Sender phone number (auto-selected if not provided) | 
messageType | string | ❌ | Message type for routing priority | 
mediaUrls | string[] | ❌ | HTTPS URLs for MMS media (max 10) | 
subject | string | ❌ | MMS subject line | 
webhookUrl | string | ❌ | HTTPS webhook URL for delivery notifications | 
webhookFailoverUrl | string | ❌ | HTTPS backup webhook URL | 
tags | string[] | ❌ | Message tags for analytics (max 20, 50 chars each) | 
*Either text or mediaUrls must be provided.
Message Types:
transactional- Order confirmations, receipts, account notifications (highest priority)otp- One-time passwords, verification codes (high priority)marketing- Promotional messages, newsletters (standard routing)alert- System alerts, emergency notifications (priority routing)promotional- Sales, offers, announcements (standard routing)
Examples:
- Basic SMS
 - Transactional
 - OTP/Verification
 - MMS with Media
 - With Webhooks
 
const result = await sendly.sms.send({
  to: '+14155552671',
  text: 'Hello from Sendly!'
});
console.log('Message ID:', result.messageId);
console.log('Status:', result.status);
console.log('Cost:', `$${result.cost.amount} ${result.cost.currency}`);
const result = await sendly.sms.send({
  to: '+14155552671',
  text: 'Your order #12345 has been shipped!',
  messageType: 'transactional',
  tags: ['order', 'shipping', 'customer-123']
});
const result = await sendly.sms.send({
  to: '+14155552671',
  text: 'Your verification code is: 123456',
  messageType: 'otp',
  from: '+18005551234' // Specific sender number
});
const result = await sendly.sms.send({
  to: '+14155552671',
  text: 'Check out these amazing photos!',
  mediaUrls: [
    'https://example.com/photo1.jpg',
    'https://example.com/photo2.png'
  ],
  subject: 'Your Photos',
  messageType: 'marketing'
});
const result = await sendly.sms.send({
  to: '+14155552671',
  text: 'Hello with delivery tracking!',
  webhookUrl: 'https://your-app.com/webhook/sms',
  webhookFailoverUrl: 'https://your-app.com/webhook/backup'
});
sendly.sms.get()
Retrieve message status and details.
get(messageId: string): Promise<SMSResponse>
Example:
const message = await sendly.sms.get('msg_2cY8vH8LkQx3J5oP');
console.log('Status:', message.status);
console.log('Delivered at:', message.deliveredAt);
console.log('Delivery attempts:', message.deliveryAttempts);
sendly.sms.list()
List messages with filtering and pagination.
list(options?: ListOptions): Promise<MessageListResponse>
Parameters:
| Parameter | Type | Description | 
|---|---|---|
limit | number | Number of messages to return (max 100) | 
cursor | string | Cursor for pagination | 
status | string | Filter by message status | 
to | string | Filter by destination number | 
from | string | Filter by sender number | 
dateFrom | string | Filter messages from date (ISO 8601) | 
dateTo | string | Filter messages to date (ISO 8601) | 
Example:
const messages = await sendly.sms.list({
  limit: 50,
  status: 'delivered',
  dateFrom: '2023-10-01T00:00:00Z'
});
for (const message of messages.data) {
  console.log(`${message.messageId}: ${message.status}`);
}
// Handle pagination
if (messages.pagination.hasMore) {
  const nextPage = await sendly.sms.list({
    cursor: messages.pagination.nextCursor
  });
}
Response Types
SMSResponse
interface SMSResponse {
  messageId: string;              // Unique message identifier
  status: MessageStatus;          // Message status
  from: string;                   // Sender phone number
  to: string;                     // Destination phone number
  text?: string;                  // Message text
  createdAt: string;              // ISO 8601 timestamp
  updatedAt: string;              // ISO 8601 timestamp
  deliveredAt?: string;           // ISO 8601 timestamp
  failedAt?: string;              // ISO 8601 timestamp
  segments: number;               // Number of SMS segments
  cost: CostInfo;                 // Cost information
  direction: 'outbound';          // Message direction
  routing: RoutingInfo;           // Routing information
  messageType?: MessageType;      // Message type
  mediaType?: string;             // MMS media type
  mediaUrls?: string[];           // MMS media URLs
  subject?: string;               // MMS subject
  webhookUrl?: string;            // Webhook URL
  webhookFailoverUrl?: string;    // Backup webhook URL
  tags?: string[];                // Message tags
  carrier?: string;               // Carrier name
  lineType?: string;              // Line type (mobile, landline, voip)
  deliveryAttempts?: number;      // Number of delivery attempts
  failureReason?: string;         // Failure reason if failed
}
Message Status Values
| Status | Description | 
|---|---|
queued | Message queued for delivery | 
sent | Message sent to carrier | 
delivered | Message delivered to device | 
failed | Message delivery failed | 
undelivered | Message not delivered (temporary) | 
Error Handling
The SDK provides comprehensive error handling with specific error types:
import { 
  SendlyClient, 
  SendlyError, 
  ValidationError, 
  AuthenticationError, 
  RateLimitError 
} from '@sendly/node';
const sendly = new SendlyClient({
  apiKey: 'sl_live_YOUR_API_KEY'
});
try {
  const result = await sendly.sms.send({
    to: '+14155552671',
    text: 'Hello world!'
  });
  
  console.log('Success:', result.messageId);
  
} catch (error) {
  if (error instanceof ValidationError) {
    console.error('Validation error:', error.message);
    console.error('Field:', error.field);
    
  } else if (error instanceof AuthenticationError) {
    console.error('Authentication error:', error.message);
    
  } else if (error instanceof RateLimitError) {
    console.error('Rate limited:', error.message);
    console.error('Retry after:', error.retryAfter, 'seconds');
    
  } else if (error instanceof SendlyError) {
    console.error('API error:', error.message);
    console.error('Status code:', error.statusCode);
    console.error('Error code:', error.code);
    
  } else {
    console.error('Network error:', error.message);
  }
}
Error Types
| Error Type | HTTP Code | Description | 
|---|---|---|
ValidationError | 400 | Invalid request parameters | 
AuthenticationError | 401 | Invalid or missing API key | 
RateLimitError | 429 | Rate limit exceeded | 
SendlyError | 4xx/5xx | General API errors | 
NetworkError | - | Network connectivity issues | 
Advanced Features
Automatic Retries
The SDK automatically retries failed requests with exponential backoff:
const sendly = new SendlyClient({
  apiKey: 'sl_live_YOUR_API_KEY',
  maxRetries: 5,           // Max retry attempts
  retryDelay: 1000,        // Base delay in ms
  retryMultiplier: 2       // Exponential backoff multiplier
});
Webhook Verification
Verify webhook signatures for security:
import { SendlyClient } from '@sendly/node';
// In your webhook handler
function verifyWebhook(payload, signature, secret) {
  return SendlyClient.verifyWebhookSignature(payload, signature, secret);
}
// Express.js example
app.post('/webhook', (req, res) => {
  const signature = req.headers['x-sendly-signature'];
  const payload = JSON.stringify(req.body);
  
  if (!verifyWebhook(payload, signature, process.env.SENDLY_WEBHOOK_SECRET)) {
    return res.status(401).send('Invalid signature');
  }
  
  const { messageId, status, to } = req.body;
  console.log(`Message ${messageId} to ${to}: ${status}`);
  
  res.status(200).send('OK');
});
Custom User Agent
Set a custom user agent for tracking:
const sendly = new SendlyClient({
  apiKey: 'sl_live_YOUR_API_KEY',
  userAgent: 'MyApp/1.0.0 (contact@myapp.com)'
});
Environment Configuration
Use environment variables for configuration:
SENDLY_API_KEY=sl_live_YOUR_API_KEY
SENDLY_BASE_URL=https://sendly.live/api
SENDLY_WEBHOOK_SECRET=whsec_YOUR_WEBHOOK_SECRET
import dotenv from 'dotenv';
dotenv.config();
const sendly = new SendlyClient({
  apiKey: process.env.SENDLY_API_KEY,
  baseUrl: process.env.SENDLY_BASE_URL
});
Examples
E-commerce Order Confirmation
async function sendOrderConfirmation(orderId, customerPhone, total) {
  try {
    const result = await sendly.sms.send({
      to: customerPhone,
      text: `Order #${orderId} confirmed! Total: $${total}. Track: https://shop.com/track/${orderId}`,
      messageType: 'transactional',
      tags: ['order', 'confirmation', `order-${orderId}`],
      webhookUrl: 'https://shop.com/webhook/sms'
    });
    
    console.log(`Order confirmation sent: ${result.messageId}`);
    return result.messageId;
    
  } catch (error) {
    console.error('Failed to send order confirmation:', error.message);
    throw error;
  }
}
Two-Factor Authentication
async function send2FACode(phone, code) {
  try {
    const result = await sendly.sms.send({
      to: phone,
      text: `Your verification code is: ${code}. Valid for 5 minutes.`,
      messageType: 'otp',
      tags: ['2fa', 'security']
    });
    
    return {
      messageId: result.messageId,
      cost: result.cost.amount
    };
    
  } catch (error) {
    if (error instanceof RateLimitError) {
      throw new Error(`Rate limited. Retry after ${error.retryAfter} seconds`);
    }
    throw error;
  }
}
Bulk Messaging
async function sendBulkMessages(recipients, messageTemplate) {
  const results = [];
  const batchSize = 10; // Process in batches
  
  for (let i = 0; i < recipients.length; i += batchSize) {
    const batch = recipients.slice(i, i + batchSize);
    
    const batchPromises = batch.map(async (recipient) => {
      try {
        const personalizedMessage = messageTemplate.replace('{name}', recipient.name);
        
        const result = await sendly.sms.send({
          to: recipient.phone,
          text: personalizedMessage,
          messageType: 'marketing',
          tags: ['bulk', 'campaign-2023']
        });
        
        return { 
          phone: recipient.phone, 
          status: 'sent', 
          messageId: result.messageId 
        };
        
      } catch (error) {
        return { 
          phone: recipient.phone, 
          status: 'failed', 
          error: error.message 
        };
      }
    });
    
    const batchResults = await Promise.all(batchPromises);
    results.push(...batchResults);
    
    // Rate limiting: wait between batches
    if (i + batchSize < recipients.length) {
      await new Promise(resolve => setTimeout(resolve, 1000)); // 1 second delay
    }
  }
  
  return results;
}
Testing
Using Test API Keys
// Use test API key for development
const sendly = new SendlyClient({
  apiKey: 'sl_test_YOUR_TEST_API_KEY' // No SMS sent, no charges
});
// Test messages will return success responses but won't be delivered
const result = await sendly.sms.send({
  to: '+14155552671',
  text: 'Test message - not delivered'
});
console.log('Test message ID:', result.messageId); // Returns fake ID
Unit Testing with Mocks
import { SendlyClient } from '@sendly/node';
jest.mock('@sendly/node');
describe('SMS Service', () => {
  let mockSendly;
  
  beforeEach(() => {
    mockSendly = {
      sms: {
        send: jest.fn()
      }
    };
    (SendlyClient as jest.Mock).mockImplementation(() => mockSendly);
  });
  
  test('should send SMS successfully', async () => {
    const mockResponse = {
      messageId: 'msg_test_123',
      status: 'queued',
      cost: { amount: 0.0075, currency: 'USD' }
    };
    
    mockSendly.sms.send.mockResolvedValue(mockResponse);
    
    const sendly = new SendlyClient({ apiKey: 'test-key' });
    const result = await sendly.sms.send({
      to: '+14155552671',
      text: 'Test message'
    });
    
    expect(result.messageId).toBe('msg_test_123');
    expect(mockSendly.sms.send).toHaveBeenCalledWith({
      to: '+14155552671',
      text: 'Test message'
    });
  });
});
Migration Guide
From Direct HTTP Requests
- Before (HTTP)
 - After (SDK)
 
import fetch from 'node-fetch';
const response = await fetch('https://sendly.live/api/v1/send', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer sl_live_YOUR_API_KEY',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    to: '+14155552671',
    text: 'Hello world!'
  })
});
const result = await response.json();
import { SendlyClient } from '@sendly/node';
const sendly = new SendlyClient({
  apiKey: 'sl_live_YOUR_API_KEY'
});
const result = await sendly.sms.send({
  to: '+14155552671',
  text: 'Hello world!'
});
Benefits of using the SDK:
- ✅ Automatic retries with exponential backoff
 - ✅ Type safety with TypeScript support
 - ✅ Comprehensive error handling
 - ✅ Built-in webhook signature verification
 - ✅ Rate limiting and connection pooling
 - ✅ Automatic request/response validation
 
Support
- 📧 Email Support - support@sendly.live
 - 💬 Discord Community - discord.gg/sendly
 - 🐛 GitHub Issues - github.com/sendly/sendly-node
 - 📖 API Reference - /api-reference
 
Next Steps
- 🐍 Python SDK - Official Python SDK
 - 🎮 API Playground - Test endpoints interactively
 - 🔗 Webhooks Guide - Real-time delivery notifications
 - 📚 Best Practices - Production tips and optimization