code examples

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

Send SMS with Node.js, Express, and Vonage: Complete Developer Guide

Build a Node.js SMS application using Express and Vonage API. Includes setup, authentication, error handling, and production best practices.

Send SMS with Node.js, Express, and Vonage: A Developer Guide

Send SMS messages programmatically using Node.js, Express, and the Vonage SMS API. This comprehensive guide walks you through building a production-ready REST API endpoint that sends text messages to any phone number worldwide. Learn how to authenticate with Vonage credentials, handle API responses, implement error handling, and deploy a scalable SMS solution for notifications, alerts, and customer communications.

The Vonage SMS API (formerly Nexmo) provides reliable message delivery with detailed status tracking and global carrier coverage. Using the official @vonage/server-sdk package with Express framework, you'll create a /send-sms endpoint that accepts phone numbers and message content, validates input, and returns detailed delivery status. This tutorial covers environment variable security with dotenv, async/await patterns, response validation, and production deployment considerations.

Whether you're building two-factor authentication, appointment reminders, order confirmations, or marketing campaigns, this guide provides the foundation for integrating SMS capabilities into your Node.js applications. Estimated completion time: 20–25 minutes.

Project Overview and Goals

What We're Building:

A minimalist Node.js REST API with a single endpoint (/send-sms). This endpoint will accept a POST request containing a destination phone number and a message body. Upon receiving the request, the API will use the Vonage API to dispatch an SMS message to the specified recipient.

Problem Solved:

Provides a straightforward, server-side mechanism to programmatically send SMS messages, decoupling the SMS sending logic from other parts of an application or system.

Technologies Used:

  • Node.js: A JavaScript runtime environment for executing server-side code. Chosen for its asynchronous nature, large ecosystem (npm), and suitability for I/O-bound tasks like API interactions.
  • Express.js: A minimal and flexible Node.js web application framework. Chosen for its simplicity in setting up routes and handling HTTP requests, making it ideal for building APIs quickly.
  • Vonage SMS API: A service provided by Vonage for sending and receiving SMS messages globally. Chosen for its reliability and developer-friendly SDK.
  • @vonage/server-sdk: The official Vonage Node.js SDK for interacting with their APIs (v3 used in examples).
  • dotenv: A module to load environment variables from a .env file into process.env. Used for securely managing API credentials outside of the codebase.

System Architecture:

