code examples

Sent logo
Sent TeamMar 8, 2026 / code examples / Article

Send SMS with Vonage Messages API: Node.js Express Tutorial 2025

Build an SMS sending API with Vonage Messages API, Node.js, and Express. Complete guide with JWT authentication, error handling, and production deployment. Updated January 2025.

Note on Article Scope: This guide covers sending SMS using Vonage (formerly Nexmo) Messages API with Express.js. Despite the filename reference to Twilio/Next.js/NextAuth, this article focuses specifically on Vonage implementation. For Twilio-specific implementations or Next.js/NextAuth integrations, refer to provider-specific documentation.

Last Updated: January 2025. SDK versions and API endpoints verified as of this date.

Send SMS with Vonage Messages API: Node.js Express Tutorial

Build a Node.js application using Express to send SMS messages via the Vonage Messages API. This guide covers everything from project setup and configuration to sending your first message and handling potential issues.

By the end of this tutorial, you'll have a functional Express API endpoint that accepts requests and dispatches SMS messages programmatically – enabling your applications to send notifications, alerts, verification codes, or engage users through SMS.

Project Overview and Goals

What You're Building: A simple Node.js server using Express. Your server will expose a single API endpoint (/send-sms) that accepts a destination phone number and message text, then uses the Vonage Messages API to send the SMS.

Problem Solved: This application provides a basic building block for integrating programmatic SMS sending capabilities into larger systems. It abstracts direct interaction with the Vonage SDK into a reusable API service.

Technologies Used:

  • Node.js: A JavaScript runtime environment for building server-side applications.
  • Express: A minimal and flexible Node.js web application framework for creating API endpoints.
  • Vonage Node.js SDK (@vonage/server-sdk): The official library for interacting with Vonage APIs (version 3.24.1 as of January 2025). Version 3.x features Promise-based API (async/await support), TypeScript support for code completion, and parameter objects for better function management.
  • dotenv: A zero-dependency module that loads environment variables from a .env file into process.env.
  • Vonage Account & API Credentials: Required for authenticating requests to the Vonage platform using JWT (JSON Web Token) authentication with Application ID and private key.

System Architecture:

Your architecture is straightforward:

text
+-------------+       +---------------------+       +-----------------+       +--------------+
|   Client    |------>| Node.js/Express API |------>| Vonage Node SDK |------>| Vonage Cloud |
| (e.g. CURL,|       |  (Listens on /send-sms) |       | (@vonage/server-sdk)|       | (Messages API)|
|  Postman)   |       +---------------------+       +-----------------+       +--------------+
+-------------+                  |                                                  |
                                 |                                                  |
                                 +------------------ SMS Sent ----------------------+-----> User's Phone
  1. Your client sends an HTTP POST request to the /send-sms endpoint of the Express application.
  2. Express receives the request and validates the payload (recipient number, message).
  3. The application uses the Vonage Node.js SDK, initialized with your credentials, to call the Vonage Messages API.
  4. Vonage processes the request and delivers the SMS message to the recipient's phone.

Prerequisites:

  • Node.js and npm (or yarn): Install on your development machine. Download Node.js
  • Vonage API Account: Sign up for a free account – you'll get free credits to start. Vonage Sign Up
  • A Vonage Phone Number: Rent a virtual number from Vonage capable of sending SMS through the Vonage API Dashboard.
  • Basic understanding of JavaScript and REST APIs.

1. Set Up the Project

Initialize your Node.js project and install the necessary dependencies.

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

    bash
    mkdir vonage-sms-sender
    cd vonage-sms-sender
  2. Initialize npm Project: Create a package.json file to manage your project's dependencies and scripts.

    bash
    npm init -y

    The -y flag accepts default settings.

  3. Install Dependencies: Install Express for the web server, the Vonage SDK to interact with the API, and dotenv to manage environment variables securely.

    bash
    npm install express @vonage/server-sdk dotenv
  4. Create Project Files: Create the main application file and a file for environment variables.

    • index.js: Contains your Express server and Vonage integration logic.
    • .env: Stores sensitive credentials like API keys and phone numbers. Never commit this file to version control.
    • .gitignore: Prevents accidentally committing sensitive files or unnecessary directories.

    Create these files using your code editor or the terminal:

    bash
    touch index.js .env .gitignore
  5. Configure .gitignore: Add node_modules and .env to your .gitignore file to ensure they aren't tracked by Git. Add private.key preemptively – it will be generated later.

    text
    # .gitignore
    
    node_modules/
    .env
    private.key

