code examples

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

Build Two-Way SMS Messaging with Node.js and Express using Vonage API

Complete guide to building a production-ready Node.js Express application for SMS messaging with Vonage API. Includes setup, authentication, error handling, security, rate limiting, and deployment strategies.

⚠️ CRITICAL NOTICE: Title-Content Mismatch

The filename of this article references "Twilio + Next.js + NextAuth + Inbound Two-Way Messaging" but the content demonstrates a Vonage + Express + Outbound SMS implementation.

Key Differences:

  • API Provider: This guide uses Vonage API (not Twilio API)
  • Framework: This guide uses Express (not Next.js with NextAuth)
  • Functionality: This guide covers outbound SMS sending (not inbound two-way messaging with webhooks)

For Twilio + Next.js + NextAuth + Inbound Two-Way Messaging:

  • Twilio uses different SDK (twilio npm package) and authentication (Account SID + Auth Token)
  • Next.js uses API routes (/pages/api or /app/api) instead of Express routes
  • NextAuth.js provides authentication middleware for protecting API routes
  • Inbound two-way messaging requires webhook endpoints that receive POST requests from Twilio and return TwiML responses
  • Twilio webhooks send data as application/x-www-form-urlencoded, requiring body-parser or Next.js urlencoded parsing

If you need the Twilio + Next.js + NextAuth + Inbound implementation, refer to:

This guide provides a complete walkthrough for building a production-ready Node.js application using the Express framework to send SMS messages via the Vonage API. You'll learn everything from project setup and configuration to implementing core functionality, error handling, security, and testing.

By the end of this guide, you'll have a simple but robust API endpoint capable of accepting a destination phone number and a message, then using Vonage to deliver that message as an SMS.

1. Node.js Project Setup: Installing Vonage SDK and Express Dependencies

Initialize your 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-sender
    cd vonage-sms-sender
  2. Initialize Node.js Project: Initialize the project using npm. The -y flag accepts the default settings.

    bash
    npm init -y

    This creates a package.json file.

  3. Install Dependencies: Install Express, the Vonage Server SDK, and dotenv.

    bash
    npm install express @vonage/server-sdk dotenv
  4. Enable ES Module Syntax (Optional but Recommended): To use modern import syntax instead of require, open your package.json file and add the following line at the top level:

    json
    // package.json
    {
      "name": "vonage-sms-sender",
      "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.x.x",
        "dotenv": "^16.x.x",
        "express": "^4.x.x"
      }
    }

    Why type: module?: It enables the use of standard ES6 import/export syntax, which is common in modern JavaScript development. Note: The versions listed (e.g., ^3.x.x) indicate compatibility with that major version. Always check for and use the latest stable versions of these packages for security and feature updates.

  5. Create Project Structure: Create the main application file and a file for Vonage-related logic.

    bash
    touch index.js vonageService.js

    Your project structure should now look like this:

    text
    vonage-sms-sender/
      node_modules/
      .env
      .gitignore
      index.js
      package.json
      package-lock.json
      vonageService.js
  6. Create .env File: This file will store your sensitive API credentials. Create a file named .env in the root of your project. Never commit this file to version control.

    env
    # .env
    PORT=3000
    
    # Vonage Credentials
    VONAGE_API_KEY=YOUR_VONAGE_API_KEY
    VONAGE_API_SECRET=YOUR_VONAGE_API_SECRET
    VONAGE_FROM_NUMBER=YOUR_VONAGE_NUMBER_OR_SENDER_ID

    Purpose of .env: Separates configuration and secrets from code, enhancing security and making it easier to manage different environments (development, production).

  7. Create .gitignore File: Prevent sensitive files and unnecessary directories from being tracked by Git. Create a file named .gitignore.

    text
    # .gitignore
    node_modules/
    .env
    *.log

2. Vonage API Configuration: Authentication and SDK Integration