mermaid
graph LR
    A[Client / API Consumer] -- POST /send-sms --> B(Node.js / Express API);
    B -- vonage.sms.send() --> C(Vonage SMS API);
    C -- SMS Network --> D(Recipient's Phone);
    C -- Response --> B;
    B -- JSON Response --> A;

Prerequisites:

  • Node.js and npm: Installed on your development machine. (Download Node.js)
  • Vonage API Account: A free or paid account is required. (Sign up for Vonage)
  • Vonage API Key and Secret: Found in your Vonage Dashboard.
  • Vonage Phone Number: A virtual number rented from Vonage or the ability to add verified test numbers (for trial accounts).
  • Basic Command Line/Terminal Knowledge: Familiarity with navigating directories and running commands.
  • (Optional) API Testing Tool: Such as Postman or curl for testing the endpoint.

1. Setting up the Project

Let's initialize our Node.js project and install the necessary dependencies.

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

    bash
    mkdir vonage-sms-guide
    cd vonage-sms-guide
  2. Initialize Node.js Project: This command creates a package.json file, which tracks project metadata and dependencies. The -y flag accepts the default settings.

    bash
    npm init -y
  3. Install Dependencies: We need Express for the web server, the Vonage SDK to interact with their API, and dotenv to manage environment variables.

    bash
    npm install express @vonage/server-sdk dotenv

    Note: This installs the latest stable versions. Check compatibility if needed.

  4. Enable ES Modules (Optional but Recommended): Using ES Module syntax (import/export) is modern practice. Open your package.json file and add the following line at the top level:

    json
    {
      "name": "vonage-sms-guide",
      "version": "1.0.0",
      "description": "",
      "main": "index.js",
      "type": "module",
      "scripts": {
        "start": "node index.js",
        "test": "echo \"Error: no test specified\" && exit 1"
      },
      "keywords": [],
      "author": "",
      "license": "ISC",
      "dependencies": {
        "@vonage/server-sdk": "^3.14.0",
        "dotenv": "^16.4.5",
        "express": "^4.19.2"
      }
    }
    • Why type: "module"? This tells Node.js to interpret .js files using ES Module syntax instead of the older CommonJS (require/module.exports) syntax, aligning with modern JavaScript standards.
    • Dependency Versions: The versions listed are examples. npm install will fetch the latest compatible versions based on semantic versioning. For specific compatibility, you might pin versions, but generally, using the latest stable releases is recommended.

SDK and Framework Requirements (as of January 2025):

  • @vonage/server-sdk: Official Vonage Node.js SDK supporting async/await promises. The SMS API is in General Availability status. (npm: @vonage/server-sdk, accessed January 2025)
  • Express: Fast, unopinionated, minimalist web framework. Requires Node.js 18 or higher for latest versions. (npm: express, accessed January 2025)
  • dotenv: Zero-dependency module that loads environment variables from .env files into process.env. (npm: dotenv, accessed January 2025)

Recommended Node.js Version: Use Node.js 18 LTS or later for optimal compatibility with Express and modern JavaScript features.

  1. Create Core Files: Create the main application file and files for environment variables and Git ignore rules.

    bash
    touch index.js .env .gitignore
    • index.js: Our main application code.
    • .env: Stores sensitive credentials like API keys (will not be committed to Git).
    • .gitignore: Specifies files and directories that Git should ignore.
  2. Configure .gitignore: Add node_modules and .env to your .gitignore file to prevent committing dependencies and sensitive credentials.

    plaintext
    # .gitignore
    
    # Dependencies
    node_modules/
    
    # Environment variables
    .env
    
    # Logs
    logs
    *.log
    npm-debug.log*
    yarn-debug.log*
    yarn-error.log*
    pnpm-debug.log*
    
    # Optional Editor directories and files
    .idea
    .vscode
    *.suo
    *.ntvs*
    *.njsproj
    *.sln
    *.sw?
    • Why ignore node_modules? This directory contains downloaded dependencies and can be very large. It should be regenerated using npm install.
    • Why ignore .env? This file contains sensitive API keys and secrets and should never be committed to version control.

2. Implementing Core Functionality (SMS Sending Logic)

We'll integrate the Vonage SDK to handle the actual SMS sending.

  1. Import Dependencies: Open index.js and import the required modules.

    javascript
    // index.js
    import express from 'express';
    import { Vonage } from '@vonage/server-sdk';
    import 'dotenv/config'; // Loads .env variables into process.env
    • Why dotenv/config? Importing this module automatically loads the variables defined in your .env file into process.env, making them accessible throughout your application. It's crucial to import it early.
  2. Initialize Vonage SDK: Create an instance of the Vonage client using credentials loaded from environment variables.

    javascript
    // index.js (continued)
    
    // Input validation (basic): Ensure required env variables are set
    if (!process.env.VONAGE_API_KEY || !process.env.VONAGE_API_SECRET || !process.env.VONAGE_FROM_NUMBER) {
      console.error('Error: Missing required Vonage environment variables (VONAGE_API_KEY, VONAGE_API_SECRET, VONAGE_FROM_NUMBER).');
      process.exit(1); // Exit if essential config is missing
    }
    
    const vonage = new Vonage({
      apiKey: process.env.VONAGE_API_KEY,
      apiSecret: process.env.VONAGE_API_SECRET
    });
    
    const fromNumber = process.env.VONAGE_FROM_NUMBER;
    • Why initialize outside the route? Creating the Vonage client instance once when the application starts is more efficient than creating it for every incoming request. Node.js module scope handles this naturally.
  3. Create the SMS Sending Function: While we could put the logic directly in the route, let's define an asynchronous function for clarity. We'll use the vonage.sms.send() method provided by the SDK, which corresponds to the simpler Vonage SMS API (distinct from the multi-channel Messages API).

    javascript
    // index.js (continued)
    
    async function sendSms(to, text) {
      try {
        const resp = await vonage.sms.send({ to, from: fromNumber, text });
        console.log('Message submitted to Vonage.');
        console.dir(resp); // Log the full response for debugging
    
        // Check Vonage response status for the SMS API via the SDK.
        // A status of '0' indicates Vonage accepted the message submission.
        // Note: This does not guarantee final delivery to the handset.
        if (resp?.messages?.[0]?.['status'] === '0') {
          console.info(`Message accepted by Vonage. Message ID: ${resp.messages[0]['message-id']}`);
          return { success: true, messageId: resp.messages[0]['message-id'] };
        } else {
          // Log the specific error text from Vonage if available
          const status = resp?.messages?.[0]?.['status'] ?? 'Unknown';
          const errorText = resp?.messages?.[0]?.['error-text'] ?? 'No error text provided.';
          console.error(`Message submission failed. Status: ${status}, Error: ${errorText}`);
          return { success: false, error: `Vonage API Error: ${errorText} (Status: ${status})` };
        }
      } catch (err) {
        console.error('Error sending SMS via Vonage SDK:', err);
        // Handle potential network errors or SDK issues
        return { success: false, error: 'Failed to send SMS due to an unexpected error during API communication.' };
      }
    }
    • Why async/await? The vonage.sms.send() method is asynchronous (it involves network communication). async/await provides a cleaner way to handle promises compared to .then().catch().
    • Why check resp?.messages?.[0]?.['status'] === '0'? For the vonage.sms.send method (using the older SMS API), the response contains a messages array. For a single message, checking the status of the first element is standard. A status of '0' signifies successful submission to Vonage. Other statuses indicate specific issues (e.g., invalid number, insufficient funds, throttling), and the error-text provides details. The optional chaining (?.) adds safety in case the response structure is unexpected.

3. Building a Complete API Layer

Now, let's set up the Express server and define the API endpoint.

  1. Initialize Express App: Create the Express application instance and configure middleware for parsing JSON request bodies.

    javascript
    // index.js (continued)
    
    const app = express();
    const PORT = process.env.PORT || 3000; // Use port from .env or default to 3000
    
    // Middleware to parse JSON bodies
    app.use(express.json());
    // Middleware to parse URL-encoded bodies (optional but good practice)
    app.use(express.urlencoded({ extended: true }));
    • Why express.json()? This middleware automatically parses incoming requests with Content-Type: application/json and makes the parsed data available on req.body.
    • Why express.urlencoded()? Parses incoming requests with Content-Type: application/x-www-form-urlencoded. extended: true allows for rich objects and arrays to be encoded.
  2. Define the /send-sms Endpoint: Create a POST route that accepts the recipient number (to) and message (text) in the request body.

    javascript
    // index.js (continued)
    
    app.post('/send-sms', async (req, res) => {
      const { to, text } = req.body;
    
      // Basic Input Validation
      if (!to || !text) {
        return res.status(400).json({ success: false, error: 'Missing required fields: "to" and "text".' });
      }
    
      // Basic 'to' number format validation (improve as needed)
      // Example: Check if it starts with '+' and contains only digits (E.164 basic check)
      if (!/^\+[1-9]\d{1,14}$/.test(to)) {
         return res.status(400).json({ success: false, error: 'Invalid "to" number format. Use E.164 format (e.g., +12125551234).' });
      }
    
      console.log(`Received request to send SMS to ${to}: "${text.substring(0, 50)}..."`); // Log snippet
    
      const result = await sendSms(to, text);
    
      if (result.success) {
        res.status(200).json({ success: true, messageId: result.messageId });
      } else {
        // Return a server error status. The specific Vonage error is logged internally.
        // Consider whether to expose result.error directly to the client based on security needs.
        res.status(500).json({ success: false, error: result.error || 'Failed to send SMS.' });
      }
    });
    • Why async route handler? Because it calls the async function sendSms, the handler itself needs to be async to use await.
    • Why basic validation? Prevents unnecessary API calls with invalid data and provides immediate feedback to the client. More robust validation (e.g., using libraries like joi or express-validator) is recommended for production. The regex checks for a basic E.164 format.
    • Why res.status()? Setting appropriate HTTP status codes (400 for bad request, 200 for success, 500 for server error) is crucial for REST API design.
  3. Start the Server: Tell the Express app to listen for incoming connections on the specified port.

    javascript
    // index.js (continued)
    
    app.listen(PORT, () => {
      console.log(`Server listening on http://localhost:${PORT}`);
      console.log(`API Endpoint: POST http://localhost:${PORT}/send-sms`);
    });

4. Integrating with Vonage (Credentials and Configuration)

Proper configuration is key to connecting to the Vonage API.

  1. Obtain Vonage Credentials:

    • Navigate to your Vonage API Dashboard.
    • On the main dashboard page (Getting Started), you will find your API key and API secret. Copy these values.
    • Important: Treat your API secret like a password – keep it confidential.
  2. Get a Vonage Number / Configure Test Numbers:

    • Purchased Number: Navigate to Numbers -> Your numbers. If you have purchased a Vonage virtual number, copy it. This will be your FROM number.
    • Trial Account (Test Numbers): If you are using a free trial account, Vonage restricts sending SMS to only verified numbers initially.
      • Go to Numbers -> Test numbers.
      • Click Add test number.
      • Enter the phone number you want to send test messages to (e.g., your personal mobile number).
      • Verify the number using the code sent via SMS or voice call.
      • Crucially: You must add every recipient number to this list while in trial mode.
    • Sender ID: For the FROM number (VONAGE_FROM_NUMBER in .env), you can often use your purchased Vonage number. In some countries, you might use an Alphanumeric Sender ID (like "MyCompany"), but support varies. For testing, using your purchased Vonage number is reliable. If using trial mode without a purchased number, you might need to check Vonage documentation or support for the appropriate sender ID (it might default to a shared short code or generic ID). For this guide, we assume you have a purchased number or have clarified the correct sender ID for trial mode.
  3. Configure Environment Variables: Open the .env file you created earlier and add your credentials and Vonage number.

    dotenv
    # .env
    
    # Server Port
    PORT=3000
    
    # Vonage API Credentials
    VONAGE_API_KEY=YOUR_VONAGE_API_KEY_HERE
    VONAGE_API_SECRET=YOUR_VONAGE_API_SECRET_HERE
    
    # Vonage Number (or approved Sender ID) to send SMS From
    # Use E.164 format (e.g., +14155550100)
    VONAGE_FROM_NUMBER=YOUR_VONAGE_VIRTUAL_NUMBER_HERE
    • Replace the placeholder values with your actual Key, Secret, and Vonage Number.
    • PORT: The port your Express server will run on locally.
    • VONAGE_API_KEY: Your public Vonage API key.
    • VONAGE_API_SECRET: Your confidential Vonage API secret.
    • VONAGE_FROM_NUMBER: The Vonage number (in E.164 format) or approved Alphanumeric Sender ID that the SMS will appear to come from.

5. Implementing Error Handling and Logging

We've already added basic error handling, but let's refine the logging.

  1. Refined Logging: Our current console.log and console.error are basic. For production, consider using a dedicated logging library like winston or pino for structured logging (e.g., JSON format), log levels (info, warn, error), and log rotation.

    Example (Conceptual - using console for simplicity here):

    javascript
    // Inside sendSms function - error block
    } catch (err) {
      // Log with more context using structured logging principles
      console.error({
        timestamp: new Date().toISOString(),
        level: 'error',
        message: 'Failed to send SMS via Vonage API SDK call',
        errorMessage: err.message, // Log only the message for brevity, or err for full stack
        // errorStack: err.stack, // Optionally log stack for detailed debugging
        // context: { to, text } // Optionally log context, be mindful of PII
      });
      return { success: false, error: 'Failed to send SMS due to an unexpected error.' };
    }
    
    // Inside /send-sms route - successful send
    if (result.success) {
      console.info({ // Use info level for successful operations
         timestamp: new Date().toISOString(),
         level: 'info',
         message: 'SMS sent successfully via API endpoint',
         to: to, // Be careful logging PII like phone numbers in production logs
         messageId: result.messageId
      });
      res.status(200).json({ success: true, messageId: result.messageId });
    }
  2. Retry Mechanisms: Network issues or temporary Vonage service problems can cause failures. Implementing a retry strategy can improve reliability.

    • Simple Retry: You could wrap the vonage.sms.send call in a loop with a short delay.
    • Exponential Backoff: A better approach is to increase the delay between retries (e.g., 1s, 2s, 4s). Libraries like async-retry can simplify this.
    • Caveat: Be cautious with retries for SMS sending. Retrying after Vonage has already accepted the message (but before confirming delivery) could lead to duplicate messages. Only retry on initial connection errors or specific non-final error codes from Vonage if their documentation advises it. For this basic guide, we won't implement automatic retries in the application layer, relying on the initial success/failure check.

