code examples

Sent logo
Sent TeamMay 3, 2025 / code examples / Article

How to Send SMS with Node.js and Express: MessageBird API Tutorial (2025)

Learn how to send SMS messages using Node.js, Express.js, and MessageBird API. Step-by-step tutorial with code examples, E.164 phone validation, error handling, and production deployment tips. Updated for 2025.

MessageBird SMS with Express.js: Complete Integration Guide

⚠️ Important Notes (Updated January 2025):

  1. SDK Maintenance Status: The messagebird npm package (v4.0.1) has not been updated since 2022 and is no longer actively maintained. Consider this when choosing an SMS provider for new projects.

  2. Bird Rebranding: MessageBird rebranded as "Bird" in February 2024. While the legacy API and SDK remain functional, the company now operates as Bird (bird.com) with updated APIs and services. Check Bird's API documentation for current offerings.

  3. Content Note: This guide covers MessageBird integration with Express.js. Despite the filename reference to Next.js and NextAuth, this article focuses on Express.js implementation.

  4. Express.js Version: This guide is compatible with Express.js 5.x (requires Node.js ≥18).

This comprehensive tutorial shows you how to send SMS messages programmatically using Node.js, Express.js, and the MessageBird SMS API. Whether you're building two-factor authentication, notification systems, or marketing campaigns, you'll learn everything from initial setup to production deployment.

By the end of this guide, you'll have a working Express API endpoint that sends SMS messages to any phone number worldwide using MessageBird's reliable messaging infrastructure.

Project Goals:

  • Create a Node.js Express server with SMS capabilities
  • Integrate the MessageBird Node.js SDK for SMS delivery
  • Securely manage API credentials using environment variables
  • Build a REST API endpoint to trigger SMS sending
  • Implement phone number validation and error handling
  • Provide guidance on testing and production deployment

Technology Stack:

  • Node.js: JavaScript runtime environment for server-side development
  • Express.js: Minimal and flexible Node.js web application framework for building REST APIs
  • MessageBird SMS API: Cloud-based SMS gateway providing reliable message delivery worldwide via the official Node.js SDK
  • dotenv: Secure environment variable management for API keys and credentials

System Architecture:

The system consists of a User/Client (e.g., using cURL or a frontend application) making an HTTP request to the Node.js/Express App's API endpoint (e.g., /send-sms). The Express application receives the request, validates the input (recipient number, message body), and uses the MessageBird Node.js SDK. The SDK, configured with the API key securely loaded from an .env file, handles authentication and communicates with the MessageBird API. The MessageBird API processes the request and sends the SMS message to the recipient's mobile phone.

Prerequisites:

  • Node.js and npm: Install Node.js 18 or higher (Express.js 5.x requires Node.js ≥18). Download Node.js
  • MessageBird Account: Sign up for a free account at MessageBird.com or bird.com
  • MessageBird API Key: Obtain your API key from the MessageBird Dashboard for authentication
    • Note: MessageBird provides both live and test API keys. Test keys validate your API integration (checking authentication and request structure) without sending actual SMS messages or incurring costs, but they won't result in a message arriving on a phone. This guide uses a live key to demonstrate the full sending process.
  • MessageBird Virtual Number: Purchase or use a trial virtual mobile number (VMN) capable of sending SMS from the MessageBird Dashboard – this will be your originator number
  • Basic Terminal/Command Line Knowledge: Familiarity with navigating directories and running commands
  • Code Editor: Such as VS Code, Sublime Text, or Atom

1. Setting Up Your Node.js SMS Project