Configure the Vonage SDK and set up the logic to send SMS messages.

  1. Obtain Vonage API Credentials:

    • Log in to your Vonage API Dashboard.
    • On the main dashboard page ("Getting started"), you'll find your API key and API secret at the top.
    • Copy these values.
  2. Configure .env:

    • Open the .env file you created earlier.
    • Replace YOUR_VONAGE_API_KEY with your actual API key.
    • Replace YOUR_VONAGE_API_SECRET with your actual API secret.
  3. Set the 'From' Number:

    • In the Vonage Dashboard, navigate to Numbers > Your numbers.
    • If you don't have a number, you may need to buy one (check pricing). Alternatively, for some destinations, you can use an Alphanumeric Sender ID (e.g., "MyApp"). Note that Sender ID support varies by country and carrier.
    • Copy your purchased Vonage virtual number (including the country code, e.g., 15551234567) or your desired Sender ID.
    • Replace YOUR_VONAGE_NUMBER_OR_SENDER_ID in your .env file with this value.
  4. Whitelist Test Numbers (Trial Accounts Only):

    • Crucial: If you're using a trial Vonage account, you can only send SMS messages to numbers you've verified and added to your test list.
    • In the Vonage Dashboard, navigate to the Sandbox & Test Numbers section (often accessible via a prompt on the dashboard or under your account settings/profile).
    • Add the phone number(s) you intend to send test messages to. You'll typically need to verify ownership by entering a code sent via SMS or voice call.
    • Why?: This prevents abuse of the free trial credits. Sending to a non-whitelisted number from a trial account will result in an error (see Troubleshooting).
  5. Implement Vonage Service Logic: Open vonageService.js and add the following code to initialize the SDK and create the sending function.

    javascript
    // vonageService.js
    import { Vonage } from '@vonage/server-sdk';
    import 'dotenv/config'; // Load .env variables
    
    // Initialize Vonage client with API key and secret
    const vonage = new Vonage({
      apiKey: process.env.VONAGE_API_KEY,
      apiSecret: process.env.VONAGE_API_SECRET,
    });
    
    const fromNumber = process.env.VONAGE_FROM_NUMBER;
    
    /**
     * Sends an SMS message using the Vonage API.
     * Uses the vonage.sms.send() method from the SDK v3.
     *
     * @param {string} to - The recipient's phone number in E.164 format (e.g., +15551234567).
     * @param {string} text - The content of the SMS message.
     * @returns {Promise<object>} - A promise that resolves with the Vonage API response.
     * @throws {Error} - Throws an error if the API call fails.
     */
    export async function sendSms(to, text) {
      console.log(`Attempting to send SMS from ${fromNumber} to ${to}`);
      try {
        const response = await vonage.sms.send({ to, from: fromNumber, text });
    
        console.log('SMS submitted successfully:', response);
        // Note: Success here means the message was *accepted* by Vonage, not necessarily delivered.
        // Delivery status comes via webhooks (not covered in this basic guide).
    
        // Check the status of the first (and likely only) message part
        if (response.messages[0].status === '0') {
            console.log(`Message to ${to} submitted successfully with ID: ${response.messages[0]['message-id']}`);
            return response; // Return the full response object on success
        } else {
            const errorCode = response.messages[0].status;
            const errorText = response.messages[0]['error-text'];
            console.error(`Message failed with error code ${errorCode}: ${errorText}`);
            // Throw a more specific error based on the Vonage response
            throw new Error(`Vonage API Error ${errorCode}: ${errorText}`);
        }
    
      } catch (error) {
        console.error('Error sending SMS:', error);
        // Re-throw the error to be caught by the calling function (API endpoint)
        throw error;
      }
    }

    Code Explanation:

    • We import Vonage and load .env variables.
    • We initialize the Vonage client using the API key and secret from process.env.
    • The sendSms function is an async function that takes the recipient (to) and message (text).
    • It calls vonage.sms.send(), passing an object with to, from (read from .env), and text. Note: The vonage.sms.send() method is part of the newer V3 SDK syntax, preferred over the older vonage.message.sendSms() seen in some research links.
    • Basic logging is included for success and errors.
    • We check the status field in the response. A status of '0' indicates success. Other statuses indicate errors, and we extract the error message.
    • Errors are caught and re-thrown to be handled by the API layer.

Understanding Vonage SMS API Status Codes

When you send an SMS via the Vonage API, the response contains a status field that indicates whether the message was accepted. Status code '0' means success; any non-zero value indicates an error.

Important: There are two types of error codes in Vonage SMS:

  1. Submit Status Codes: Returned immediately when you submit the API request (indicates whether Vonage accepted your request)
  2. Delivery Receipt (DLR) Error Codes: Returned via webhook after delivery attempt (indicates final delivery status from the carrier)

Common Submit Status Codes:

Status CodeMeaningTroubleshooting
0SuccessMessage accepted by Vonage for delivery
1ThrottledYou are sending messages too quickly. Implement rate limiting or request rate increase from Vonage
2Missing paramsRequired parameter missing (e.g., to, from, text). Check request payload
3Invalid paramsParameter value invalid (e.g., malformed phone number). Validate E.164 format
4Invalid credentialsVONAGE_API_KEY or VONAGE_API_SECRET incorrect. Verify credentials in Vonage dashboard
5Internal errorVonage server error. Retry with exponential backoff
6Invalid messageMessage content rejected (e.g., exceeds length, invalid encoding)
9Partner quota exceededInsufficient account balance or daily quota reached. Add credit or upgrade account
10Non-whitelisted destinationTrial accounts only: Recipient not in approved test numbers. Add to whitelist in dashboard
15Illegal sender addressVONAGE_FROM_NUMBER invalid or not owned by your account. Verify in dashboard under Numbers

Delivery Receipt Status: All DLRs with a non-zero err-code indicate failed delivery (SMS did not reach recipient). Set up a webhook endpoint to receive DLR callbacks and track final delivery status.

References:

3. Building the Express API Endpoint for SMS Sending

Create the Express server and the API endpoint that will use your sendSms function.

  1. Set up Express Server: Open index.js and add the following code:

    javascript
    // index.js
    import express from 'express';
    import 'dotenv/config';
    import { sendSms } from './vonageService.js'; // Import our SMS function
    
    const app = express();
    const port = process.env.PORT || 3000; // Use port from .env or default to 3000
    
    // Middleware to parse JSON request bodies
    app.use(express.json());
    // Middleware to parse URL-encoded request bodies
    app.use(express.urlencoded({ extended: true }));
    
    // Simple root route for health check / welcome message
    app.get('/', (req, res) => {
      res.send('Vonage SMS Sender API is running!');
    });
    
    // --- API Endpoint to Send SMS ---
    app.post('/api/send-sms', async (req, res) => {
      // 1. Basic Input Validation
      const { to, text } = req.body; // Destructure from request body
    
      if (!to || typeof to !== 'string') {
        return res.status(400).json({ success: false, error: 'Missing or invalid ""to"" field (string required).' });
      }
      if (!text || typeof text !== 'string') {
        return res.status(400).json({ success: false, error: 'Missing or invalid ""text"" field (string required).' });
      }
    
      // Basic E.164 format check (allows optional '+'). Vonage recommends E.164 (e.g., +15551234567).
      if (!/^\+?[1-9]\d{1,14}$/.test(to)) {
         console.warn(`Received potentially invalid ""to"" number format: ${to}. Attempting to send anyway.`);
         // For production, consider stricter validation that *rejects* non-compliant formats based on your requirements.
         // Example stricter rejection:
         // return res.status(400).json({ success: false, error: 'Invalid ""to"" number format. Use E.164 format (e.g., +1234567890).' });
      }
    
      try {
        // 2. Call the Vonage service function
        console.log(`Received request to send SMS to: ${to}`);
        const vonageResponse = await sendSms(to, text);
    
        // 3. Send Success Response
        // Include Vonage message ID for potential tracking
        const messageId = vonageResponse.messages[0]['message-id'];
        res.status(200).json({
            success: true,
            message: `SMS submitted successfully to ${to}`,
            messageId: messageId,
            // Optionally include the full vonage response for debugging
            // vonageDetails: vonageResponse
         });
    
      } catch (error) {
        // 4. Send Error Response
        console.error(`Failed to send SMS to ${to}:`, error);
    
        // Determine appropriate status code based on error if possible
        // For now, use 500 for server/Vonage errors, 400 was handled above
        let statusCode = 500;
        let errorMessage = 'Failed to send SMS due to an internal error.';
    
        // Check if it's a Vonage API error from our service function
        if (error.message.startsWith('Vonage API Error')) {
            errorMessage = error.message;
            // Potentially map Vonage error codes to HTTP status codes if needed
            // e.g., code '2' (Missing params) -> 400, '9' (Partner quota exceeded) -> 503
        } else if (error instanceof Error) {
             // Handle generic errors differently if needed
             errorMessage = error.message || errorMessage;
        }
    
        res.status(statusCode).json({
            success: false,
            error: errorMessage,
            // Optionally include error details during development
            // errorDetails: error.toString()
         });
      }
    });
    
    // Start the server
    app.listen(port, () => {
      console.log(`Server listening at http://localhost:${port}`);
      console.log(`API Endpoint: POST http://localhost:${port}/api/send-sms`);
    });

    Code Explanation:

    • Imports express, loads .env, and imports our sendSms function.
    • Initializes the Express app and sets the port.
    • Uses express.json() and express.urlencoded() middleware to parse incoming request bodies.
    • Defines a POST route at /api/send-sms.
    • Input Validation: It checks if to and text are present in the request body and are strings. It also includes a basic regex check for the 'to' number format, logging a warning but allowing the request to proceed for flexibility (with comments suggesting stricter validation for production).
    • Service Call: It calls the sendSms function within a try...catch block.
    • Success Response: If sendSms resolves successfully, it sends a 200 OK JSON response including the Vonage message ID.
    • Error Response: If sendSms throws an error, it catches it, logs the error, and sends an appropriate error JSON response (usually 500 Internal Server Error for API/server issues, but could be refined).

