code examples

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

Vonage SMS API Tutorial: Send SMS with Node.js & Express (2025)

Learn how to integrate Vonage SMS API with Node.js v22 and Express v5. Step-by-step guide with code examples, E.164 formatting, error handling, webhooks, and production deployment best practices.

Send SMS with Node.js, Express, and Vonage

Learn how to build a production-ready Node.js application using the Express framework to send SMS messages via the Vonage SMS API. This comprehensive tutorial covers project setup, API integration, error handling, phone number formatting, and deployment strategies.

By the end of this tutorial, you'll have a functional Express API endpoint that accepts a phone number and message text, then uses Vonage to deliver that message as an SMS.

Project Overview and Goals

Goal: Create a backend service that exposes an API endpoint to send SMS messages reliably using Vonage.

Problem Solved: This provides a foundational component for applications needing programmatic SMS capabilities, such as sending notifications, verification codes, or alerts.

Technologies Used:

  • Node.js: A JavaScript runtime environment ideal for building scalable network applications.
  • Express: A minimal and flexible Node.js web application framework, perfect for creating APIs. Express v5 was released October 15, 2024, with improved async error handling and security enhancements.
  • Vonage SMS API: A powerful API for sending and receiving SMS messages globally. You'll use the @vonage/server-sdk v3.24.1 for Node.js (latest as of 2025).
  • dotenv: A module to load environment variables from a .env file into process.env, keeping sensitive credentials out of source code.

System Architecture:

A client sends a POST request with recipient (to) and message (text) to the Express API /send-sms endpoint. The Express app validates the input and uses the Vonage Node SDK (initialized with API credentials) to make an API call to the Vonage Cloud SMS API. Vonage then attempts to deliver the SMS to the recipient's phone. The Express app returns a success or error JSON response to the client based on the outcome of the Vonage API call.

Prerequisites:

  • Node.js and npm (or yarn): Node.js v20 or later required (Node.js v18 reached EOL on April 30, 2025). Recommend v22 LTS for active support through April 2027. (Download Node.js)
  • Vonage API Account: A free trial account is sufficient to start. You'll need your API Key and API Secret. (Sign up for Vonage)
  • Vonage Phone Number: A virtual number rented from Vonage or the ability to register and verify test numbers (required for trial accounts).
  • Basic understanding of JavaScript and Node.js concepts.
  • A tool for making API requests: Such as curl, Postman, or Insomnia.
  • Critical API Format Note: Vonage SMS API requires phone numbers in E.164 format without the leading '+' sign (e.g., 15551234567, not +15551234567). This differs from standard E.164 notation and is essential for successful API calls. (Source: Vonage API Support documentation, effective 2025)

1. Setting up the project

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

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

    bash
    mkdir vonage-sms-sender
    cd vonage-sms-sender
  2. Initialize Node.js Project: Use npm to create a package.json file. 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 the API, and dotenv for managing environment variables.

    bash
    npm install express @vonage/server-sdk dotenv --save
    • express: The web framework.
    • @vonage/server-sdk: The official Vonage Node.js library.
    • dotenv: To load environment variables from a .env file.
  4. Enable ES Modules: For using modern import/export syntax, open your package.json file and add the following line at the top level:

    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.11.0",
        "dotenv": "^16.3.1",
        "express": "^4.18.2"
      }
    }
    • "type": "module" enables ES Module syntax.
    • The "start" script provides a standard way to run the application.
  5. Create .gitignore: Prevent sensitive files and unnecessary directories from being committed to version control. Create a file named .gitignore in the project root:

    text
    # .gitignore
    
    # Dependencies
    node_modules/
    
    # Environment variables
    .env
    
    # Logs
    logs
    *.log
    npm-debug.log*
    yarn-debug.log*
    yarn-error.log*
    lerna-debug.log*
    
    # OS generated files
    .DS_Store
    Thumbs.db
  6. Create Environment File (.env): This file will store your Vonage credentials and sender number. Never commit this file to Git. Create a file named .env in the project root:

    dotenv
    # .env - Store your Vonage Credentials Here
    # DO NOT COMMIT THIS FILE
    
    # Vonage API Credentials (Get from Vonage Dashboard -> API Settings)
    VONAGE_API_KEY=YOUR_API_KEY
    VONAGE_API_SECRET=YOUR_API_SECRET
    
    # Vonage Virtual Number or Sender ID (Must be owned or registered)
    VONAGE_SENDER_NUMBER=YOUR_VONAGE_NUMBER_OR_ID
    
    # Server Port
    PORT=3000
    • Replace placeholders with your actual credentials later.
  7. Project Structure: Our basic structure will look like this:

    vonage-sms-sender/ ├── node_modules/ ├── .env ├── .gitignore ├── index.js # Main application file (Express server, API endpoint) ├── vonageClient.js # Initializes the Vonage SDK client ├── smsService.js # Contains the function to send SMS ├── package.json └── package-lock.json