Initialize your Node.js project and install the necessary dependencies for sending SMS messages.

  1. Create Project Directory: Open your terminal and create a new directory for your project, then navigate into it.

    bash
    mkdir node-messagebird-sms
    cd node-messagebird-sms
  2. Initialize Node.js Project: Create a package.json file to track your project's metadata and dependencies.

    bash
    npm init -y

    (The -y flag accepts the default settings)

  3. Install Dependencies: Install Express for the web server, the MessageBird SDK, and dotenv for managing environment variables.

    bash
    npm install express messagebird dotenv
  4. Create Project Files: Create the main application file and files for environment variables and Git ignore rules.

    bash
    touch index.js .env .gitignore
    • index.js: Main application code
    • .env: Stores sensitive information like API keys (configure later)
    • .gitignore: Specifies files Git should ignore (like .env and node_modules)
  5. Configure .gitignore: Open .gitignore and add these lines to prevent committing sensitive data and local dependencies:

    text
    # Dependencies
    node_modules/
    
    # Environment variables
    .env
    
    # Logs
    npm-debug.log*
    yarn-debug.log*
    yarn-error.log*
    *.log
    
    # Optional Editor directories
    .vscode
    .idea
  6. Set Up Basic Express Server (index.js): Open index.js and add the basic structure for an Express application.

    javascript
    // index.js
    require('dotenv').config(); // Load environment variables from .env file FIRST
    const express = require('express');
    
    const app = express();
    const PORT = process.env.PORT || 3000; // Use port from env var or default to 3000
    
    // Middleware to parse JSON request bodies
    app.use(express.json());
    
    // Simple root route
    app.get('/', (req, res) => {
      res.send('MessageBird SMS Sender API is running!');
    });
    
    // Start the server
    app.listen(PORT, () => {
      console.log(`Server listening on port ${PORT}`);
    });
    • Why dotenv first? Call require('dotenv').config() at the very top to ensure environment variables are loaded before any other code tries to access them.
    • Why express.json()? This middleware is crucial for parsing incoming requests with JSON payloads (which you'll use for your API endpoint).
  7. Run the Basic Server: Test if the basic setup works.

    bash
    node index.js

    You should see Server listening on port 3000 in your console. Open http://localhost:3000 in your browser to see the welcome message. Press Ctrl+C to stop the server.

2. Integrating the MessageBird SMS API

Configure the MessageBird SDK and obtain the necessary credentials for sending SMS.

  1. Obtain MessageBird API Key:

    • Log in to your MessageBird Dashboard
    • Navigate to the Developers section in the left sidebar
    • Click on the API access (REST) tab
    • If you don't have a key, click Add access key. Create or use a Live API Key if you intend to send real messages (refer to the Prerequisites note about test vs. live keys)
    • Copy the generated Live API Keytreat this key like a password and keep it secret
  2. Obtain MessageBird Virtual Number (Originator):

    • In the MessageBird Dashboard, navigate to Numbers in the sidebar
    • If you don't have a number, click Buy a number
    • Select your country and ensure the SMS capability is enabled
    • Choose a number and complete the purchase (or use a trial number if available)
    • Copy the full number in E.164 format (e.g., +12025550149, +442071838750) – this will be your sender ID (originator)
  3. Configure Environment Variables (.env): Open the .env file and add your API key and originator number. Replace the placeholder values with your actual credentials.

    bash
    # .env
    
    # Your LIVE MessageBird API Key
    MESSAGEBIRD_API_KEY=YOUR_LIVE_API_KEY_HERE
    
    # Your purchased MessageBird Virtual Number (in E.164 format)
    MESSAGEBIRD_ORIGINATOR_NUMBER=+12345678900
    • MESSAGEBIRD_API_KEY: The live access key copied from the MessageBird dashboard – authenticates your requests
    • MESSAGEBIRD_ORIGINATOR_NUMBER: The virtual number you purchased or obtained – the 'sender' number that recipients will see (subject to country regulations). Note: In some countries, using an alphanumeric sender ID (like MyCompany) is possible, but it often requires pre-registration and isn't universally supported. Using a virtual number is generally more reliable for programmatic sending.
  4. Initialize MessageBird Client (index.js): Modify index.js to initialize the MessageBird client using the API key from environment variables.

    javascript
    // index.js
    require('dotenv').config();
    const express = require('express');
    
    // --- Add MessageBird initialization ---
    const messagebirdApiKey = process.env.MESSAGEBIRD_API_KEY;
    if (!messagebirdApiKey) {
        console.error('FATAL ERROR: MESSAGEBIRD_API_KEY environment variable not set.');
        process.exit(1); // Exit if the key is missing
    }
    const messagebird = require('messagebird')(messagebirdApiKey);
    // --- End initialization ---
    
    const app = express();
    const PORT = process.env.PORT || 3000;
    
    app.use(express.json());
    
    // --- Add Originator check ---
    if (!process.env.MESSAGEBIRD_ORIGINATOR_NUMBER) {
        console.warn('WARNING: MESSAGEBIRD_ORIGINATOR_NUMBER environment variable not set. Sending might fail or use default "MessageBird".');
        // You might want to exit here too depending on requirements: // process.exit(1);
    }
    // --- End check ---
    
    app.get('/', (req, res) => {
      res.send('MessageBird SMS Sender API is running!');
    });
    
    // Placeholder for the SMS sending route (coming next)
    
    app.listen(PORT, () => {
      console.log(`Server listening on port ${PORT}`);
    });
    • Why check environment variables? This simple check prevents the application from starting without essential configuration, making debugging easier.

3. Building the SMS Sending API Endpoint

Create the API endpoint that handles requests to send SMS messages programmatically.

  1. Create the API Endpoint (/send-sms): Add a new POST route to index.js to handle SMS sending requests.

    javascript
    // index.js
    // ... (previous code: require statements, app init, middleware, checks) ...
    
    app.get('/', (req, res) => {
      res.send('MessageBird SMS Sender API is running!');
    });
    
    // --- Add SMS Sending Route ---
    app.post('/send-sms', (req, res) => {
        const { recipient, message } = req.body; // Extract recipient and message from request body
    
        // 1. Input Validation
        if (!recipient || !message) {
            console.warn('Send SMS failed: Missing recipient or message', { body: req.body });
            return res.status(400).json({ error: 'Missing required fields: recipient and message' });
        }
    
        // E.164 validation: Must start with '+' followed by country code (1-9) and 3-14 additional digits
        // Total length: 4-15 digits (including country code)
        // This matches MessageBird's required format
        if (!/^\+[1-9]{1}[0-9]{3,14}$/.test(recipient)) {
             console.warn('Send SMS failed: Invalid recipient format', { recipient });
             return res.status(400).json({ error: 'Invalid recipient format. Use E.164 format (e.g., +12025551234).' });
        }
    
        const originator = process.env.MESSAGEBIRD_ORIGINATOR_NUMBER || 'MessageBird'; // Use configured number or default
    
        // 2. Prepare Message Parameters
        const params = {
            originator: originator,
            recipients: [recipient], // Must be an array
            body: message,
        };
    
        // 3. Send Message via MessageBird SDK
        console.log(`Attempting to send SMS to ${recipient} from ${originator}`);
        messagebird.messages.create(params, (err, response) => {
            if (err) {
                // 4. Handle Errors
                console.error('MessageBird API Error:', err);
    
                let statusCode = 500;
                let errorMessage = 'Failed to send SMS due to an internal server error.';
                let errorCode = null;
                if (err.errors && err.errors.length > 0) {
                   // Try to get specific error details from MessageBird response
                   const firstError = err.errors[0];
                   errorCode = firstError.code;
                   errorMessage = `MessageBird Error ${errorCode}: ${firstError.description}`;
                   // Map common MessageBird error codes to HTTP status codes (selective mapping)
                   if ([2, 9, 21, 22].includes(errorCode)) statusCode = 400; // Bad request
                   if (errorCode === 7) statusCode = 402; // Payment Required
                   if (errorCode === 10) statusCode = 401; // Unauthorized
                } else if (err.statusCode) {
                    statusCode = err.statusCode;
                    errorMessage = `Network or Client Error: ${err.message || 'Failed to communicate with MessageBird API'}`;
                }
                return res.status(statusCode).json({ error: errorMessage, errorCode: errorCode, details: err.errors || err.message });
            }
    
            // 5. Handle Success
            console.log('MessageBird API Success:', response);
            const recipientInfo = response.recipients.items[0] || {};
            res.status(200).json({
                message: 'SMS submitted successfully!',
                details: {
                    id: response.id,
                    status: recipientInfo.status, // e.g., 'sent', 'scheduled'
                    statusDatetime: recipientInfo.statusDatetime,
                    recipient: recipientInfo.recipient,
                }
            });
        });
    });
    // --- End SMS Sending Route ---
    
    
    app.listen(PORT, () => {
      console.log(`Server listening on port ${PORT}`);
    });
    • Request Body: Expects a JSON body with recipient (E.164 format phone number) and message (the text content)
    • Input Validation: Basic checks ensure required fields are present and the recipient number has a valid format – crucial for security and preventing errors
    • Parameters: The params object maps directly to the requirements of the MessageBird messages.create function. recipients must be an array, even for a single number
    • messagebird.messages.create: The core SDK function – takes the parameters and a callback function
    • Callback Function: The callback receives err (if an error occurred during the API call) or response (on success)
    • Error Handling: If err exists, log it and return a relevant HTTP status code (e.g., 400 for bad input, 402 for balance issues, 500 for server/API errors) and error message. Attempt to parse specific errors from the MessageBird response
    • Success Handling: If successful, log the response and return a 200 status with details like the MessageBird message ID and initial status

4. Testing Your SMS API with cURL and Postman

Test the endpoint using a tool like curl or Postman to verify SMS sending functionality.

  1. Start the Server: Ensure your .env file is correctly configured with your live API key and originator number.

    bash
    node index.js
  2. Send a Test Request (using curl): Open a new terminal window (leave the server running in the first one). Replace +1YOUR_PHONE_NUMBER with your actual mobile number in E.164 format.

    bash
    curl -X POST http://localhost:3000/send-sms \
         -H "Content-Type: application/json" \
         -d '{
               "recipient": "+1YOUR_PHONE_NUMBER",
               "message": "Hello from Node.js and MessageBird!"
             }'
  3. Check the Response:

    • Success: You should receive a JSON response similar to this in your curl terminal, and the SMS should arrive on your phone shortly (standard SMS charges may apply).
      json
      {
        "message": "SMS submitted successfully!",
        "details": {
          "id": "mb-message-id-string",
          "status": "sent",
          "statusDatetime": "2023-10-27T10:30:00Z",
          "recipient": "+1YOUR_PHONE_NUMBER"
        }
      }
      Your server console (where node index.js is running) should also show the success logs.
    • Error: If something is wrong (e.g., invalid API key, incorrect recipient format, insufficient funds), you'll get an error response:
      json
      // Example: Invalid recipient format
      {
        "error": "Invalid recipient format. Use E.164 format (e.g., +12025551234)."
      }
      json
      // Example: MessageBird API error (e.g., bad key or insufficient balance)
      {
         "error": "MessageBird Error 7: No balance",
         "errorCode": 7,
         "details": [ /* ... detailed error object from MessageBird ... */ ]
      }
      Check the server console logs for more detailed error information.
  4. Testing with Postman:

    • Open Postman
    • Create a new request
    • Set the method to POST
    • Enter the URL: http://localhost:3000/send-sms
    • Go to the "Body" tab, select "raw", and choose "JSON" from the dropdown
    • Enter the JSON payload:
      json
      {
        "recipient": "+1YOUR_PHONE_NUMBER",
        "message": "Testing SMS via Postman!"
      }
    • Click "Send". Observe the response in Postman and the logs in your server console