4. Error Handling and Logging Best Practices for Vonage SMS

You've already incorporated basic error handling and logging:

  • vonageService.js:
    • Logs attempt details.
    • Uses try...catch around the vonage.sms.send() call.
    • Logs success responses from Vonage.
    • Checks the Vonage status code (messages[0].status) and throws a specific error message if not '0'.
    • Logs caught errors before re-throwing.
  • index.js (API Endpoint):
    • Uses try...catch around the sendSms() call.
    • Handles validation errors explicitly with 400 Bad Request.
    • Logs received requests and failures.
    • Returns standardized JSON responses for success ({ success: true, ... }) and failure ({ success: false, error: '...' }).

Further Improvements (Production Considerations):

  • Structured Logging: Use a dedicated logging library like Pino or Winston for structured JSON logs, log levels (info, warn, error), and easier log management/analysis.
  • Centralized Error Handling: Implement Express error-handling middleware for a cleaner way to manage errors across different routes.
  • Detailed Vonage Error Mapping: Map specific Vonage error codes (returned in messages[0].status or messages[0]['error-text']) to more specific HTTP status codes and user-friendly error messages. Refer to the Vonage SMS API Error Codes documentation.
  • Retry Mechanisms: For transient network errors or specific Vonage errors (like rate limits), implement a retry strategy (e.g., exponential backoff) using libraries like async-retry. Vonage itself has some retry logic for webhooks, but not necessarily for API calls from your end.

5. Security Features: Rate Limiting, 10DLC Compliance, and API Protection

Security is crucial for any API, especially one handling communication.

  • Environment Variables: We are already using .env to keep API keys out of the code and .gitignore to prevent committing them. This is the most critical security step.
  • 10DLC Compliance (US SMS): If sending SMS to US numbers, you must comply with 10DLC (10-Digit Long Code) registration requirements. As of January 2025, Reseller ID requirements are mandatory. Unregistered traffic may be filtered or blocked by carriers. Register your brand and campaign through the Vonage dashboard under Messaging > 10DLC. Note: Brand vetting can take 1-3 business days; budget accordingly.
  • Input Validation: Basic validation in index.js prevents malformed requests and potential issues downstream. The current phone number validation is lenient; consider enforcing strict E.164 format in production.
    • Improvement: Use a dedicated validation library like Joi or express-validator for more complex and robust validation schemas. Sanitize inputs if they were being stored or reflected (though less critical here as they only go to Vonage).
  • Rate Limiting: Protect your API endpoint (and your Vonage budget) from abuse or simple loops by adding rate limiting.
    1. Install the library:

      bash
      npm install express-rate-limit
    2. Apply it in index.js:

      javascript
      // index.js
      import express from 'express';
      import 'dotenv/config';
      import { sendSms } from './vonageService.js';
      import rateLimit from 'express-rate-limit'; // Import rate-limiter
      
      const app = express();
      // ... other setup ...
      app.use(express.json());
      app.use(express.urlencoded({ extended: true }));
      
      // Apply rate limiting to the SMS endpoint
      const smsLimiter = rateLimit({
        windowMs: 15 * 60 * 1000, // 15 minutes
        max: 100, // Limit each IP to 100 requests per windowMs
        message: { success: false, error: 'Too many requests, please try again after 15 minutes.' },
        standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
        legacyHeaders: false, // Disable the `X-RateLimit-*` headers
      });
      
      app.use('/api/send-sms', smsLimiter); // Apply middleware specifically to this route
      
      // ... your root route ...
      app.get('/', (req, res) => { /* ... */ });
      
      // ... your route definition ...
      app.post('/api/send-sms', async (req, res) => {
        // ... existing route logic ...
      });
      
      // ... start server ...
      const port = process.env.PORT || 3000;
      app.listen(port, () => { /* ... */ });
  • HTTPS: In a production environment, always run your Node.js application behind a reverse proxy (like Nginx or Caddy) or use a platform (like Heroku, Vercel, AWS) that terminates SSL/TLS, ensuring all communication is encrypted over HTTPS. Do not handle TLS directly in Node.js unless necessary.
  • Authentication/Authorization (Beyond Scope): This guide creates a public endpoint. For internal or protected use, you would need to implement authentication (e.g., API keys specific to your clients, JWT tokens) to ensure only authorized services can trigger SMS sends.