2. Implementing Core Functionality (Vonage Integration)

We'll create separate modules for initializing the Vonage client and for the SMS sending logic. This promotes better organization and testability.

  1. Initialize Vonage Client (vonageClient.js): Create a file named vonageClient.js. This module initializes the Vonage SDK using credentials from environment variables.

    javascript
    // vonageClient.js
    import { Vonage } from '@vonage/server-sdk';
    import 'dotenv/config'; // Load .env variables into process.env
    
    // Ensure required environment variables are set
    if (!process.env.VONAGE_API_KEY || !process.env.VONAGE_API_SECRET) {
      console.error('Error: VONAGE_API_KEY and VONAGE_API_SECRET must be set in .env file.');
      process.exit(1); // Exit if credentials are missing
    }
    
    const vonage = new Vonage({
      apiKey: process.env.VONAGE_API_KEY,
      apiSecret: process.env.VONAGE_API_SECRET
    });
    
    console.log('Vonage client initialized successfully.');
    
    export default vonage; // Export the initialized client instance
    • We import Vonage from the SDK and dotenv/config to load the .env file immediately.
    • We add a check to ensure the API key and secret are present, exiting if not.
    • We instantiate Vonage with the credentials.
    • We export the vonage instance for use in other modules.
  2. Create SMS Sending Service (smsService.js): Create a file named smsService.js. This module defines the function responsible for actually sending the SMS using the initialized Vonage client.

    javascript
    // smsService.js
    import vonage from './vonageClient.js'; // Import the initialized client
    import 'dotenv/config';
    
    // Ensure sender number is set
    if (!process.env.VONAGE_SENDER_NUMBER) {
      console.error('Error: VONAGE_SENDER_NUMBER must be set in .env file.');
      process.exit(1);
    }
    
    const from = process.env.VONAGE_SENDER_NUMBER;
    
    /**
     * Sends an SMS message using the Vonage API.
     * @param {string} recipient - The recipient's phone number (E.164 format recommended, e.g., +15551234567).
     * @param {string} messageText - The text content of the SMS message.
     * @returns {Promise<object>} - A promise that resolves with the Vonage API response data on success.
     * @throws {Error} - Throws an error if the SMS sending fails.
     */
    export async function sendSms(recipient, messageText) {
      console.log(`Attempting to send SMS from ${from} to ${recipient}`);
      try {
        // Note: We use vonage.sms.send (part of the older SMS API binding within the SDK)
        // as it directly maps to the simple Key/Secret authentication used here.
        // The vonage.messages.send method often requires Application ID/Private Key auth.
        const responseData = await vonage.sms.send({ to: recipient, from: from, text: messageText });
    
        // Check the status of the first (and usually only) message response
        if (responseData.messages[0]['status'] === '0') {
          console.log(`Message sent successfully to ${recipient}. Message ID: ${responseData.messages[0]['message-id']}`);
          return responseData; // Resolve with the full response
        } else {
          const errorCode = responseData.messages[0]['status'];
          const errorText = responseData.messages[0]['error-text'];
          console.error(`Message failed with error code ${errorCode}: ${errorText}`);
          // Throw a specific error for easier catching
          throw new Error(`Vonage API Error ${errorCode}: ${errorText}`);
        }
      } catch (err) {
        // Handle SDK-level errors (e.g., network issues, invalid credentials before API call)
        console.error('Error sending SMS:', err);
        // Re-throw the error to be handled by the caller (API route)
        throw err;
      }
    }
    • We import the initialized vonage client.
    • We retrieve the VONAGE_SENDER_NUMBER from .env.
    • The sendSms function takes the recipient number (to) and messageText as arguments.
    • It calls vonage.sms.send(), which is suitable for API Key/Secret authentication for basic SMS.
    • It checks the status field in the Vonage response. Status '0' means success.
    • If successful, it logs the success and returns the response.
    • If unsuccessful, it logs the error details provided by Vonage and throws a new Error containing the Vonage error message.
    • A try...catch block handles potential errors during the SDK call itself (like network errors).