6. Creating a Database Schema and Data Layer

Not applicable for this simple guide. This application is stateless and doesn't require a database to store information about sent messages (though a production system might log message IDs and statuses to a database for tracking).

7. Adding Security Features

Security is paramount, especially when handling API keys and potentially user data.

  1. API Key Security:

    • Environment Variables: As implemented, using .env keeps keys out of the code.
    • .gitignore: Ensures .env isn't committed.
    • Production Environment: Use your hosting provider's mechanism for managing secrets (e.g., AWS Secrets Manager, Heroku Config Vars, Vercel Environment Variables). Never hardcode keys in production code.
  2. Input Validation:

    • We added basic checks for to and text presence and to format.
    • Recommendation: Use a dedicated validation library (joi, express-validator) for more complex rules (e.g., message length limits, character set validation).

    Install Joi example:

    bash
    npm install joi

    Illustrative example using Joi for validation (add to index.js if implementing):

    javascript
    import Joi from 'joi';
    
    const sendSmsSchema = Joi.object({
      to: Joi.string().pattern(/^\+[1-9]\d{1,14}$/).required().messages({
        'string.pattern.base': 'Invalid "to" number format. Use E.164 format (e.g., +12125551234).',
        'any.required': '"to" field is required.'
      }),
      text: Joi.string().min(1).max(1600).required().messages({ // Example length limits
        'string.min': '"text" cannot be empty.',
        'string.max': '"text" exceeds maximum length (1600 characters).',
        'any.required': '"text" field is required.'
      })
    });
    
    app.post('/send-sms', async (req, res) => {
      const { error, value } = sendSmsSchema.validate(req.body);
    
      if (error) {
        console.warn('Validation Error:', error.details[0].message);
        return res.status(400).json({ success: false, error: error.details[0].message });
      }
    
      const { to, text } = value; // Use validated values
      // ... rest of the handler logic using 'to' and 'text' from 'value'
       console.log(`Received valid request (Joi) to send SMS to ${to}: "${text.substring(0, 50)}..."`);
       const result = await sendSms(to, text);
       // ... handle result as before
    });
  3. Rate Limiting: Protect your API endpoint (and your Vonage account balance) from abuse by limiting the number of requests a client can make in a given time window.

    • Recommendation: Use middleware like express-rate-limit.

    Install express-rate-limit example:

    bash
    npm install express-rate-limit

    Illustrative example using basic rate limiting (add to index.js if implementing):

    javascript
    import rateLimit from 'express-rate-limit';
    
    const smsLimiter = rateLimit({
      windowMs: 15 * 60 * 1000, // 15 minutes
      max: 100, // Limit each IP to 100 requests per windowMs
      standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
      legacyHeaders: false, // Disable the `X-RateLimit-*` headers
      message: { success: false, error: 'Too many SMS requests from this IP, please try again after 15 minutes' }
    });
    
    // Apply the rate limiting middleware to the specific SMS route
    app.post('/send-sms', smsLimiter, async (req, res) => {
      // Handler logic remains the same, rate limiter runs before it
      // ...
    });
  4. Authentication/Authorization (Beyond Scope): This simple API is open. In a real application, you would protect this endpoint:

    • API Key: Require clients to send a secret API key in headers.
    • JWT/OAuth: Implement standard authentication flows if users are triggering the SMS.