Your project structure should now look like this:

text
vonage-sms-sender/
├── .env
├── .gitignore
├── index.js
├── node_modules/
├── package-lock.json
└── package.json

2. Integrate with Vonage

Configure Vonage-specific settings and obtain the necessary credentials. The Messages API uses an Application ID and Private Key for authentication.

  1. Log in to Vonage: Access your Vonage API Dashboard.

  2. Verify Default SMS API: Navigate to API Settings (Dashboard → API settings). Under SMS settings, ensure Messages API is selected as the default API for sending SMS messages. Save changes if necessary. This ensures webhooks (if you use them later) and API behavior align with the SDK methods you'll use.

  3. Create a Vonage Application:

    • Navigate to Your applications in the dashboard menu.
    • Click Create a new application.
    • Give your application a meaningful name (e.g., NodeJS SMS Sender).
    • Click Generate public and private key. This automatically downloads a private.key file. Save this file securely within your project directory (e.g., directly in vonage-sms-sender/). Remember you added private.key to .gitignore.
    • Note the Application ID displayed on the page – you'll need this.
    • Enable the Messages capability for this application.
    • You'll see fields for Inbound URL and Status URL. For sending SMS, these aren't strictly required to be functional endpoints, but application setup often requires them. Enter placeholder URLs like http://localhost:3000/webhooks/inbound and http://localhost:3000/webhooks/status for now. If you later implement receiving messages or delivery receipts, update these with real, publicly accessible URLs (using a tool like ngrok during development).
    • Click Generate new application.
  4. Link Your Vonage Number:

    • After creating the application, you'll be taken to its configuration page.
    • Scroll down to the Link virtual numbers section.
    • Find the Vonage number you rented (or rent one if needed) and click the Link button next to it. This associates incoming/outgoing messages on that number with this specific application's configuration and credentials.
  5. Configure Environment Variables: Open the .env file you created and add your Vonage credentials and number.

    dotenv
    # .env
    
    # Vonage Credentials (Messages API)
    VONAGE_APPLICATION_ID=YOUR_APPLICATION_ID
    VONAGE_PRIVATE_KEY_PATH=./private.key # Path relative to your project root
    
    # Vonage Number linked to the application
    VONAGE_NUMBER=YOUR_VONAGE_PHONE_NUMBER # e.g., 14155550100
    
    # API Server Port
    PORT=3000
    • Replace YOUR_APPLICATION_ID with the Application ID from step 3.
    • Ensure VONAGE_PRIVATE_KEY_PATH points to the correct location of your downloaded private.key file. ./private.key assumes it's in the project root.
    • Replace YOUR_VONAGE_PHONE_NUMBER with the full Vonage number (including country code, no symbols) that you linked to the application in step 4.
    • PORT defines the port your Express server will listen on.

Security Note: The .env file and private.key contain sensitive credentials. Ensure they are never committed to version control. Use environment variable management tools provided by your deployment platform (e.g., Heroku Config Vars, AWS Secrets Manager, Docker secrets) in production environments. See Section 12 for more details on handling the private key in deployment.

3. Implement Core Functionality (Send SMS)

Write the Node.js code in index.js to set up the Express server and use the Vonage SDK.

javascript
// index.js

// 1. Import necessary modules
require('dotenv').config(); // Load environment variables from .env file
const express = require('express');
const fs = require('fs'); // Import file system module
const { Vonage } = require('@vonage/server-sdk');

// 2. Initialize Express App
const app = express();
const port = process.env.PORT || 3000; // Use port from .env or default to 3000

// 3. Initialize Vonage SDK
// Ensure environment variables are loaded correctly
if (!process.env.VONAGE_APPLICATION_ID || !process.env.VONAGE_PRIVATE_KEY_PATH || !process.env.VONAGE_NUMBER) {
    console.error('ERROR: Missing Vonage credentials in .env file.');
    console.error('Please ensure VONAGE_APPLICATION_ID, VONAGE_PRIVATE_KEY_PATH, and VONAGE_NUMBER are set.');
    process.exit(1); // Exit if credentials are missing
}