6. Common Vonage SMS Errors: Troubleshooting and Solutions

  • Non-Whitelisted Destination Error: This is the most common issue for trial accounts.
    • Cause: Sending an SMS to a phone number not added to your approved test numbers list in the Vonage dashboard.
    • Solution: Log in to the Vonage dashboard, navigate to ""Sandbox & Test Numbers"" (or similar section), and add/verify the recipient's phone number.
  • Invalid Credentials (Error Code 4):
    • Cause: VONAGE_API_KEY or VONAGE_API_SECRET in your .env file are incorrect or missing.
    • Solution: Double-check the credentials in your .env file against the Vonage dashboard. Ensure the .env file is in the project root and being loaded correctly (dotenv/config). Restart your server after changes.
  • Invalid 'From' Number (Error Code 15: Illegal Sender Address):
    • Cause: The VONAGE_FROM_NUMBER is not a valid Vonage number associated with your account or an invalid/unsupported Alphanumeric Sender ID.
    • Solution: Verify the number/Sender ID in the Vonage dashboard (""Numbers"" > ""Your numbers""). Ensure it's entered correctly in .env. Check Sender ID regulations for the destination country if using one.
  • Insufficient Funds (Error Code 9):
    • Cause: Your Vonage account balance is too low to send the message.
    • Solution: Add credit to your Vonage account.
  • Invalid 'To' Number Format: While our basic validation allows some flexibility, Vonage strongly prefers the E.164 format (e.g., +14155552671). Incorrectly formatted numbers might fail, even if our current code attempts to send them. Stricter validation is recommended.
  • Carrier Filtering: Sometimes, messages might be blocked by the recipient's mobile carrier as spam, even if Vonage accepts the message. This is harder to debug and might require checking Vonage logs or contacting support if persistent. Using registered Sender IDs or Toll-Free Numbers can sometimes improve deliverability.
  • SDK Version: Ensure you are using a compatible version of @vonage/server-sdk. This guide assumes v3.x (currently v3.24.1 as of January 2025). Key differences in v3:
    • All function callbacks removed – must use async/await or .then/.catch
    • Functions use parameter objects instead of positional arguments (e.g., vonage.sms.send({to, from, text}))
    • Uses TypeScript for better code completion and type safety
    • Check the official Vonage Node SDK documentation for the latest methods and syntax: https://developer.vonage.com/en/changelogs/server_sdk/vonage-node-sdk
  • Multi-Channel Messaging: For applications requiring SMS fallback with WhatsApp, RCS, or MMS, consider migrating to the Vonage Messages API (vonage.messages.send()), which supports channel failover and returns a workflow_id for tracking message sequences across multiple channels.

7. Production Deployment Strategies for Node.js SMS Applications