8. Handling Special Cases

Real-world SMS sending involves nuances.

  1. Number Formatting (E.164):

    • Requirement: Vonage (and most SMS providers) require numbers in E.164 format: + followed by country code, then the number without spaces or symbols (e.g., +14155550100, +442071838750).
    • Action: Ensure any user input is normalized to this format before sending to the API. Our basic validation checks this pattern. Libraries like libphonenumber-js can provide robust parsing and validation.
  2. Trial Account Whitelisting:

    • Problem: Sending fails with "Non-Whitelisted Destination" if the recipient isn't added to the Test Numbers list in the Vonage dashboard during the trial period.
    • Action: Ensure target numbers are added and verified in the Vonage dashboard under Numbers -> Test numbers. This is the most common issue for new users.
  3. Character Limits and Encoding:

    • Standard SMS messages have character limits (e.g., 160 GSM characters, fewer for UCS-2/Unicode). Longer messages are split into multiple segments (concatenated SMS), billed separately.
    • Using non-GSM characters (like emojis or certain accented letters) forces UCS-2 encoding, reducing the per-segment limit significantly (~70 characters).
    • Action: Be aware of potential costs for long messages or messages with Unicode characters. Validate text length if necessary (text.length). The Vonage SDK/API handles segmentation automatically. Consider adding length checks in validation (as shown in the Joi example).
  4. Sender ID Restrictions:

    • Using Alphanumeric Sender IDs (e.g., "MyCompany") is not supported in all countries (e.g., the US often requires pre-registration or use of short codes/virtual numbers).
    • Action: Use your purchased Vonage virtual number as the FROM number for maximum compatibility, especially when starting. Check Vonage's country-specific guidelines if you need Alphanumeric Sender IDs.