3. Building the API Layer

Now, let's create the Express server and the API endpoint that will use our smsService.

  1. Create Express Server (index.js): Create the main application file, index.js.

    javascript
    // index.js
    import express from 'express';
    import 'dotenv/config';
    import { sendSms } from './smsService.js'; // Import our SMS sending 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 / Basic Info
    app.get('/', (req, res) => {
      res.status(200).send('Vonage SMS Sender API is running!');
    });
    
    // --- API Endpoint to Send SMS ---
    app.post('/send-sms', async (req, res) => {
      // 1. Input Validation
      const { to, text } = req.body;
    
      if (!to || !text) {
        console.log('Validation Error: Missing "to" or "text" in request body');
        return res.status(400).json({
          success: false,
          message: 'Missing required fields: "to" (recipient phone number) and "text" (message content).',
        });
      }
    
      // Basic check for phone number format (can be enhanced)
      // E.164 format (e.g., +15551234567) is generally recommended for Vonage
      if (!/^\+?[1-9]\d{1,14}$/.test(to)) {
         console.log(`Validation Error: Invalid phone number format for "to": ${to}`);
         return res.status(400).json({
             success: false,
             message: 'Invalid phone number format. Please use E.164 format (e.g., +15551234567).',
         });
      }
    
    
      try {
        // 2. Call the SMS Service
        console.log(`Received request to send SMS to: ${to}`);
        const result = await sendSms(to, text);
    
        // 3. Send Success Response
        // Optionally, filter the result to return only essential info like message ID
        const messageId = result.messages[0]['message-id'];
        res.status(200).json({
          success: true,
          message: 'SMS sent successfully.',
          messageId: messageId,
          // You might include remaining balance if needed, from result.messages[0]['remaining-balance']
        });
    
      } catch (error) {
        // 4. Send Error Response
        console.error(`Failed to send SMS via API endpoint: ${error.message}`);
    
        // Check for specific Vonage errors if needed, e.g., non-whitelisted number
        // The error message from smsService already contains Vonage details
        let statusCode = 500; // Default to Internal Server Error
        if (error.message.includes("Non-Whitelisted Destination")) {
            statusCode = 403; // Forbidden - specific issue
        } else if (error.message.includes("Invalid Credentials")) {
            statusCode = 401; // Unauthorized
        }
        // Add more specific error code mappings as needed
    
        res.status(statusCode).json({
          success: false,
          message: `Failed to send SMS: ${error.message}`,
        });
      }
    });
    
    // --- Start the Server ---
    app.listen(port, () => {
      console.log(`Server listening on http://localhost:${port}`);
      console.log(`SMS Send Endpoint: POST http://localhost:${port}/send-sms`);
    });
    • We import express and our sendSms function.
    • We set up the Express app and configure middleware (express.json, express.urlencoded) to handle incoming request bodies.
    • A basic / route is added for simple health checks.
    • The POST /send-sms route is defined as async to use await.
    • Input Validation: It first checks if to and text are present in the request body (req.body). If not, it returns a 400 Bad Request error. A basic phone number format check is also included.
    • Service Call: It calls await sendSms(to, text) inside a try...catch block.
    • Success Response: If sendSms resolves successfully, it sends a 200 OK JSON response indicating success and includes the messageId from the Vonage result.
    • Error Handling: If sendSms throws an error (either from the SDK or our explicit checks), the catch block executes. It logs the error and sends an appropriate error status code (e.g., 400, 403, 500) with a JSON response detailing the failure. We map specific error messages to HTTP status codes.
    • Finally, app.listen starts the server on the configured port.

4. Integrating with Vonage API (Configuration and Setup)