5. Error Handling and Logging Best Practices

While basic error handling using console.log/console.error works for development, production systems need more robustness. This section introduces improvements incorporated into the final code (Section 13).

  • Consistent Error Format: Ensure all error responses follow a consistent JSON structure (e.g., { "error": "message", "errorCode": 123, "details": { ... } })
  • Structured Logging: Switch from console.log to a dedicated logging library like winston or pino. This provides significant advantages:
    • Different Log Levels: Distinguish between informational messages (info), warnings (warn), and critical errors (error)
    • Structured Output: Logging in JSON format makes logs easier to parse, query, and analyze by log management systems (like Datadog, Splunk, ELK stack)
    • Configurable Transports: Send logs to different destinations (console, files, external services) based on environment or severity
    • Example winston setup (as used in Section 13):
      bash
      npm install winston
      javascript
      // Example setup with Winston (replaces console.log/error)
      const winston = require('winston');
      const logger = winston.createLogger({
        level: 'info', // Log only info, warn, error by default
        format: winston.format.json(), // Log as JSON
        defaultMeta: { service: 'sms-sender' }, // Add service context
        transports: [
          // Write errors to error.log
          new winston.transports.File({ filename: 'error.log', level: 'error' }),
          // Write all logs (info and above) to combined.log
          new winston.transports.File({ filename: 'combined.log' }),
        ],
      });
      // Also log to the console in non-production environments
      if (process.env.NODE_ENV !== 'production') {
        logger.add(new winston.transports.Console({
          format: winston.format.simple(), // Use simpler format for console
        }));
      }
      
      // Usage (replaces console.log/error):
      // logger.error('MessageBird API Error:', { error: err });
      // logger.info('SMS submitted successfully', { recipient: recipient, messageId: response.id });
  • Retry Mechanisms: For critical messages where transient network errors are unacceptable, implement a retry strategy (e.g., using libraries like async-retry) with exponential backoff. This adds complexity but improves reliability