let privateKeyValue;
try {
    // Read the private key file content
    privateKeyValue = fs.readFileSync(process.env.VONAGE_PRIVATE_KEY_PATH);
} catch (err) {
    console.error(`ERROR: Could not read private key file at path: ${process.env.VONAGE_PRIVATE_KEY_PATH}`);
    console.error(err.message);
    process.exit(1); // Exit if key file is not readable
}

const vonage = new Vonage({
    applicationId: process.env.VONAGE_APPLICATION_ID,
    privateKey: privateKeyValue // Use the key content directly
});

// 4. Middleware Setup
app.use(express.json()); // Enable parsing of JSON request bodies
app.use(express.urlencoded({ extended: true })); // Enable parsing of URL-encoded request bodies

// 5. Define the SMS Sending Route (implemented in the next section)
// app.post('/send-sms', ...);

// 6. Basic Root Route (Optional: for testing server is running)
app.get('/', (req, res) => {
    res.send('Vonage SMS Sender API is running!');
});

// 7. Start the Server
app.listen(port, () => {
    console.log(`Server listening at http://localhost:${port}`);
});

Explanation:

  1. Imports: Import dotenv, express, the fs module for file reading, and the Vonage class.
  2. Express Init: Standard Express setup.
  3. Vonage Init: Instantiate the Vonage SDK client.
    • First check that required environment variables (VONAGE_APPLICATION_ID, VONAGE_PRIVATE_KEY_PATH, VONAGE_NUMBER) are set.
    • Use fs.readFileSync() to read the content of the private key file specified by the VONAGE_PRIVATE_KEY_PATH environment variable. A try...catch block handles potential errors if the file cannot be read.
    • Pass the applicationId from process.env and the privateKey content (read from the file) to the Vonage constructor.
  4. Middleware: express.json() and express.urlencoded() parse request bodies.
  5. Route Placeholder: Add the /send-sms route logic next.
  6. Root Route: A simple / route for basic server checks.
  7. Server Start: app.listen starts the server.

4. Build the API Layer

Implement the /send-sms endpoint. This endpoint receives a POST request containing the recipient's phone number (to) and the message text (text).

Add this code block to your index.js file, replacing the // app.post('/send-sms', ...); placeholder:

javascript
// index.js (continued...)

// 5. Define the SMS Sending Route
app.post('/send-sms', async (req, res) => {
    console.log(`Received request to /send-sms:`, req.body); // Log incoming request

    // Basic Input Validation
    const { to, text } = req.body;
    if (!to || !text) {
        console.error(`Validation Error: 'to' and 'text' fields are required.`);
        return res.status(400).json({ success: false, message: ""'to' and 'text' fields are required."" });
    }

    // Validate 'to' number format (basic E.164 check)
    // This regex checks for an optional '+' followed by 1 to 15 digits.
    const e164Regex = /^\+?[1-9]\d{1,14}$/;
    if (!e164Regex.test(to)) {
         console.error(`Validation Error: Invalid 'to' number format: ${to}`);
         return res.status(400).json({ success: false, message: "Invalid 'to' number format. Use E.164 format (e.g., +14155550100)." });
    }
    // Note: This regex provides basic E.164 format checking (max 15 digits, starts with 1-9).
    // For production applications, consider using a comprehensive phone number validation library
    // (e.g., google-libphonenumber, awesome-phonenumber) for stricter validation including
    // country-specific rules, valid area codes, and number length verification.

    const fromNumber = process.env.VONAGE_NUMBER;

    try {
        console.log(`Attempting to send SMS from ${fromNumber} to ${to}`);

        const resp = await vonage.messages.send({
            message_type: ""text"",
            to: to,
            from: fromNumber,
            channel: ""sms"",
            text: text
        });

        console.log(`Vonage API Response:`, resp); // Log Vonage response

        // Check for successful message dispatch
        // Note: A successful API call (`resp.message_uuid` exists) doesn't guarantee delivery,
        // only that Vonage accepted the message. Delivery receipts are needed for confirmation.
        if (resp.message_uuid) {
             console.log(`Message successfully sent with UUID: ${resp.message_uuid}`);
             res.status(200).json({
                 success: true,
                 message: ""SMS message submitted successfully."",
                 message_uuid: resp.message_uuid
             });
        } else {
            // This case might indicate an issue even if no error was thrown, examine 'resp'
             console.error(`Error: Message submission failed, no message_uuid received.`, resp);
             res.status(500).json({
                 success: false,
                 message: ""Failed to send SMS message. Vonage response did not include a message_uuid."",
                 details: resp // Include Vonage response for debugging if appropriate
             });
        }

    } catch (err) {
        console.error(`Error sending SMS via Vonage:`, err); // Log the full error

        // Provide a more user-friendly error response
        res.status(500).json({
            success: false,
            message: ""Failed to send SMS message due to an internal error."",
            // Optionally include error details in non-production environments
            // error: process.env.NODE_ENV !== 'production' ? err.message : undefined,
            // errorDetails: process.env.NODE_ENV !== 'production' ? err : undefined
        });
    }
});