This section details how to get the necessary credentials and configure your Vonage account for SMS messaging.

  1. Sign Up/Log In: Go to the Vonage API Dashboard and sign up or log in.

  2. Find API Key and Secret:

    • Navigate to your Dashboard homepage.
    • Your API key and API secret are displayed prominently near the top under the "API keys" section.
    • Dashboard Path: Vonage Dashboard -> API Settings (usually visible on the main dashboard page).
    • Action: Copy these values.
  3. Update .env File: Paste your copied API Key and Secret into your .env file:

    dotenv
    # .env
    VONAGE_API_KEY=YOUR_ACTUAL_API_KEY_HERE
    VONAGE_API_SECRET=YOUR_ACTUAL_API_SECRET_HERE
    # ... other variables
  4. Get a Vonage Sender Number/ID:

    • Paid Accounts: You can purchase virtual numbers. Navigate to Numbers -> Buy numbers. Search for and buy a number with SMS capability. Copy the full number (including country code, e.g., 12015550123).
    • Trial Accounts (IMPORTANT): Trial accounts often cannot send SMS messages to any number initially. You must:
      • Register Test Numbers: Navigate to Numbers -> Test numbers. Add the phone number(s) you intend to send test SMS messages to. You will need to verify ownership via an SMS or voice call code sent by Vonage.
      • Use Your Vonage Number (if provided): Sometimes trial accounts come with a pre-assigned number. Check under Numbers -> Your numbers.
      • Use an Alphanumeric Sender ID (Limited): In some countries, you can use a custom string (like your brand name, e.g., "MyApp") as the sender ID instead of a number. This usually requires registration and may have limitations (e.g., recipients cannot reply). Check Vonage documentation for supported countries and regulations. For simplicity, using a purchased or test-verified number is recommended initially.
    • Dashboard Path: Vonage Dashboard -> Numbers -> (Buy numbers | Your numbers | Test numbers).
    • Action: Obtain your Vonage virtual number or approved Sender ID.
  5. Update .env File: Add your Vonage sender number/ID to your .env file:

    dotenv
    # .env
    VONAGE_API_KEY=YOUR_ACTUAL_API_KEY_HERE
    VONAGE_API_SECRET=YOUR_ACTUAL_API_SECRET_HERE
    VONAGE_SENDER_NUMBER=YOUR_VONAGE_NUMBER_OR_ID_HERE # e.g., 12015550123 or MyAppName
    PORT=3000

Environment Variables Summary:

  • VONAGE_API_KEY: Your unique key for authenticating API requests. Found on the Vonage dashboard.
  • VONAGE_API_SECRET: Your secret key for authentication. Found on the Vonage dashboard. Treat this like a password.
  • VONAGE_SENDER_NUMBER: The phone number (in E.164 format like 15551234567) or registered Alphanumeric Sender ID that will appear as the sender of the SMS. Must be associated with your Vonage account.
  • PORT: The port your Express application will listen on (e.g., 3000).

5. Error Handling and Logging

Our current implementation includes basic error handling and logging:

  • Initialization Checks: vonageClient.js and smsService.js check for essential environment variables on startup and exit if they are missing.
  • API Input Validation: The /send-sms endpoint validates the presence and basic format of to and text fields, returning 400 Bad Request if invalid.
  • Vonage API Error Handling: smsService.js checks the status code from the Vonage API response. If it's not '0', it throws an error including the Vonage error code and message.
  • Catch Block in API: The try...catch block in the /send-sms endpoint catches errors thrown by smsService.js (including Vonage API errors) or other unexpected issues. It logs the error and returns a structured JSON error response with an appropriate HTTP status code (e.g., 500, 403, 401).
  • Logging: We use console.log for informational messages (server start, request received, SMS sent attempt, success) and console.error for errors (missing env vars, validation failures, SMS send failures, API errors).

Further Enhancements (Beyond Basic):

  • Structured Logging: Implement a dedicated logging library (like winston or pino) for structured logging (e.g., JSON format), different log levels (debug, info, warn, error), and routing logs to files or external services.
  • Centralized Error Handling Middleware: Create Express middleware to handle errors consistently across all routes, reducing code duplication in individual route handlers.
  • Retry Mechanisms: For transient network errors or specific Vonage rate limit responses, implement a retry strategy (e.g., exponential backoff) using libraries like async-retry. This is more relevant for critical notifications.

6. Database Schema and Data Layer

Not Applicable for this Basic Guide.

This specific example focuses solely on sending an SMS via an API call and does not require a database to store state, user data, or message history.

For more complex applications (e.g., tracking message delivery status via webhooks, storing user preferences), you would typically integrate a database (like PostgreSQL, MongoDB) and use an ORM (like Prisma, Sequelize) or a database client library.

7. Security Features