6. Database Schema and Data Layer

For this basic SMS sending API endpoint, a database is not strictly required – messages are sent based on direct API requests.

However, if you were building a more comprehensive application (e.g., tracking message history, handling replies, managing user preferences), you would need a database (like PostgreSQL, MongoDB, MySQL). This would involve:

  • Defining Schemas/Models: Structure for storing message details (ID, recipient, sender, body, status, timestamps), user data, etc.
  • Data Access Layer: Using an ORM/ODM (like Sequelize for SQL, Mongoose for MongoDB) or plain database drivers to interact with the database
  • Storing Message Status: Updating message status based on delivery reports received via MessageBird webhooks (a more advanced topic)

7. Security Features

Security is critical when building APIs that handle potentially sensitive information.

  • Environment Variables: Never commit your .env file or hardcode API keys/credentials in your source code. Use dotenv for local development and your deployment platform's secure environment variable management in production. Ensure .env is listed in your .gitignore
  • Input Validation: Rigorously validate all input from external sources (the request body in this case)
    • Recipient: Ensure it adheres to the E.164 format. The regex /^\+[1-9]{1}[0-9]{3,14}$/ validates the format properly – it requires a + sign, followed by a country code digit (1–9), and 3–14 additional digits, for a total length of 4–15 digits. For maximum accuracy, consider libraries like google-libphonenumber (though potentially overkill for a basic service)
    • Message Body: Check for reasonable length. SMS messages have character limits (typically 160 GSM-7 characters, 70 Unicode characters per part). Very long messages are split and cost more. Reject empty or excessively long messages. Sanitize content if it could contain malicious input, especially if derived from user-generated content elsewhere in your system
  • Rate Limiting: Protect your API endpoint (and your MessageBird budget) from abuse (accidental or malicious). Use middleware like express-rate-limit to limit the number of requests from a single IP address within a time window. This is implemented in the final code (Section 13)
    bash
    npm install express-rate-limit
    javascript
    // Example implementation (see Section 13 for full context)
    const rateLimit = require('express-rate-limit');
    const smsLimiter = rateLimit({
        windowMs: 15 * 60 * 1000, // 15 minutes
        max: 10, // Limit each IP to 10 requests per window
        message: { error: 'Too many SMS requests created from this IP, please try again after 15 minutes' }
    });
    app.post('/send-sms', smsLimiter, /* route handler */ );
  • Authentication/Authorization (If Applicable): If this API is not intended for public use, secure it. Ensure only authenticated and authorized clients (users, other services) can call the /send-sms endpoint. Methods include API keys specific to your service, JWT tokens, session authentication, OAuth, etc.
  • HTTPS: Always deploy your application behind a proxy that terminates SSL/TLS, ensuring traffic between clients and your server is encrypted (HTTPS). Most modern hosting platforms handle this automatically