9. Implementing Performance Optimizations

For this simple application, performance bottlenecks are unlikely to be in the Node.js code itself, but rather in the latency of the external Vonage API call.

  1. SDK Initialization: As done, initialize the Vonage SDK instance once outside the request handler.
  2. Asynchronous Handling: Node.js and Express handle concurrent requests efficiently due to the non-blocking I/O model. The async/await pattern ensures the server isn't blocked while waiting for Vonage's response.
  3. Load Testing (Beyond Scope): For high-throughput scenarios, tools like k6, artillery, or autocannon could be used to simulate load and identify potential bottlenecks (e.g., CPU limits, Vonage API rate limits).

10. Adding Monitoring, Observability, and Analytics

For production readiness, monitoring is essential.

  1. Health Checks:

    • Add a simple health check endpoint (e.g., GET /health) that returns a 200 OK status if the server is running. This is useful for load balancers and uptime monitoring services.
    javascript
    // index.js (Add this route before app.listen)
    app.get('/health', (req, res) => {
      res.status(200).json({ status: 'UP', timestamp: new Date().toISOString() });
    });
  2. Logging: As discussed in Section 5, implement structured logging to capture errors and key events. Send these logs to a centralized logging service (e.g., Datadog, Logz.io, AWS CloudWatch Logs).

  3. Error Tracking: Integrate an error tracking service (e.g., Sentry, Bugsnag) to capture, aggregate, and alert on unhandled exceptions or significant errors.

  4. Metrics: Track key metrics:

    • Request rate to /send-sms.
    • Request latency for /send-sms.
    • Error rate (4xx and 5xx responses).
    • Vonage API call success/failure rate.
    • Node.js process metrics (CPU, memory).
    • Tools like Prometheus/Grafana or managed APM (Application Performance Monitoring) solutions can track these.