// ... (rest of index.js: app.get('/'), app.listen(...) )

Explanation:

  1. Route Definition: Define an async handler for POST /send-sms.
  2. Logging: Log the incoming request body.
  3. Input Validation:
    • Extract to and text.
    • Check for presence; return 400 Bad Request if missing.
    • Perform a basic E.164 regex check on the to number. Return 400 if invalid. As noted, more robust validation might be needed in production.
  4. Vonage Call:
    • Use try...catch for error handling.
    • Call vonage.messages.send() with parameters: message_type, to, from (from .env), channel, text.
  5. Response Handling:
    • If the call succeeds and returns a message_uuid, log success and return 200 OK with the UUID.
    • If the call succeeds but lacks a message_uuid, log an error and return 500 Internal Server Error.
  6. Error Handling:
    • If vonage.messages.send() throws, the catch block logs the detailed error and returns a generic 500 Internal Server Error to the client.

5. Handle Errors, Logging, and Retries

Your current implementation includes basic try...catch and console.log/console.error. For production:

  • Consistent Error Strategy: Use a standardized JSON error format.

  • Logging: Use a library like Winston or Pino for structured logging (e.g., JSON), configurable levels, and directing output to files or log services.

    javascript
    // Example using a hypothetical logger setup
    // logger.info({ message: 'Received request', body: req.body });
    // logger.error({ message: 'Vonage API Error', error: err, stack: err.stack });
  • Retry Mechanisms: Use libraries like async-retry for transient errors (network issues, some 5xx errors from Vonage). Caution: Don't retry 4xx errors (invalid input, authentication) without fixing the cause.

    javascript
    // Conceptual example using async-retry
    const retry = require('async-retry');
    
    // Inside the POST /send-sms handler...
    try {
      const resp = await retry(async (bail, attempt) => {
        console.log(`Attempt ${attempt} to send SMS...`);
        // The bail function is crucial: call bail(new Error('Non-retryable error'))
        // if you detect an error that should *not* be retried (e.g., 4xx status code from Vonage).
        try {
            const vonageResp = await vonage.messages.send({ /* ... message params ... */ });
             // Example check: If Vonage responds with an error indicating bad input (e.g., a 400 status internally)
             // if (vonageResp.someInternalErrorCode === 'BAD_REQUEST') {
             //    bail(new Error('Bad request, not retrying.'));
             // }
            if (!vonageResp.message_uuid) {
                // Decide if lack of UUID is retryable or needs bailing
                // bail(new Error('Non-retryable: Vonage accepted but failed without UUID.'));
                throw new Error('Message submission failed, no message_uuid received.'); // Make it retryable by default? Or bail?
            }
            return vonageResp; // Return successful response
        } catch (error) {
            // Example check: Bail on specific error types (e.g., authentication errors)
            // if (error.statusCode === 401) { // Hypothetical status code check
            //     bail(new Error('Authentication error, not retrying.'));
            // }
            console.warn(`Attempt ${attempt} failed: ${error.message}`);
            throw error; // Re-throw error to trigger retry
        }
      }, {
        retries: 3,
        factor: 2,
        minTimeout: 1000,
        onRetry: (error, attempt) => {
          console.warn(`Retrying Vonage API call (attempt ${attempt}) after error: ${error.message}`);
        }
      });
    
      // Process successful response after retries
      console.log(`Message successfully sent with UUID: ${resp.message_uuid}`);
      res.status(200).json({ /* ... success response ... */ });
    
    } catch (err) {
      // Handle final error after all retries failed
      console.error(`Error sending SMS after retries:`, err);
      res.status(500).json({ /* ... final error response ... */ });
    }
    • Clearly define which errors are transient and which should cause the bail function to be called to stop retries immediately.
  • Testing Errors: Simulate errors (invalid credentials, network blocks, invalid input, Vonage test numbers).