While basic, our implementation includes some security considerations:

  • Secrets Management: API keys and secrets are stored in environment variables (.env) and excluded from version control (.gitignore). This is crucial.
  • Input Validation: The /send-sms endpoint performs basic validation on the to and text inputs to prevent obviously malformed requests and potentially mitigate simple injection attempts (though more robust sanitization might be needed depending on how text is used later). The phone number format check adds another layer.

Further Enhancements (Recommended for Production):

  • Rate Limiting: Implement rate limiting on the /send-sms endpoint using middleware like express-rate-limit to prevent abuse and excessive costs.
  • Authentication/Authorization: Secure the API endpoint itself. If this API is not meant to be public, add authentication (e.g., API keys, JWT tokens) to ensure only authorized clients can trigger SMS sending.
  • Input Sanitization: If the text field could ever contain user-generated content that is processed further, use libraries like DOMPurify (if rendering as HTML) or appropriate validation/escaping based on the context to prevent Cross-Site Scripting (XSS) or other injection attacks.
  • Helmet Middleware: Use the helmet middleware for Express to set various HTTP headers that improve security (e.g., X-Content-Type-Options, Referrer-Policy, Strict-Transport-Security).
  • Dependency Scanning: Regularly scan project dependencies for known vulnerabilities using npm audit or tools like Snyk.

8. Handling Special Cases

  • Phone Number Formatting: The Vonage API generally expects phone numbers in E.164 format (e.g., +15551234567). While our basic validation checks for a plausible format, you might need more sophisticated parsing/validation using libraries like libphonenumber-js if dealing with varied user inputs. Our code currently recommends E.164 in the error message.
  • Character Limits & Encoding: Standard SMS messages have character limits (160 for GSM-7, 70 for UCS-2/Unicode). Longer messages are split (concatenated SMS). Vonage handles concatenation automatically, but be mindful of billing (you pay per segment). Unicode characters (like emojis) will reduce the limit per segment significantly. The SDK handles encoding.
  • Sender ID Restrictions: Alphanumeric Sender IDs are not supported in all countries (e.g., the US often requires standard numbers) and may have registration requirements. Using a Vonage virtual number is generally more reliable globally.
  • Delivery Status: This guide only covers sending. To confirm delivery, you need to configure Delivery Receipts (DLRs) by setting up a webhook endpoint in your Vonage application settings and processing the callbacks Vonage sends to it. This is beyond the scope of this basic guide.

9. Performance Optimizations

For this simple application, performance is largely dependent on the Vonage API's response time. However:

  • SDK Client Initialization: We initialize the Vonage SDK client once when the application starts (vonageClient.js) and reuse that instance. Creating a new client for every request would add unnecessary overhead.
  • Asynchronous Operations: The use of async/await ensures that the Node.js event loop is not blocked while waiting for the Vonage API response, allowing the server to handle other requests concurrently.

Further Enhancements (For Higher Scale):

  • Caching: Not directly applicable for sending unique SMS, but if you frequently look up user data before sending, caching that data could improve performance.
  • Load Balancing: For very high throughput, deploy multiple instances of the application behind a load balancer.
  • Connection Pooling: While the SDK manages underlying HTTP connections, ensure your server has sufficient resources (CPU, memory, network) to handle the desired load.

10. Monitoring, Observability, and Analytics

Basic monitoring is achieved through console logging.

Further Enhancements:

  • Health Check Endpoint: The / route serves as a very basic health check. More robust checks could verify connectivity to Vonage (e.g., by making a low-cost API call like checking account balance).
  • Application Performance Monitoring (APM): Integrate APM tools (like Datadog, New Relic, Dynatrace) to get detailed insights into request latency, error rates, resource usage, and distributed tracing.
  • Metrics: Track key metrics like the number of SMS sent successfully, number of failures, API latency, and error types. Expose these via a /metrics endpoint for scraping by systems like Prometheus, or send them directly to a monitoring service.
  • Error Tracking Services: Use services like Sentry or Bugsnag to automatically capture, aggregate, and alert on application errors in real-time.