Deploy this application by running the Node.js process on a server and managing environment variables securely.

  • Platforms:
    • PaaS (Platform as a Service): Heroku, Vercel, Render, Google App Engine. These often simplify deployment. You typically push your code, and the platform builds and runs it. Environment variables are configured through their dashboards or CLI tools.
    • IaaS (Infrastructure as a Service): AWS EC2, Google Compute Engine, DigitalOcean Droplets. Requires more setup (OS, Node.js installation, process manager like PM2, potentially a reverse proxy like Nginx). Environment variables can be set directly on the server or managed via deployment scripts.
    • Serverless: AWS Lambda + API Gateway, Google Cloud Functions. Your code runs in response to triggers (like API Gateway requests). Requires packaging the code and dependencies. Environment variables are managed within the function configuration.
  • Process Manager: Use a process manager like PM2 to keep your Node.js application running reliably in production (handles crashes, restarts, clustering).
    bash
    npm install pm2 -g # Install globally
    pm2 start index.js --name vonage-sms-api # Start the app
    pm2 save # Save the process list to restart on server reboot
    pm2 logs # View logs
  • Environment Variables: Crucially, never hardcode API keys/secrets. Use the environment variable mechanisms provided by your chosen hosting platform. Do not upload your .env file to the server.
  • CI/CD (Continuous Integration / Continuous Deployment): Set up pipelines using tools like GitHub Actions, GitLab CI, Jenkins, or Bitbucket Pipelines to automate testing, building, and deploying your application whenever you push changes to your repository.

8. Testing Your Vonage SMS API: Verification and Debugging

Test the endpoint thoroughly to ensure it works as expected.

  1. Start the Server:

    bash
    npm start

    You should see Server listening at http://localhost:3000 and the API endpoint URL logged.

  2. Test with curl: Open a new terminal window. Replace +15551234567 with a whitelisted recipient number (if on a trial account) and adjust the message text.

    bash
    curl -X POST http://localhost:3000/api/send-sms \
         -H ""Content-Type: application/json"" \
         -d '{
               ""to"": ""+15551234567"",
               ""text"": ""Hello from the Vonage Express API!""
             }'
    • Expected Success Response (200 OK):

      json
      {
        ""success"": true,
        ""message"": ""SMS submitted successfully to +15551234567"",
        ""messageId"": ""some-vonage-message-id-12345""
      }

      You should also receive the SMS on the target phone shortly after.

    • Expected Validation Error Response (400 Bad Request - Missing 'text'):

      bash
      curl -X POST http://localhost:3000/api/send-sms \
           -H ""Content-Type: application/json"" \
           -d '{ ""to"": ""+15551234567"" }'
      json
      {
        ""success"": false,
        ""error"": ""Missing or invalid \""text\"" field (string required).""
      }
    • Expected Rate Limit Error Response (429 Too Many Requests - after many rapid requests):

      json
      {
        ""success"": false,
        ""error"": ""Too many requests, please try again after 15 minutes.""
      }
  3. Test with Postman:

    • Create a new request.
    • Set the method to POST.
    • Enter the URL: http://localhost:3000/api/send-sms.
    • Go to the ""Body"" tab, select ""raw"", and choose ""JSON"" from the dropdown.
    • Enter the JSON payload:
      json
      {
        ""to"": ""+15551234567"",
        ""text"": ""Test message from Postman via Vonage API""
      }
    • Click ""Send"".
    • Observe the response body, status code, and check the target phone for the SMS. Test error cases by omitting fields or sending rapid requests.
  4. Check Vonage Dashboard Logs:

    • Log in to the Vonage Dashboard.
    • Navigate to Logs > Messages API (or potentially SMS logs depending on account configuration).
    • You should see records of your sent messages, their status (e.g., submitted, delivered, failed), the recipient, and any error codes if they failed. This is useful for confirming Vonage received the request and for debugging delivery issues.

Verification Checklist:

  • Project initializes correctly (npm install).
  • Server starts without errors (npm start).
  • .env file is correctly configured with Vonage credentials and 'From' number.
  • Sending to a valid, whitelisted (if trial) number results in a 200 OK response and an SMS delivery.
  • Response JSON includes success: true and a messageId.
  • Sending without the to field results in a 400 Bad Request response with an appropriate error message.
  • Sending without the text field results in a 400 Bad Request response with an appropriate error message.
  • Sending with invalid Vonage credentials results in a 500 Internal Server Error (or similar) response detailing the Vonage error.
  • Rapidly sending requests eventually triggers a 429 Too Many Requests response (if rate limiting is enabled).
  • Sent messages appear in the Vonage Dashboard logs.

Frequently Asked Questions (FAQ)

What's the difference between Vonage SMS API and Twilio SMS API?

Vonage uses API Key + API Secret authentication and the @vonage/server-sdk npm package with vonage.sms.send() method. Twilio uses Account SID + Auth Token authentication and the twilio npm package with client.messages.create() method. Both require E.164 phone number formatting. Vonage has a newer Messages API for multi-channel support (SMS, WhatsApp, RCS), while Twilio has Programmable Messaging with similar capabilities. Choose based on pricing, regional coverage, and existing infrastructure.