6. Database Schema and Data Layer

This guide focuses purely on sending and doesn't use a database. A real application might store message history, user data, or delivery statuses.

  • Schema (Example):
    sql
    CREATE TABLE messages (
        id SERIAL PRIMARY KEY,
        message_uuid VARCHAR(255) UNIQUE,
        recipient_number VARCHAR(20) NOT NULL,
        sender_number VARCHAR(20) NOT NULL,
        message_text TEXT,
        status VARCHAR(50) DEFAULT 'submitted',
        submitted_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
        last_updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
    );
  • Data Layer: Use an ORM like Prisma or Sequelize.

7. Implement Security Features

  • Input Validation/Sanitization: Use libraries like express-validator. Sanitize text if displayed elsewhere.

  • Rate Limiting: Use express-rate-limit to prevent abuse.

    javascript
    const rateLimit = require('express-rate-limit');
    const smsLimiter = rateLimit({
        windowMs: 15 * 60 * 1000, // 15 minutes
        max: 10, // Limit each IP to 10 requests per windowMs
        message: { success: false, message: 'Too many SMS requests, please try again later.' }
    });
    app.post('/send-sms', smsLimiter, async (req, res) => { /* ... route handler ... */ });
  • Authentication/Authorization: Protect the endpoint (API key, JWT, etc.).

  • Common Vulnerabilities: Guard against OWASP Top 10 (Injection, Broken Auth, etc.).

  • HTTPS: Use HTTPS in production (via reverse proxy like Nginx/Caddy).

8. Handle Special Cases

  • Internationalization: Use E.164 format (+14155550100). Be aware of character limits and encoding:
    • GSM-7 encoding (standard characters A-Z, 0-9, basic punctuation): Single message up to 160 characters. Multi-part messages: 153 characters per segment (7 characters reserved for User Data Header/UDH for message reassembly).
    • UCS-2/Unicode encoding (emoji, special characters, non-Latin scripts, Chinese/Arabic characters): Single message up to 70 characters. Multi-part messages: 67 characters per segment (6-byte UDH overhead).
    • Automatic fallback: Using even one emoji or special character (including "curly quotes" like " or ", or accented characters like 'á') automatically switches the entire message to UCS-2 encoding, reducing capacity from 160 to 70 characters. Text editors that auto-replace straight quotes with smart quotes can trigger this unexpectedly.
  • Character Limits: Longer messages are automatically split into multiple segments and billed accordingly. A 200-character GSM-7 message counts as 2 segments (153 + 47 characters). Plan message content to stay within single-segment limits when possible to minimize costs.
  • Vonage Number Capabilities: Ensure your number is SMS-enabled for the destination country. Check Vonage Dashboard for number capabilities and supported regions. Some countries have restrictions on sender ID types or require pre-registration.

9. Performance Optimizations

  • Asynchronous Processing: For high volume, queue requests (RabbitMQ, Redis) and process them with background workers.
  • Connection Pooling: Handled by SDK; ensure server resources are adequate.
  • Load Testing: Use k6, Artillery etc. to find bottlenecks.

10. Monitoring, Observability, and Analytics

  • Health Checks: Add a /health endpoint.
    javascript
    app.get('/health', (req, res) => { res.status(200).json({ status: 'UP' }); });
  • Performance Metrics: Monitor latency, RPM, error rates (Prometheus/Grafana, Datadog).
  • Error Tracking: Use Sentry, Bugsnag etc.
  • Vonage Dashboard: Monitor usage, logs, delivery rates.