8. SMS Delivery Best Practices and Special Cases

  • Originator Restrictions: Alphanumeric sender IDs (e.g., "MyCompany") are not supported in all countries (like the US and Canada) and often require pre-registration. Using a purchased virtual number (in E.164 format) as the originator is generally the most reliable approach for global sending. Always check Bird's country restrictions documentation (formerly MessageBird) for current regulations
  • Character Limits & Encoding: Standard SMS (using GSM-7 encoding) allows 160 characters. Using characters outside this set (like many emojis or non-Latin scripts) forces Unicode (UCS-2) encoding, reducing the limit to 70 characters per SMS part. MessageBird automatically handles splitting longer messages into multiple parts (concatenated SMS), but you are billed for each part. Inform users or truncate messages appropriately if length/cost is a concern
  • Invalid or Non-Mobile Numbers: The E.164 validation helps, but MessageBird's API will also perform checks. An invalid or non-existent number will result in an API error (often code: 2 – invalid parameter) or a failed delivery status later
  • Blocked Numbers/Opt-outs: Implement mechanisms to handle user opt-outs (e.g., responding to "STOP" messages, maintaining a suppression list). Sending to users who have opted out can violate regulations (like TCPA in the US). MessageBird offers features to help manage opt-outs

9. Performance Optimizations

For this specific function (a single API call per request), performance bottlenecks within the Node.js code itself are less likely than external factors.

  • Asynchronous Nature: Node.js is non-blocking. The messagebird.messages.create call is asynchronous, meaning your server isn't stalled waiting for MessageBird's response and can handle other requests concurrently. This is inherently performant for I/O-bound tasks
  • External API Latency: The primary performance factor will be the network latency and processing time of the MessageBird API itself
  • Load Testing: If high throughput is expected, use load testing tools (e.g., k6, artillery, autocannon) to simulate traffic. This helps identify potential limits (CPU, memory, network bandwidth) or issues with dependencies (like rate limits)
  • Caching: Not typically applicable for sending unique SMS messages. Caching might be relevant if you were frequently fetching message statuses via API calls, but using MessageBird's webhooks for status updates is generally more efficient

10. Production Monitoring and Observability

To ensure your service runs reliably in production:

  • Health Checks: Implement a simple /health endpoint that returns a 200 OK status. Monitoring systems (like uptime checkers, Kubernetes liveness probes) can ping this endpoint to verify the service is operational (added in Section 13)
    javascript
    // Example health check endpoint
    app.get('/health', (req, res) => {
      res.status(200).json({ status: 'UP', timestamp: new Date().toISOString() });
    });
  • Application Performance Monitoring (APM): Use APM tools (e.g., Datadog, New Relic, Dynatrace, Sentry APM, OpenTelemetry) to monitor key metrics like request latency, throughput (requests per minute), error rates, CPU/memory usage. These tools help diagnose performance issues and bottlenecks
  • Structured Logging: As discussed in Section 5, use structured logging (e.g., Winston sending JSON to a log aggregator) for effective analysis and troubleshooting
  • Error Tracking: Integrate dedicated error tracking services (Sentry, Bugsnag, Rollbar) to capture, aggregate, alert on, and provide context for runtime exceptions in your Node.js application
  • MessageBird Dashboard: Regularly utilize the MessageBird Dashboard ("Messaging → Logs"). It provides invaluable insights into the status of each message (e.g., sent, delivered, failed), delivery timestamps, error codes if failures occur, and associated costs. This is crucial for debugging specific SMS delivery problems

11. Troubleshooting and Caveats