11. Troubleshooting and Caveats

Common issues and their solutions:

  • Error: Non-Whitelisted Destination (Vonage Response)

    • Cause: You are using a Vonage trial account, and the recipient phone number (to) has not been added and verified in your Vonage Dashboard under Numbers -> Test numbers.
    • Solution: Log in to the Vonage Dashboard, navigate to Numbers -> Test numbers, and add/verify the destination number.
  • Error: Authentication failed or Invalid Credentials (Vonage Response)

    • Cause: The VONAGE_API_KEY or VONAGE_API_SECRET in your .env file is incorrect or doesn't match the credentials in your Vonage Dashboard.
    • Solution: Double-check the API Key and Secret in your Vonage Dashboard (API Settings) and ensure they are correctly copied into your .env file. Make sure the .env file is being loaded correctly (check for typos in dotenv/config import or ensure the server was restarted after changes).
  • Error: Invalid Sender Address (From) (Vonage Response)

    • Cause: The VONAGE_FROM_NUMBER in your .env file is not a valid Vonage number associated with your account or an approved Sender ID for the destination country.
    • Solution: Verify the number exists in Numbers -> Your numbers in the Vonage Dashboard. Ensure it's in E.164 format. If using an Alphanumeric ID, check Vonage's country regulations.
  • Error: Invalid 'to' number format. Use E.164 format... (API Response)

    • Cause: The to number provided in the POST request body is not in the correct E.164 format (e.g., missing +, contains spaces or dashes).
    • Solution: Ensure the client sending the request formats the number correctly (e.g., +12125551234).
  • Error: Missing required fields: "to" and "text" (API Response)

    • Cause: The POST request body did not include the to or text fields, or they were empty.
    • Solution: Ensure the client sends a JSON body including both fields with non-empty values.
  • No SMS Received, but API returns Success (200 OK):

    • Cause: Delivery issues downstream (carrier filtering, incorrect number but valid format, device issues). The Vonage message-id is returned, indicating Vonage accepted the message.
    • Solution: Use the message-id to check delivery status via Vonage APIs or logs if available. Double-check the recipient number is correct and the device has service. Check for carrier filtering.
  • Dependency Version Conflicts:

    • Cause: Incompatibility between Node.js version, Express, or Vonage SDK versions.
    • Solution: Ensure you are using compatible versions. Check the documentation for each library. Use npm ls to view the dependency tree and identify potential issues. Consider using npm ci for consistent installs based on package-lock.json.

12. Deployment and CI/CD