11. Troubleshooting and Caveats

  • Non-Whitelisted Destination Error (403 Forbidden):
    • Meaning: You are using a Vonage trial account and tried to send an SMS to a phone number that has not been added and verified in your "Test numbers" list.
    • Solution: Go to your Vonage Dashboard -> Numbers -> Test numbers. Add the recipient's phone number and verify it using the code Vonage sends.
  • Invalid Credentials Error (401 Unauthorized):
    • Meaning: The VONAGE_API_KEY or VONAGE_API_SECRET in your .env file is incorrect or does not match the account associated with the VONAGE_SENDER_NUMBER.
    • Solution: Double-check your API Key and Secret in the Vonage Dashboard (API Settings) and ensure they are correctly copied into your .env file. Make sure there are no extra spaces or characters.
  • Invalid Sender Address / Illegal Sender Address Error:
    • Meaning: The VONAGE_SENDER_NUMBER in your .env file is not a valid number associated with your account, is not SMS-capable, or is an Alphanumeric Sender ID that is not permitted or registered for the destination country.
    • Solution: Verify the sender number in your Vonage Dashboard (Numbers -> Your numbers). Ensure it has SMS capabilities enabled. If using an Alphanumeric Sender ID, check regulations and registration status.
  • Missing Environment Variables:
    • Meaning: The application terminated on startup with an error message indicating a required VONAGE_... variable was not found.
    • Solution: Ensure your .env file exists in the project root, is correctly formatted, and contains all required variables (VONAGE_API_KEY, VONAGE_API_SECRET, VONAGE_SENDER_NUMBER). Ensure dotenv/config is imported early in vonageClient.js and smsService.js.
  • Incorrect Content-Type for POST Request:
    • Meaning: Your API client (curl, Postman) sent the request without the Content-Type: application/json header, or sent data in the wrong format. express.json() middleware won't parse the body correctly.
    • Solution: Ensure your API client is sending a POST request with the header Content-Type: application/json and the body is valid JSON (e.g., { "to": "+15551234567", "text": "Hello" }).
  • SMS Not Received:
    • Check the API response/logs for success (status: '0').
    • Verify the recipient number (to) is correct.
    • Check if the recipient device has signal/is not blocked.
    • Check Vonage Dashboard (Logs -> Messaging) for detailed logs of the message attempt.
    • Consider potential carrier filtering on the recipient's end.

12. Deployment and CI/CD

Deployment:

  1. Environment Variables: Deployment platforms (like Heroku, Vercel, AWS Elastic Beanstalk, Docker containers) have specific ways to set environment variables securely. Do not deploy your .env file. Configure VONAGE_API_KEY, VONAGE_API_SECRET, VONAGE_SENDER_NUMBER, and PORT in the platform's settings.
  2. Build Step: Not strictly required for this simple Node.js app unless using TypeScript or a bundler.
  3. Running the App: Ensure the platform runs your application using the start command defined in package.json.
    • package.json script: "start": "node index.js"
    • Command to run: npm start

Example (Conceptual Heroku Deployment):

bash
# Login to Heroku CLI
heroku login

# Create Heroku app
heroku create your-app-name

# Set environment variables
heroku config:set VONAGE_API_KEY=YOUR_ACTUAL_API_KEY_HERE
heroku config:set VONAGE_API_SECRET=YOUR_ACTUAL_API_SECRET_HERE
heroku config:set VONAGE_SENDER_NUMBER=YOUR_VONAGE_NUMBER_OR_ID_HERE
# PORT is usually set automatically by Heroku

# Deploy using Git
git add .
git commit -m "Prepare for Heroku deployment"
git push heroku main # Or your default branch

CI/CD (Conceptual):