Common issues and things to be aware of:

  • Error: Authentication failed or Request not allowed (incorrect login details…) (Code 2, 10, 21):
    • Cause: Invalid MESSAGEBIRD_API_KEY in your environment variables. It might be mistyped, have extra whitespace, be a test key when a live one is needed, or belong to a different account
    • Solution: Double-check the API key in your .env file (or production environment variables) against the key shown in the MessageBird Dashboard (DevelopersAPI access). Ensure you are using the correct key type (live/test). Restart your application after making changes
  • Error: recipient is invalid or similar parameter errors (Code 2, 22):
    • Cause: The phone number provided in the recipient field is not in the valid E.164 format (+ sign followed by country code and number, no spaces or dashes)
    • Solution: Verify the input number format. Ensure your validation logic (regex or library) correctly enforces E.164. Check the data being sent in the request body
  • Error: originator is invalid (Code 2, 21):
    • Cause: The MESSAGEBIRD_ORIGINATOR_NUMBER in your environment variables is incorrect, not associated with your MessageBird account, formatted incorrectly, or disallowed as a sender ID in the destination country
    • Solution: Confirm the number matches a number listed under Numbers in your MessageBird Dashboard and is in E.164 format. If using an alphanumeric ID, check country regulations and registration status
  • Error: No balance (Code 7):
    • Cause: Your MessageBird account lacks sufficient credits to cover the cost of the SMS message
    • Solution: Add funds to your account through the MessageBird Dashboard (Billing section)
  • SMS Not Received by Recipient:
    • Cause: Can be varied: transient carrier delays, recipient's phone is off/out of service, incorrect number provided, carrier spam filtering, number is blocked, MessageBird platform issues (rare)
    • Solution:
      1. Verify the recipient number is absolutely correct
      2. Check the MessageBird Dashboard logs (MessagingLogs) for that specific message. Look at its status (delivered, failed, expired) and any error details provided
      3. Wait a few minutes – delivery isn't always instantaneous
      4. Test sending to a different number or carrier if possible to isolate the issue
      5. If logs show delivered but the user insists they didn't receive it, the issue likely lies with the recipient's device or carrier
      6. If logs show failed with an error code, investigate that specific code
      7. Contact MessageBird support if issues persist despite logs showing sent or if you suspect a platform problem
  • Caveat: Cost: Sending SMS messages incurs costs based on the destination country and message volume. Monitor your MessageBird balance and be aware of pricing. Test keys are free but don't send real messages
  • Caveat: Delivery Status: The API response provides an initial status (like sent or scheduled). The final delivery confirmation (delivered or failed) happens asynchronously. To get these final statuses reliably, you need to implement webhooks to receive Delivery Reports from MessageBird (see "Next Steps"). The code examples provided map common API call errors, but a production system might need more comprehensive mapping or handling logic based on MessageBird's documentation

12. Deployment and CI/CD

To deploy your Node.js Express application:

  1. Choose a Hosting Platform: Many options exist, including:
    • PaaS (Platform-as-a-Service): Heroku, Render, Fly.io (easy deployment from Git)
    • Serverless: AWS Lambda, Google Cloud Functions, Vercel (often require framework adaptations)
    • Containers: AWS Fargate/ECS, Google Cloud Run, Azure Container Apps (using Docker)
    • VPS/Servers: AWS EC2, Google Compute Engine, DigitalOcean Droplets (require more manual setup)
  2. Prepare package.json: Ensure your scripts include a start command
    json
    // package.json excerpt
    {
      "scripts": {
        "start": "node index.js",
        "test": "echo \"Error: no test specified\" && exit 1"
      }
    }
  3. Environment Variables: Configure MESSAGEBIRD_API_KEY and MESSAGEBIRD_ORIGINATOR_NUMBER securely within your chosen platform's settings. Never commit the .env file to Git. Also set NODE_ENV=production for performance and security benefits
  4. CI/CD Pipeline (Optional but Recommended): Automate testing, building, and deployment:
    • Use services like GitHub Actions, GitLab CI/CD, CircleCI, or Jenkins
    • Define workflows to run tests (npm test) and deploy on successful builds to your hosting platform
    • Example GitHub Actions workflow (.github/workflows/deploy.yml):
      yaml
      name: Deploy to Production
      on:
        push:
          branches: [main]
      jobs:
        deploy:
          runs-on: ubuntu-latest
          steps:
            - uses: actions/checkout@v3
            - uses: actions/setup-node@v3
              with:
                node-version: '18'
            - run: npm ci
            - run: npm test
            # Add your deployment steps here (e.g., Heroku deploy, AWS deploy)
  5. Health Check Endpoint: Ensure your application has a /health endpoint (see Section 10) that monitoring systems can use to verify service availability

13. Complete Production-Ready Code

Here's the full index.js with all best practices integrated:

javascript
// index.js - Production-Ready Version
require('dotenv').config();
const express = require('express');
const rateLimit = require('express-rate-limit');

// Initialize MessageBird
const messagebirdApiKey = process.env.MESSAGEBIRD_API_KEY;
if (!messagebirdApiKey) {
    console.error('FATAL ERROR: MESSAGEBIRD_API_KEY environment variable not set.');
    process.exit(1);
}
const messagebird = require('messagebird')(messagebirdApiKey);

const app = express();
const PORT = process.env.PORT || 3000;

// Middleware
app.use(express.json());

// Rate limiting for SMS endpoint
const smsLimiter = rateLimit({
    windowMs: 15 * 60 * 1000, // 15 minutes
    max: 10, // Limit each IP to 10 requests per window
    message: { error: 'Too many SMS requests from this IP, please try again after 15 minutes' },
    standardHeaders: true,
    legacyHeaders: false,
});

// Validation checks
if (!process.env.MESSAGEBIRD_ORIGINATOR_NUMBER) {
    console.warn('WARNING: MESSAGEBIRD_ORIGINATOR_NUMBER not set. SMS sending may fail.');
}

// Health check endpoint
app.get('/health', (req, res) => {
    res.status(200).json({
        status: 'UP',
        timestamp: new Date().toISOString(),
        service: 'messagebird-sms-api'
    });
});

// Root endpoint
app.get('/', (req, res) => {
    res.json({
        message: 'MessageBird SMS API',
        version: '1.0.0',
        endpoints: {
            health: '/health',
            sendSms: 'POST /send-sms'
        }
    });
});

// SMS sending endpoint with rate limiting
app.post('/send-sms', smsLimiter, (req, res) => {
    const { recipient, message } = req.body;

    // Input validation
    if (!recipient || !message) {
        console.warn('Send SMS failed: Missing fields', {
            hasRecipient: !!recipient,
            hasMessage: !!message
        });
        return res.status(400).json({
            error: 'Missing required fields: recipient and message'
        });
    }

    // E.164 format validation
    if (!/^\+[1-9]{1}[0-9]{3,14}$/.test(recipient)) {
        console.warn('Send SMS failed: Invalid recipient format', { recipient });
        return res.status(400).json({
            error: 'Invalid recipient format. Use E.164 format (e.g., +12025551234).'
        });
    }

    // Message length check (warn if > 160 chars)
    if (message.length > 160) {
        console.warn('Long message detected', {
            length: message.length,
            segments: Math.ceil(message.length / 160)
        });
    }

    const originator = process.env.MESSAGEBIRD_ORIGINATOR_NUMBER || 'MessageBird';

    const params = {
        originator: originator,
        recipients: [recipient],
        body: message,
    };

    console.log(`Sending SMS to ${recipient} from ${originator}`);

    messagebird.messages.create(params, (err, response) => {
        if (err) {
            console.error('MessageBird API Error:', {
                errors: err.errors,
                statusCode: err.statusCode
            });

            let statusCode = 500;
            let errorMessage = 'Failed to send SMS due to an internal server error.';
            let errorCode = null;

            if (err.errors && err.errors.length > 0) {
                const firstError = err.errors[0];
                errorCode = firstError.code;
                errorMessage = `MessageBird Error ${errorCode}: ${firstError.description}`;

                // Map error codes to HTTP status codes
                if ([2, 9, 21, 22].includes(errorCode)) statusCode = 400;
                if (errorCode === 7) statusCode = 402;
                if (errorCode === 10) statusCode = 401;
            } else if (err.statusCode) {
                statusCode = err.statusCode;
                errorMessage = `Network error: ${err.message || 'Failed to communicate with MessageBird API'}`;
            }

            return res.status(statusCode).json({
                error: errorMessage,
                errorCode: errorCode,
                details: err.errors || err.message
            });
        }

        console.log('SMS sent successfully:', {
            messageId: response.id,
            recipient: recipient,
            status: response.recipients.items[0]?.status
        });

        const recipientInfo = response.recipients.items[0] || {};
        res.status(200).json({
            message: 'SMS submitted successfully!',
            details: {
                id: response.id,
                status: recipientInfo.status,
                statusDatetime: recipientInfo.statusDatetime,
                recipient: recipientInfo.recipient,
            }
        });
    });
});

// 404 handler
app.use((req, res) => {
    res.status(404).json({ error: 'Endpoint not found' });
});

// Error handler
app.use((err, req, res, next) => {
    console.error('Unhandled error:', err);
    res.status(500).json({ error: 'Internal server error' });
});

// Start server
app.listen(PORT, () => {
    console.log(`MessageBird SMS API listening on port ${PORT}`);
    console.log(`Environment: ${process.env.NODE_ENV || 'development'}`);
});

14. Next Steps and Advanced Features