11. Troubleshooting and Caveats

  • Error: Authentication failed / Invalid Credentials:
    • Check VONAGE_APPLICATION_ID in .env.
    • Verify VONAGE_PRIVATE_KEY_PATH is correct and the file is readable.
    • Ensure private.key content is unchanged.
  • Error: Non-Whitelisted Destination (Trial Accounts):
    • Add recipient number to Test numbers in Vonage dashboard for trial accounts.
    • Ensure your paid number is linked to the correct Vonage Application.
  • Error: Invalid Sender / Illegal Sender Address:
    • Confirm VONAGE_NUMBER in .env is correct, owned, and linked.
    • Check number's SMS capability for the destination.
  • Message Not Received:
    • Check Vonage Dashboard Logs for the message_uuid.
    • Verify recipient number format (E.164).
    • Check for carrier filtering.
    • Implement webhooks for Delivery Receipts (DLRs) for reliable status.
  • private.key Path/Read Issues: Ensure path in .env is correct relative to execution, or use absolute path. Check file permissions. The code in Section 3 now includes a check for file readability on startup.
  • Missing .env values: The startup check helps. Test with node -r dotenv/config -e 'console.log(process.env.VONAGE_APPLICATION_ID)'.

12. Deployment and CI/CD

  • Deployment:
    • Choose a platform (Heroku, AWS, GCP, Docker, etc.).
    • Crucially: Configure environment variables (VONAGE_APPLICATION_ID, VONAGE_PRIVATE_KEY_PATH or VONAGE_PRIVATE_KEY_CONTENT, VONAGE_NUMBER, PORT) securely via the platform. Do not commit .env.
    • Handling the Private Key:
      • Option 1 (File Path): Copy the private.key file securely during deployment and set VONAGE_PRIVATE_KEY_PATH to its location on the server. Ensure file permissions are correct. The current code in index.js uses this method.
      • Option 2 (Content in Env Var - Recommended): Store the entire content of the private.key file in a secure environment variable (e.g., VONAGE_PRIVATE_KEY_CONTENT). This is often more secure and easier for PaaS platforms. If you use this method, you must modify the SDK initialization in index.js to:
        javascript
        // Replace the fs.readFileSync logic with this:
        // Ensure you also update the initial credential check for VONAGE_PRIVATE_KEY_CONTENT instead of _PATH
        if (!process.env.VONAGE_APPLICATION_ID || !process.env.VONAGE_PRIVATE_KEY_CONTENT || !process.env.VONAGE_NUMBER) {
            console.error('ERROR: Missing Vonage credentials in .env file.');
            console.error('Please ensure VONAGE_APPLICATION_ID, VONAGE_PRIVATE_KEY_CONTENT, and VONAGE_NUMBER are set.');
            process.exit(1); // Exit if credentials are missing
        }
        
        const vonage = new Vonage({
            applicationId: process.env.VONAGE_APPLICATION_ID,
            privateKey: process.env.VONAGE_PRIVATE_KEY_CONTENT // Directly use the key content from env var
        });
    • Run npm install during deployment.
    • Start with node index.js or pm2.
  • CI/CD:
    • Set up a pipeline (GitHub Actions, GitLab CI, Jenkins) for linting, testing, building (e.g., Docker image), and deploying.
    • Manage environment variables securely in CI/CD settings.

13. Verification and Testing

  • Manual Verification:

    1. Ensure .env is configured with your credentials, number, and the correct VONAGE_PRIVATE_KEY_PATH.

    2. Start the server: node index.js

    3. Use curl or Postman to send a POST request. Important: Replace the placeholder to number with a phone number you have verified in your Vonage account, especially if using a trial account (see Troubleshooting point about whitelisted numbers).

      Using curl:

      bash
      curl -X POST http://localhost:3000/send-sms \
      -H ""Content-Type: application/json"" \
      -d '{
            ""to"": ""+15551234567"",
            ""text"": ""Hello from Node.js and Vonage!""
          }'

      Replace +15551234567 with YOUR verified test phone number (E.164 format).

      Using Postman:

      • Set Request Type: POST
      • Set URL: http://localhost:3000/send-sms
      • Go to Body -> raw -> Select JSON
      • Enter the JSON payload:
        json
        {
          ""to"": ""+15551234567"",
          ""text"": ""Hello from Node.js and Vonage!""
        }
        Replace +15551234567 with YOUR verified test phone number (E.164 format).
      • Click Send.
    4. Check Server Logs: Look for Received request, Attempting to send SMS, and Vonage API Response logs.

    5. Check API Response: Expect JSON success (200 OK with message_uuid) or error (400, 500).

    6. Check Your Phone: Verify the SMS arrives on the number you specified.

  • Automated Testing:

    • Unit Tests: Use Jest or Mocha/Chai. Mock @vonage/server-sdk to test validation, parsing, formatting without real API calls.
    • Integration Tests: Use supertest to test the API endpoint. Mock Vonage or use a dedicated test environment/credentials for limited real API calls.