A typical CI/CD pipeline (using GitHub Actions, GitLab CI, Jenkins) would:

  1. Trigger: On push to main branch or creation of a Pull Request.
  2. Checkout Code: Get the source code.
  3. Setup Node.js: Specify the Node.js version.
  4. Install Dependencies: Run npm ci (preferred over install in CI for consistency).
  5. Linting/Formatting: Run tools like ESLint or Prettier.
  6. Testing: Run unit and integration tests (see Section 13). Requires setting mock environment variables for tests.
  7. Build (if needed): Run build steps.
  8. Deploy: If tests pass and on the main branch, deploy to the target environment (e.g., using the platform's CLI like heroku deploy).

13. Verification and Testing

Manual Verification:

  1. Run the Application:

    bash
    npm start

    You should see output like:

    Vonage client initialized successfully. Server listening on http://localhost:3000 SMS Send Endpoint: POST http://localhost:3000/send-sms
  2. Send a Test Request (using curl): Open a new terminal window. Replace +15551234567 with a verified test number (if using a trial account) or a valid recipient number, and use your actual VONAGE_SENDER_NUMBER.

    bash
    curl -X POST http://localhost:3000/send-sms \
    -H "Content-Type: application/json" \
    -d '{
      "to": "+15551234567",
      "text": "Hello from Node.js and Vonage! Test at '"$(date)"'"
    }'
  3. Check Application Logs: Look at the terminal where npm start is running. You should see logs indicating the request was received and the attempt to send the SMS.

    • Success: Attempting to send SMS..., Message sent successfully...
    • Failure: Attempting to send SMS..., Message failed with error..., Failed to send SMS via API endpoint...
  4. Check API Response: The curl command should output a JSON response.

    • Success: { "success": true, "message": "SMS sent successfully.", "messageId": "..." }
    • Failure: { "success": false, "message": "Failed to send SMS: Vonage API Error..." } or { "success": false, "message": "Missing required fields..." }
  5. Check Recipient Phone: The phone number specified in the to field should receive the SMS message shortly.

  6. Check Vonage Dashboard: Log in to the Vonage Dashboard and navigate to Logs -> Messaging. You should see a record of the attempted SMS, including its status (e.g., delivered, failed, rejected).

Automated Testing (Conceptual):

  • Unit Tests: Test individual functions in isolation.
    • Test smsService.js: Use a testing framework like jest or mocha. Mock the @vonage/server-sdk to simulate successful and failed API responses from vonage.sms.send without actually calling the API. Verify that sendSms handles responses correctly and throws errors appropriately.
    • Test input validation logic separately.
  • Integration Tests: Test the interaction between components.
    • Use a library like supertest to make HTTP requests to your running Express application (/send-sms endpoint).
    • Mock the smsService.js module to control its behavior during the test (e.g., make it simulate success or failure) and assert that the API endpoint returns the correct HTTP status code and JSON response based on the mocked service behavior. This avoids hitting the actual Vonage API during tests.

Frequently Asked Questions (FAQ)

What is the correct phone number format for Vonage SMS API?

Vonage SMS API requires phone numbers in E.164 format without the leading '+' sign. Use format like 15551234567 (country code + number) instead of +15551234567. This differs from standard E.164 notation and is critical for successful API calls. The Vonage API sends exactly what you input, so formatting must be correct before sending.

How do I get Vonage API credentials?

Log in to your Vonage API Dashboard at dashboard.nexmo.com. Your API Key and API Secret are displayed prominently on the main dashboard page under "API Settings." Copy these values and store them securely in your .env file. Never commit these credentials to version control.

Can I send SMS with a Vonage trial account?

Yes, but with restrictions. Trial accounts require you to register and verify recipient phone numbers before sending. Navigate to your Vonage Dashboard → Numbers → Test numbers, add each recipient's phone number, and verify it using the code Vonage sends. Trial accounts typically cannot send to unverified numbers.

What Node.js version should I use for Vonage SMS integration?

Use Node.js v20 or later. Node.js v18 reached end-of-life on April 30, 2025, and no longer receives security updates. We recommend Node.js v22 LTS "Jod" for the longest support timeline (active support through April 2027). Express v5 requires Node.js v18 minimum but v20+ is recommended.

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

This error occurs when using a trial account to send SMS to an unverified phone number. Solution: Add the recipient's phone number to your Test numbers list in the Vonage Dashboard (Numbers → Test numbers) and complete the verification process. Once verified, you can send SMS to that number.

How do I handle Vonage SMS API rate limits?

Vonage sets a default rate limit of 30 API requests per second per API key (up to 2,592,000 SMS per day). For US 10DLC traffic, additional throughput limits apply based on your brand vetting score and campaign type. Implement retry logic with exponential backoff for rate limit errors, and contact Vonage support for higher throughput if needed.

What's the difference between vonage.sms.send and vonage.messages.send?

vonage.sms.send() works with API Key/Secret authentication and is suitable for basic SMS sending (used in this tutorial). vonage.messages.send() requires Application ID and Private Key authentication and supports multiple channels (SMS, MMS, WhatsApp, Viber). For simple SMS-only use cases with Key/Secret auth, use vonage.sms.send().

How do I verify SMS delivery with Vonage?