Why does my Vonage trial account show "Non-Whitelisted Destination" errors?

Trial accounts can only send SMS to verified/whitelisted numbers. Log into the Vonage dashboard, navigate to Sandbox & Test Numbers (or similar section), and add the recipient's phone number. You'll need to verify ownership via SMS or voice code. This restriction prevents abuse of free trial credits. Upgrade to a paid account to send to any valid phone number.

What does Vonage SMS API status code '0' mean?

Status code '0' indicates success – Vonage accepted your SMS for delivery. Any non-zero status code is an error. Common errors include: 4 (invalid credentials), 9 (insufficient balance), 10 (non-whitelisted destination on trial), 15 (invalid sender address). Check the error-text field in the response for details. Note: Status '0' means accepted, not delivered. Use Delivery Receipt (DLR) webhooks to track final delivery status.

How do I format phone numbers for Vonage SMS API?

Vonage requires E.164 format: +[country code][number] (e.g., +14155552671 for US, +442071838750 for UK). Include the + symbol and remove spaces, dashes, or parentheses. The regex /^\+?[1-9]\d{1,14}$/ validates E.164 format. Invalid formats may result in status code 3 (Invalid params). Always validate phone numbers before sending.

What is 10DLC and do I need it for US SMS?

10DLC (10-Digit Long Code) is a US regulatory requirement for Application-to-Person (A2P) SMS messaging. As of January 2025, Reseller ID requirements are mandatory. Register your Brand (business info + EIN) and Campaign (use case like "2FA" or "Marketing") through the Vonage dashboard under Messaging > 10DLC. Unregistered traffic may be filtered or blocked by US carriers. Brand vetting takes 1-3 business days. This applies to all US 10-digit long codes, not toll-free or short codes.

How do I receive inbound SMS messages with Vonage?

Set up a webhook endpoint in your Express app (e.g., app.post('/inbound-sms', ...)) that accepts POST requests from Vonage. Configure the webhook URL in the Vonage dashboard under Numbers > Your numbers > select number > Inbound Webhook URL. Vonage sends inbound message data (sender, text, timestamp) as URL-encoded POST. Parse with express.urlencoded() middleware. For local development, use ngrok to create a public URL that forwards to localhost.

What's the difference between Vonage SMS API and Messages API?

SMS API (vonage.sms.send()) is the legacy API for SMS-only messaging, simpler but limited to SMS/MMS. Messages API (vonage.messages.send()) supports multi-channel messaging (SMS, MMS, WhatsApp, Viber, Facebook Messenger, RCS) with automatic failover between channels. Messages API returns a workflow_id to track message sequences. Use Messages API for modern applications requiring channel flexibility and better deliverability through fallback chains.

How do I handle Vonage API rate limits?

Vonage returns status code 1 (Throttled) when you exceed rate limits. Implement rate limiting on your API endpoint using express-rate-limit (100 requests per 15 minutes recommended). For Vonage rate limit errors, implement exponential backoff retry using libraries like async-retry. Contact Vonage support to request higher rate limits for production use. Trial accounts have stricter limits than paid accounts.

Can I use Vonage Server SDK v3 with TypeScript?

Yes, v3.x is written in TypeScript and provides full type definitions out of the box. Import types from @vonage/server-sdk: import { Vonage, Auth } from '@vonage/server-sdk'. All methods support TypeScript auto-completion. Use async/await syntax (callbacks removed in v3). Parameter objects provide better type safety than positional arguments used in older versions.

How do I deploy a Vonage Express app to production?

Use a PaaS like Heroku, Render, or Vercel (configure environment variables via dashboard), or IaaS like AWS EC2 (use PM2 process manager, Nginx reverse proxy). Store VONAGE_API_KEY, VONAGE_API_SECRET, and VONAGE_FROM_NUMBER as environment variables (never hardcode). Enable HTTPS via reverse proxy or platform SSL termination. Set up webhook URLs with public HTTPS endpoints for delivery receipts and inbound messages. Use CI/CD pipelines (GitHub Actions, GitLab CI) to automate testing and deployment.

What Node.js version does Vonage Server SDK v3 require?