Deploying a Node.js application:

  1. Choose a Platform: Common choices include:

    • PaaS (Platform-as-a-Service): Heroku, Vercel, Render (often easier for simple apps).
    • IaaS (Infrastructure-as-a-Service): AWS (EC2, Lambda, Fargate), Google Cloud (Compute Engine, Cloud Run), Azure (App Service, Functions).
    • Containers: Dockerizing the app and deploying to Kubernetes, ECS, etc.
  2. Environment Variables: Configure PORT, VONAGE_API_KEY, VONAGE_API_SECRET, and VONAGE_FROM_NUMBER securely using the platform's environment variable management. Do not commit your .env file.

  3. Build Process: For simple apps like this, often no build step is needed. For more complex setups (e.g., TypeScript), you'd add a build script to package.json.

  4. Running the App: Ensure the platform runs your application using npm start or node index.js. Use a process manager like pm2 for better stability, logging, and clustering in non-serverless environments.

  5. CI/CD Pipeline (e.g., GitHub Actions):

    • Create a workflow file (e.g., .github/workflows/deploy.yml).
    • Trigger: On pushes to the main branch.
    • Steps:
      • Check out code.
      • Set up Node.js.
      • Install dependencies (npm ci - uses package-lock.json for exact versions).
      • (Optional) Run linters/tests.
      • Deploy to your chosen platform (using CLI tools like heroku deploy, vercel deploy, AWS CLI, etc.). Store platform API keys/tokens as GitHub Secrets.

    Example GitHub Actions workflow for Heroku (adapt for your platform):

    yaml
    # .github/workflows/deploy.yml
    name: Deploy to Heroku
    
    on:
      push:
        branches: [ main ] # Or your deployment branch
    
    jobs:
      deploy:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v4 # Use latest checkout action
          - uses: actions/setup-node@v4 # Use latest setup-node action
            with:
              node-version: '18' # Specify your Node.js version
              cache: 'npm'
          - name: Install dependencies
            run: npm ci # Use ci for faster, consistent installs in CI
          # Add build/test steps here if needed
          # e.g., npm run build
          # e.g., npm test
          - name: Deploy to Heroku
            uses: akhileshns/heroku-deploy@v3.12.14 # Check for latest version of deploy action
            with:
              heroku_api_key: ${{ secrets.HEROKU_API_KEY }} # Store Heroku key in GitHub Secrets
              heroku_app_name: "your-heroku-app-name" # Replace with your Heroku app name
              heroku_email: "your-heroku-email@example.com" # Replace with your Heroku account email

Frequently Asked Questions (FAQ)

How do I send an SMS using Node.js and Vonage?

Install the @vonage/server-sdk package, initialize the Vonage client with your API key and secret, then call vonage.sms.send() with the recipient number, sender number, and message text. The SDK handles authentication and API communication automatically.

What Node.js version is required for Express and Vonage SDK?

Use Node.js 18 LTS or later. Express 4.x requires Node.js 18+, and modern versions of @vonage/server-sdk support async/await patterns available in Node.js 18 and above.

Where do I find my Vonage API credentials?

Navigate to the Vonage Dashboard and locate your API key and API secret on the main Getting Started page. Keep your API secret confidential and store it in environment variables, never in source code.

Why am I getting "Non-Whitelisted Destination" errors?

Trial accounts restrict SMS sending to verified test numbers only. Add recipient phone numbers to your whitelist by navigating to NumbersTest numbers in the Vonage Dashboard, then verify each number via SMS or voice call.

What phone number format does Vonage require?

Use E.164 format: + followed by country code and number without spaces or special characters. Examples: +12125551234 (US), +442071838750 (UK). The @vonage/server-sdk expects all numbers in this format.

How do I handle Vonage SMS delivery failures?

Check the response from vonage.sms.send(). A status of '0' indicates successful submission. Other status codes indicate specific errors (invalid number, insufficient funds, throttling). Access the error-text field in the response for detailed error messages.

Can I use alphanumeric sender IDs with Vonage?

Alphanumeric sender IDs (like "MyCompany") work in some countries but not all. The US requires pre-registered short codes or virtual numbers. For maximum compatibility, use your purchased Vonage virtual number as the sender ID.

How many characters can I send in a single SMS?

Standard SMS supports 160 GSM-7 characters per segment. Unicode/UCS-2 encoding (for emojis or special characters) reduces this to 70 characters per segment. Longer messages split into multiple segments, each billed separately.