To extend your SMS application beyond basic sending:

  • Delivery Reports (DLRs) via Webhooks: Implement webhook endpoints to receive real-time delivery status updates from MessageBird. This allows you to track when messages are delivered, failed, or expired
  • Inbound SMS Handling: Set up webhooks to receive incoming SMS messages, enabling two-way communication (e.g., for customer support, surveys, or STOP/opt-out processing)
  • Message Scheduling: Schedule SMS messages to be sent at specific times using MessageBird's scheduling parameters
  • Bulk SMS Sending: Optimize for sending large volumes of messages by batching requests or using MessageBird's bulk sending features
  • Database Integration: Store message history, track delivery statuses, and manage contact lists using a database (PostgreSQL, MongoDB, etc.)
  • Template Management: Create reusable message templates with variable substitution for personalized bulk messaging
  • Multi-Channel Messaging: Extend beyond SMS to include WhatsApp, Voice, or Email using MessageBird's (Bird's) unified API
  • A/B Testing: Test different message variations to optimize engagement and conversion rates
  • Compliance Management: Implement opt-in/opt-out workflows, maintain suppression lists, and ensure GDPR/TCPA compliance

Ready to send SMS with Node.js? Follow this tutorial to build your own production-ready SMS API using Express.js and MessageBird. Whether you're implementing authentication codes, notifications, or marketing campaigns, this comprehensive guide covers everything you need to know.

Frequently Asked Questions

How to send SMS with Node.js and Express

Set up an Express server, install the MessageBird SDK and dotenv, configure your .env file with API keys, and create a /send-sms endpoint to handle requests. This endpoint will use the MessageBird SDK to send messages based on the provided recipient and message body. The article provides a detailed walkthrough of this process.

What is MessageBird used for in Node.js

MessageBird is a messaging platform that provides APIs for various communication channels, including SMS. Its Node.js SDK simplifies the process of integrating SMS functionality into your Node.js and Express applications. You'll use their REST API via the official Node.js SDK.

Why does dotenv matter for MessageBird integration

Dotenv is essential for securely managing your MessageBird API keys. It loads environment variables from a .env file, preventing you from hardcoding sensitive credentials directly into your application code, thus enhancing security.

When should I validate recipient phone numbers

Always validate recipient phone numbers upon receiving them in your /send-sms endpoint. This prevents sending messages to invalid numbers, which can result in errors or unnecessary charges. Use a regex or a specialized library like google-libphonenumber for comprehensive validation.

Can I use an alphanumeric sender ID with MessageBird

Alphanumeric sender IDs are not universally supported and may be restricted or require pre-registration in certain countries. For reliable global SMS sending, use a purchased virtual number as the originator, which is supported across more regions.

How to set up a MessageBird SMS endpoint in Express

Create a POST route (/send-sms) in your Express app that extracts recipient and message data from the request body. Validate the inputs, prepare the parameters, and then use messagebird.messages.create() to send the SMS through the MessageBird API.

What is the role of express.json() middleware

The express.json() middleware is crucial for parsing incoming request bodies in JSON format, allowing your Express application to access the recipient and message data submitted to the /send-sms endpoint.

How to handle MessageBird API errors in Node.js

Implement robust error handling in your /send-sms route's callback function. Check for errors returned by the MessageBird API and respond with appropriate HTTP status codes and informative error messages. Logging errors with details is also crucial for debugging.

Why use environment variables for API keys

Storing API keys in environment variables, managed by dotenv locally and through your deployment platform in production, ensures that sensitive credentials are not exposed in your codebase or version control system.

How to test the MessageBird SMS integration locally

Use tools like curl or Postman to send POST requests to your /send-sms endpoint with test data. Check the server console for logs and the response for success or error messages. Verify that the SMS arrives on the recipient's phone (if using a live API key).

When to use a logging library like Winston

For production applications, switch from console.log to a structured logging library like Winston. This allows for different log levels, JSON-formatted output for easier analysis, and configurable transports to send logs to various destinations.

What are some common MessageBird API error codes

Common errors include code 2 for invalid parameters (like recipient format or originator), code 7 for insufficient balance, and code 10 for authentication failures (invalid API key). Refer to the MessageBird API documentation for a complete list of error codes.

How to implement rate limiting for SMS sending

Use middleware like express-rate-limit to restrict the number of requests from a single IP address within a specified time window, preventing abuse and protecting your MessageBird budget. Configure the middleware with appropriate limits and error messages.

What security measures to consider for SMS API

Essential security measures include using environment variables for API keys, validating all user inputs, implementing rate limiting, enforcing HTTPS, and adding authentication/authorization if the API is not public.

How to troubleshoot SMS not received by recipient

Check the MessageBird Dashboard logs for message status and any error codes. Verify the recipient number, consider carrier delays, and contact MessageBird support if necessary.