Node.js v14 or higher is required for modern JavaScript features (async/await, optional chaining, nullish coalescing). As of 2025, Node.js v18+ LTS is recommended for production deployments. The SDK uses ES modules (import/export), so add "type": "module" to package.json or use .mjs file extensions. Check compatibility at https://github.com/Vonage/vonage-node-sdk.

How do I test Vonage SMS locally during development?

  1. Get trial account with free credits from vonage.com
  2. Whitelist test numbers in Vonage dashboard (Sandbox & Test Numbers)
  3. Use ngrok for webhook testing: ngrok http 3000 creates public URL for localhost:3000
  4. Test with curl or Postman: curl -X POST http://localhost:3000/api/send-sms -H "Content-Type: application/json" -d '{"to":"+15551234567","text":"Test"}'
  5. Check Vonage logs in dashboard under Logs > Messages API to verify submissions and track delivery

This guide provides a solid foundation for sending SMS messages using Node.js, Express, and Vonage. You can build upon this by adding more features like delivery receipt webhooks, handling incoming messages, integrating with databases, or implementing more sophisticated error handling and monitoring.

Frequently Asked Questions

How to send SMS messages with Node.js and Express

Use the Vonage Server SDK for Node.js along with Express. Create an API endpoint that accepts recipient and message details, then leverages the SDK to send the SMS through the Vonage API. This allows you to programmatically integrate SMS capabilities into your applications.

What is the Vonage Server SDK for Node.js?

It's the official library for interacting with Vonage APIs, simplifying communication with their platform for sending SMS messages and other services. The SDK handles the complex details of API calls, letting you focus on application logic.

Why use dotenv with Node.js and Express?

Dotenv loads environment variables from a .env file into process.env. This is crucial for securely managing API credentials, such as your Vonage API key and secret, outside of your codebase, preventing them from being exposed publicly.

When should I whitelist test numbers in Vonage?

Whitelisting is mandatory for trial Vonage accounts. You can only send SMS to verified numbers added to your test list. This prevents abuse of free trial credits and ensures compliance with Vonage's terms of service.

Can I use an Alphanumeric Sender ID with Vonage?

Yes, for some destinations, you can use a Sender ID (like "MyApp") instead of a phone number. However, Sender ID support varies by country and carrier, so check Vonage's documentation for compatibility.

How to set up a Node.js project for sending SMS

Initialize a Node.js project with npm init, install Express, the Vonage Server SDK, and dotenv. Create a .env file to store your Vonage API credentials (key, secret, and 'from' number) and a .gitignore to exclude this file from version control.

What is the purpose of the .env file?

The .env file stores sensitive data, especially your Vonage API key and secret. It keeps configuration and credentials outside of your main code, enhancing security and portability across different environments (e.g., development, production).

How to integrate Vonage API into a Node.js app?

First, obtain your API key and secret from the Vonage dashboard. Then, install the Vonage Server SDK for Node.js and initialize a Vonage client object using your credentials within your application code. This client enables you to send SMS through their API.

What is the correct format for phone numbers with Vonage?

Vonage strongly recommends the E.164 format, which includes a plus sign and the country code (e.g., +15551234567). While the provided code example performs a lenient check, stricter enforcement of E.164 is advisable for production environments to avoid potential issues.

How to handle Vonage API errors in Node.js?

Use try-catch blocks around calls to the sendSms function and examine the error details from the API response. The example code demonstrates how to check message status and extract error text, which you should use to provide more specific error responses to clients.

Why does the "Non-Whitelisted Destination" error occur with Vonage?

Trial Vonage accounts can only send messages to pre-verified numbers in your test list. Add the recipient number to your whitelisted numbers in the Vonage dashboard's "Sandbox & Test Numbers" section.

How to implement rate limiting for the SMS endpoint

Use the express-rate-limit library. Configure it to limit the number of requests from a specific IP address within a time window (e.g., 100 requests every 15 minutes). This protects your application from abuse and helps manage Vonage API costs.

What are best practices for securing a Vonage SMS API integration

Crucially, store API keys and secrets in environment variables (via .env) and never commit them to version control. Implement input validation, rate limiting using express-rate-limit, and always use HTTPS in production.

How to test the Vonage SMS API endpoint

Use tools like curl or Postman to send POST requests to your /api/send-sms endpoint. Include the 'to' and 'text' parameters in the JSON request body. Verify success by receiving the SMS and checking the response for 'success: true' and a message ID. Also, test error cases like invalid inputs.