How do I secure my Vonage API credentials in production?

Store credentials in environment variables using your hosting platform's secrets management (AWS Secrets Manager, Heroku Config Vars, Vercel Environment Variables). Use the dotenv package for local development and ensure .env files are excluded from version control via .gitignore.

What HTTP status codes should my SMS API endpoint return?

Return 200 OK for successful SMS submission, 400 Bad Request for invalid input (missing fields, wrong phone format), and 500 Internal Server Error for Vonage API failures or network errors. Include descriptive error messages in JSON responses.

How do I test my Vonage SMS integration locally?

Run your Express server with node index.js, then send POST requests to http://localhost:3000/send-sms using curl, Postman, or similar tools. Include JSON body with to (E.164 phone number) and text (message content) fields.

Does Vonage support SMS delivery tracking?

Yes. Vonage returns a message-id for each SMS. Use this ID with Vonage's Delivery Receipt webhooks or API endpoints to track delivery status, including submitted, delivered, failed, and expired states.

Frequently Asked Questions

how to send sms with node js and express

Set up an Express server with the Vonage Node.js SDK. Create a POST route to handle incoming requests with recipient number and message text. Use the `vonage.sms.send()` method to dispatch the SMS via the Vonage API, ensuring credentials are securely loaded from environment variables.

what is vonage sms api and how to use

The Vonage SMS API is a service that allows you to send text messages programmatically. Integrate the `@vonage/server-sdk` into your Node.js project and use the `vonage.sms.send()` method to send SMS messages. Remember to set up your API key and secret in your Vonage dashboard.

why use express js for sending sms messages

Express.js simplifies the process of building a REST API endpoint for handling incoming SMS requests and routing them to the Vonage API. Its minimal setup and middleware support make it ideal for this purpose, allowing for efficient handling of HTTP requests and responses.

when should I initialize the vonage sdk

Initialize the Vonage SDK client once when the application starts, outside of any request handlers. This improves efficiency by avoiding redundant initialization for each incoming SMS request, leveraging Node.js's module scope.

can I use alphanumeric sender id with vonage

Using alphanumeric sender IDs (like "MyCompany") depends on the destination country and may require pre-registration. For broader compatibility, particularly during trial periods, it's recommended to use your purchased Vonage virtual number as the sender ID. Always check Vonage's country-specific guidelines.

how to set up vonage api credentials in node js

Store your Vonage API key, secret, and virtual number in a `.env` file. Use the `dotenv` package in your Node.js project to load these variables into `process.env`. Make sure to add `.env` to your `.gitignore` file to prevent accidentally exposing these sensitive credentials.

what is the correct format for phone numbers in vonage sms api

Vonage requires phone numbers in E.164 format. This format starts with a '+' followed by the country code and the number without any spaces or symbols (e.g., +14155550100). Ensure any user input is properly formatted before sending to the API.

how to handle vonage sms api errors in express

Check the `status` field in the Vonage API response. A '0' status means Vonage accepted the message submission. Any other status code indicates an error; use the 'error-text' field for debugging and notify the user or implement retry mechanisms as needed.

why am I getting non whitelisted destination error vonage

If using a trial Vonage account, you need to add all recipient numbers to your Test Numbers list in the Vonage dashboard. Verify each number to enable sending during the trial period. This is a frequent setup issue for new Vonage users.

how to implement rate limiting for vonage sms api endpoint

Use the `express-rate-limit` middleware in your Express app. Configure it to restrict the number of SMS requests from a specific IP address within a time window, protecting your Vonage account and API from overuse.

what is the character limit for vonage sms messages

Standard SMS messages are limited to 160 GSM characters. Using non-GSM characters (like emojis) can significantly reduce the character limit. Longer messages will be segmented, potentially incurring additional costs.

how to secure my vonage api key and secret in production

Never commit the `.env` file. Use your hosting provider's secure mechanism to manage environment variables, such as AWS Secrets Manager, Heroku config vars, or similar services.

how to test my vonage sms api integration

Use a tool like Postman or `curl` to send test POST requests to your `/send-sms` endpoint. Verify that the SMS messages are sent correctly. Remember to add any test numbers to your Vonage account's Test Numbers list if you are using a trial account.

how to add health check for node js express sms app

Add a simple `GET /health` route to your Express app. This endpoint should return a 200 OK status with a JSON payload indicating the server's health. This allows monitoring tools to quickly check if your application is running.