This guide provides a solid foundation for sending SMS messages using Node.js, Express, and the Vonage Messages API. Remember to consult the official Vonage Messages API documentation for more advanced features and details.

Frequently Asked Questions (FAQ)

How do I send SMS with Vonage in Node.js?

To send SMS with Vonage in Node.js, install the @vonage/server-sdk package (version 3.24.1 or later), create a Vonage application with Messages API capability, generate a private key for JWT authentication, and use the vonage.messages.send() method with your Application ID and private key credentials.

What is the difference between Vonage SMS API and Messages API?

The Vonage Messages API is the newer, more flexible API that supports multiple channels (SMS, MMS, WhatsApp, Viber) with unified authentication using JWT. The older SMS API uses API key/secret authentication and only supports SMS. For new applications, use the Messages API with Application ID and private key authentication.

How do I authenticate with Vonage Messages API?

Vonage Messages API uses JWT (JSON Web Token) authentication. Generate a public/private key pair when creating your Vonage application, download the private.key file, and use it with your Application ID to authenticate. The Node.js SDK (@vonage/server-sdk v3.24.1+) automatically generates JWTs for you when properly configured.

What is the character limit for Vonage SMS messages?

Vonage SMS supports two encoding types: GSM-7 allows 160 characters in a single message (153 per segment for multi-part), while UCS-2/Unicode allows 70 characters (67 per segment). Using any emoji or special character automatically switches to UCS-2, reducing capacity from 160 to 70 characters.

How much does Vonage SMS cost?

Vonage SMS pricing varies by destination country. New accounts receive free trial credits. Check the Vonage pricing page for current rates. Messages are billed per segment, so a 200-character GSM-7 message counts as 2 segments.

Can I use Vonage with Express.js?

Yes, Vonage works seamlessly with Express.js. Create an Express endpoint that handles POST requests, validate the input (phone number and message), and call vonage.messages.send() using the @vonage/server-sdk. The SDK supports async/await syntax for clean Promise-based code.

What is E.164 phone number format?

E.164 is the international phone number format that starts with a + followed by country code and subscriber number, up to 15 digits total (e.g., +14155550100). Vonage requires phone numbers in E.164 format for reliable SMS delivery. The format ensures proper international routing.

How do I handle Vonage SMS errors in Node.js?

Wrap Vonage API calls in try-catch blocks to handle errors. Common errors include authentication failures (invalid Application ID or private key), non-whitelisted destinations (trial accounts), and invalid sender numbers. Check the error message and status code to determine if errors are retryable (5xx) or need immediate fixes (4xx).

Do I need a Vonage phone number to send SMS?

Yes, you need a Vonage virtual phone number to send SMS. Rent a number through the Vonage Dashboard, ensure it has SMS capability for your target countries, and link it to your Vonage application. The linked number becomes your sender ID for outbound SMS messages.

How do I deploy Vonage SMS application to production?

For production deployment, store your Vonage credentials securely using environment variables (never commit .env or private.key to version control). Use platform-specific secret management (Heroku Config Vars, AWS Secrets Manager, Docker secrets). Store the private key content in an environment variable rather than as a file for easier deployment.

Can I send international SMS with Vonage?

Yes, Vonage Messages API supports SMS to 200+ countries. Ensure your Vonage number supports the destination country, use proper E.164 format, and check for country-specific restrictions (some require pre-registration or have sender ID limitations). Verify pricing for each destination country.

What Node.js version does Vonage SDK require?

The @vonage/server-sdk v3.24.1 supports Node.js 14.x and higher. For production applications, use the current LTS version of Node.js for best performance and security. The SDK uses modern JavaScript features including Promises and async/await.