Implement delivery receipts (DLRs) by configuring a webhook endpoint in your Vonage application settings. Vonage sends POST requests to your webhook URL with delivery status updates. You'll need to create an endpoint like /webhooks/vonage/delivery-receipt to receive and process these callbacks. Status codes include: delivered, failed, rejected, expired.

Can I use alphanumeric sender IDs with Vonage?

Yes, but with country-specific restrictions. Alphanumeric sender IDs (3-11 characters like "MyBrand") are not supported in all countries (e.g., US requires numeric long codes or short codes). Many countries require sender ID registration through Vonage's Global Sender ID Portal, with processing times of 7-14 business days. Check country-specific requirements before implementation.

How do I deploy a Vonage SMS application to production?

Configure environment variables (VONAGE_API_KEY, VONAGE_API_SECRET, VONAGE_SENDER_NUMBER) securely in your hosting platform (Heroku, AWS, Vercel, etc.). Never deploy your .env file. Implement production-ready features: rate limiting, authentication/authorization, structured logging, error tracking (Sentry), health check endpoints, and monitoring. Use npm ci in CI/CD pipelines for consistent dependency installation.

Frequently Asked Questions

How to send SMS with Node.js and Express?

Use the Vonage SMS API and the Express.js framework. Install the necessary dependencies (`npm install express @vonage/server-sdk dotenv`), set up an Express server, and create a route that handles sending messages via the Vonage API.

What is the Vonage SMS API?

The Vonage SMS API is a service that enables you to send and receive text messages programmatically. Use the `@vonage/server-sdk` package to interact with the API from your Node.js application. This API allows sending SMS globally.

Why use dotenv with Vonage API keys?

Dotenv helps manage environment variables, storing sensitive data like API keys outside your source code. Create a `.env` file to store your `VONAGE_API_KEY` and `VONAGE_API_SECRET`, preventing accidental exposure in version control.

How to set up a Vonage SMS API project?

Install the Vonage Server SDK (`@vonage/server-sdk`), Express.js, and dotenv. Set up environment variables for your Vonage API key and secret, as well as your Vonage phone number. Create an Express server with an endpoint to handle SMS sending.

What is Vonage sender number?

The Vonage sender number or ID is what recipients see as the message origin. It can be a virtual number you rent from Vonage, or an alphanumeric Sender ID (with limitations). This number or ID must be linked to your Vonage account.

When should I validate phone numbers for Vonage?

Always validate phone numbers upon receiving them in your API requests. The recommended format is E.164 (e.g., +15551234567). This ensures messages are sent to the correct recipients and minimizes errors from the Vonage API.

How to handle Vonage API errors?

Implement robust error handling by using try-catch blocks around Vonage API calls and checking the status code in the API response. The Vonage API returns a status code of '0' for success, other codes indicate errors. Handle errors gracefully with appropriate logging and responses to the client.

How to check SMS delivery status with Vonage?

Configure Delivery Receipts (DLRs) by setting up a webhook endpoint in your Vonage application settings. Vonage sends callbacks to this endpoint, providing real-time delivery status updates.

What is an alphanumeric sender ID in Vonage?

An alphanumeric sender ID is a custom text string (e.g., your brand name) that can be used as the sender instead of a phone number. This feature has limitations: not available in all countries and may prevent recipients from replying. Trial accounts may allow this option when numbers are restricted.

Can I use emojis in Vonage SMS messages?

Yes, but be aware that emojis and other Unicode characters consume more space in SMS messages. Standard messages have a limit of 160 characters (GSM-7 encoding) or 70 characters (UCS-2 encoding for Unicode). Longer messages are split, impacting cost (charged per segment).

How to test Vonage SMS integration locally?

Use a tool like `curl` or Postman to make POST requests to your local Express server's SMS endpoint. Provide test data, including the recipient number and message text, and verify the responses and logs. If using a trial account, ensure the recipient numbers are registered as test numbers in the Vonage Dashboard.

What are common troubleshooting tips for Vonage SMS?

Check for the following: `Non-Whitelisted Destination` errors when using trial accounts, `Invalid Credentials` errors due to incorrect API keys, `Invalid Sender Address` issues from problems with your Vonage number or ID, or general network problems.

How to implement rate limiting for Vonage SMS?

Use middleware like `express-rate-limit` to restrict the number of SMS messages sent from your API endpoint within a specific time window. This helps prevent abuse, reduces costs, and stays within Vonage API limits.