Frequently Asked Questions
Use the Vonage Messages API with the Node.js SDK. Initialize the Vonage client with your API credentials, then use the vonage.messages.send() method with a MessengerText object specifying the recipient, sender, and message content. Ensure your Vonage number is linked to a Vonage Application.
It's a unified API from Vonage that allows you to send and receive messages across multiple channels, including SMS, MMS, and WhatsApp. This guide focuses on using it for two-way SMS communication.
Webhooks are essential for receiving incoming SMS messages and delivery status updates asynchronously. Vonage uses them to push real-time notifications to your application when a user replies or a message's status changes.
ngrok is useful during local development to create a temporary public URL that Vonage can send webhooks to. It is not recommended for production, which requires a proper deployment on a publicly accessible server.
No, Vonage has two separate APIs for SMS: the legacy SMS API and the newer Messages API. This guide uses the Messages API which has a different format for webhooks and generally offers more capabilities. Check your API settings on the Vonage dashboard if unsure.
Set up a POST route (e.g., /webhooks/inbound) in your Express app. Configure this URL as the 'Inbound URL' in your Vonage Application settings. Vonage will send a POST request to this endpoint whenever an SMS is sent to your Vonage number.
The private.key file, along with your application ID, is used to authenticate requests to the Vonage Messages API securely when you send messages. It's downloaded when you create a Vonage Application and should never be committed to version control.
A 200 OK response from your webhook endpoint acknowledges to Vonage that you've received the webhook. If your server doesn't send a 200 OK, or takes too long, Vonage might retry the webhook multiple times.
Create a POST route (e.g., /webhooks/status) and set it as the 'Status URL' in your Vonage Application. Vonage will send POST requests to this endpoint with updates on the delivery status of your outbound SMS messages.
The webhook request body includes the sender's number (from.number), message text (text), timestamp (timestamp), message UUID (message_uuid), and potentially other metadata. See the guide's code examples for handling the inbound SMS webhook for JSON examples.
Combine the outbound SMS sending logic with inbound webhook handling. When you receive an inbound message, extract the sender's number and use sendSms() to reply to them. See section 6 of the guide for a clear example of implementing auto-replies within server.js.
The dotenv package helps manage environment variables. It loads variables from a .env file into process.env, allowing you to store sensitive information like API keys and secrets separately from your code.
Test manually by sending SMS to your Vonage number and checking logs. Use ngrok's web interface to inspect webhooks. For automated tests, use Jest or Mocha to unit test functions and create mock webhooks to test server logic. For production readiness, implement E2E testing, verifying the complete messaging flow.
Validating webhooks ensures that the requests genuinely originate from Vonage, protecting your application from malicious actors or erroneous requests. Consider signature validation, IP whitelisting, or basic authentication where possible for production setups.
Last Updated: October 5, 2025
Developer guide: Implementing two-way SMS messaging with Node.js, Express, and Vonage
This guide provides a complete walkthrough for building a production-ready Node.js application using the Express framework to handle two-way SMS messaging via the Vonage Messages API. You'll learn how to send outbound SMS messages and set up webhooks to receive inbound messages and delivery statuses, enabling interactive communication flows.
Note: Vonage (formerly Nexmo) provides the Messages API, which supersedes the legacy SMS API with enhanced features and unified multi-channel support.
Source: Vonage Developer Documentation (developer.vonage.com, verified October 2025)
By the end of this guide, you will have a functional Node.js application capable of:
<!-- DEPTH: Prerequisites section lacks specifics about required skill level and estimated time commitment (Priority: Medium) --> We assume you have a basic understanding of Node.js, asynchronous programming (Promises, async/await), and REST APIs.
Project overview and goals
Goal: To create a robust Node.js service that can both send and receive SMS messages using Vonage's communication capabilities.
Problem Solved: This implementation enables applications to engage users via SMS for notifications, alerts, two-factor authentication (2FA), customer support, marketing campaigns, or any scenario requiring direct mobile communication. It provides the foundation for building interactive SMS-based services.
<!-- EXPAND: Could benefit from real-world use case examples with cost estimates (Type: Enhancement) -->
Technologies Used:
@vonage/server-sdk: The official Vonage Node.js SDK simplifies interaction with Vonage APIs.ngrok: A tool to expose local development servers to the internet, essential for testing Vonage webhooks without deploying. Note: Free tier has session time limits (typically 2 hours) and URLs change on restart.dotenv: A module to load environment variables from a.envfile intoprocess.env, keeping sensitive credentials out of source code.Source: npm package registry (@vonage/server-sdk, verified October 2025), ngrok documentation
System Architecture:
Expected Outcome: A running Node.js application with two main parts:
send-sms.js) to send an outbound SMS on demand.server.js) listening for incoming HTTP POST requests from Vonage webhooks (inbound messages and status updates).Prerequisites:
ngrok: Installed and authenticated (a free account is sufficient). Download from ngrok.com. Development only – not for production use.npm install -g @vonage/cli. Useful for managing applications and numbers.Source: ITU-T Recommendation E.164 (phone number format standard)
1. Setting up the project
Let's initialize our Node.js project and install the necessary dependencies.
Create Project Directory: Open your terminal and create a new directory for the project, then navigate into it.
Initialize Node.js Project: This creates a
package.jsonfile to manage project details and dependencies.Install Dependencies: Install
expressfor the web server,@vonage/server-sdkto interact with the Vonage API, anddotenvfor managing environment variables.Project Structure: Create the following basic structure:
Configure
.gitignore: Create a.gitignorefile in the root directory to prevent committing sensitive information and unnecessary files.Why
.gitignore? This ensures your API secrets, private keys, and local environment configurations are not accidentally pushed to version control systems like Git, which is crucial for security.2. Vonage configuration
<!-- DEPTH: Configuration section needs screenshots or visual aids for dashboard navigation (Priority: High) --> Before writing code, configure your Vonage account and application settings. Note: Specific UI labels and navigation paths within the Vonage Dashboard may change over time. These instructions were verified October 2025.
API Key and Secret: Navigate to your Vonage API Dashboard. Your API Key and API Secret are displayed prominently at the top. You'll need these shortly.
Purchase a Vonage Number:
Numbers->Buy Numbers(or equivalent section) in the dashboard.VONAGE_NUMBER.<!-- GAP: Missing pricing information for phone numbers and SMS costs per country (Type: Critical) -->
Select Messages API for SMS:
SMS Settings, ensureDefault SMS Settingis set toUse the Messages API.Save changes.Why this setting? It ensures that webhooks related to your Vonage number use the Messages API format and the
@vonage/server-sdkmethods we'll use. The legacy SMS API uses different webhook schemas.Source: Vonage API Settings documentation (developer.vonage.com, verified October 2025)
Create a Vonage Application: The Messages API uses Applications for authentication via JWT (JSON Web Token) with public/private key pairs, which is more secure than using only API Key/Secret. The Application also defines webhook endpoints.
Applications->Create a new application(or equivalent).Nodejs SMS App).Generate public and private key. Immediately save theprivate.keyfile that downloads. Move this file into your project's root directory (where.gitignorewill prevent committing it).Messagescapability.YOUR_NGROK_URL/webhooks/inbound(We'll getYOUR_NGROK_URLlater using ngrok).YOUR_NGROK_URL/webhooks/statushttp://example.com/inbound. We will update them after startingngrok.Generate new application.Source: Vonage Applications API documentation (developer.vonage.com/en/application/overview)
Link Your Number to the Application:
Numbers->Your numbers.Edit(pencil) icon orManagebutton next to the number.ForwardingorApplicationsection, select the Vonage Application you just created (Nodejs SMS App) from the dropdown underMessages.Why link the number? This tells Vonage to route incoming messages and status updates for this specific number to the webhooks defined in the linked Application.
3. Environment setup (
.env)Create the
.envfile in your project root and add your credentials. Replace the placeholder values with your actual details.VONAGE_API_KEY,VONAGE_API_SECRET: Used by the SDK for certain operations (e.g., some account management functions) or potentially as fallback authentication if Application ID/Key isn't configured for a specific client.VONAGE_APPLICATION_ID,VONAGE_PRIVATE_KEY_PATH: The primary credentials used by the Messages API client in this guide for sending messages via JWT generation using your private key.VONAGE_NUMBER: The Vonage virtual number you purchased and linked to the application. This will be the sender (from) number. Use E.164 format (+country_code + number, no spaces/symbols, e.g., +14155552671).MY_TEST_NUMBER: The destination number (to) for sending test messages. Use your own mobile number in E.164 format (country code + number, no spaces or symbols, e.g.,+14155552671).Source: ITU-T Recommendation E.164 standard for international phone numbering
4. Sending SMS messages
<!-- DEPTH: Code example lacks inline comments explaining SMS delivery flow and timing expectations (Priority: Medium) --> Create the script (
send-sms.js) to send an outbound SMS.Explanation:
require('dotenv').config();: Loads variables from your.envfile intoprocess.env.@vonage/server-sdk: Imports the necessary Vonage SDK components. We useVonagefor the client andMessengerTextas a helper class for constructing the SMS payload correctly for the Messages API.new Vonage(...): Initializes the client. Crucially, for the Messages API, we provideapplicationIdandprivateKey. The SDK handles JWT generation for authentication behind the scenes.sendSmsfunction:to) and message content (text) as arguments.fromnumber (your Vonage number) from environment variables.vonage.messages.send()which is the method for the Messages API.new MessengerText({...}), specifyingchannel: 'sms'.async/awaitfor cleaner asynchronous code.messageUuidon success, which is useful for tracking.try...catchfor robust error handling, logging the detailed error response from Vonage if available.sendSms.<!-- GAP: Missing explanation of common error codes and their resolutions (Type: Substantive) -->
Run the Sending Script:
Execute the script from your terminal:
You should see output indicating the attempt and success (with a message UUID) or failure (with error details). Check your mobile phone for the incoming SMS!
5. Receiving SMS messages (Webhook Server)
Now, build the Express server (
server.js) to handle incoming webhooks from Vonage.1. Start
ngrok:Vonage needs a publicly accessible URL to send webhooks to.
ngrokcreates a secure tunnel from the internet to your local machine.Important limitations:
Source: ngrok documentation (ngrok.com/docs)
Open a new terminal window (keep the first one for running the Node server later) and run:
Why port 3000? This matches the port our Express server will listen on.
ngrokwill display output similar to this:Copy the
https://<RANDOM_SUBDOMAIN>.ngrok-free.appURL. This isYOUR_NGROK_URL.2. Update Vonage Application Webhooks:
Applications-> Your Application Name).ngrokForwarding URL into the webhook fields:https://<RANDOM_SUBDOMAIN>.ngrok-free.app/webhooks/inboundhttps://<RANDOM_SUBDOMAIN>.ngrok-free.app/webhooks/statusSave changes.3. Create the Express Server (
server.js):<!-- DEPTH: Webhook endpoint code needs explanation of HTTP status codes and retry behavior (Priority: High) -->
Explanation:
express.json(),express.urlencoded()): Essential for parsing the incoming JSON and form-encoded data that Vonage sends in webhook requests./webhooks/inboundEndpoint (POST):req.body) received from Vonage. This is crucial for debugging and understanding the payload structure.from(an object containing the sender'snumber),text(the message content),timestamp, andmessage_uuid. Includes basic validation.res.status(200).send(...). This acknowledges receipt to Vonage. Without this, Vonage will assume failure and retry sending the webhook, leading to duplicate processing./webhooks/statusEndpoint (POST):message_uuid,status("delivered", "failed", etc.),timestamp, andto(recipient). Includes basic validation.200 OKresponse.app.listen): Starts the Express server on the specifiedPORT(defaulting to 3000 to matchngrok).<!-- GAP: Missing explanation of webhook payload structure and all available fields (Type: Substantive) -->
4. Run the Webhook Server:
Go back to your first terminal window (where you ran
npm install) and start the server:You should see the "Server listening…" message.
5. Test Receiving:
node server.jsis running. You should see the "--- Inbound SMS Received ---" log, followed by the JSON payload of the incoming message.ngrokis running. You should seePOST /webhooks/inbound 200 OKindicating the request was received and forwarded.ngrokweb interface (usuallyhttp://127.0.0.1:4040).6. Test Status Updates:
node send-sms.js.node server.jsterminal. After a short delay, you should see the "--- Message Status Update Received ---" log with the delivery status (likely "delivered" or "accepted" initially).ngroklogs/interface forPOST /webhooks/status 200 OK.6. Putting it together: Basic two-way messaging (Auto-Reply)
Let's modify the
server.jsto automatically reply to incoming messages.Refactor Sending Logic: Move the
sendSmsfunction fromsend-sms.jsintoserver.js(or a separate utility file) so the webhook handler can call it. Ensure@vonage/server-sdk,dotenv, and theVonageclient initialization are also present inserver.js.Modify
/webhooks/inboundHandler:Explanation of Changes:
sendSmsfunction and Vonage client initialization are now part ofserver.js./webhooks/inboundhandler is markedasync.res.status(200).send(...)before processing the message and sending the reply. This prevents Vonage webhook timeouts if the reply takes time.req.body.from.number.replyText.await sendSms(from.number, replyText)to send the reply back to the original sender.<!-- EXPAND: Could benefit from decision tree for different auto-reply scenarios (Type: Enhancement) -->
Retest:
Ctrl+Cthennode server.js.7. Error handling and logging
<!-- DEPTH: Error handling section lacks specific error code examples and retry strategies (Priority: High) --> Our current setup uses basic
console.logandconsole.error. For production:message_uuids if necessary, especially if writing to a database. Always respond200 OKquickly.try...catchblock aroundvonage.messages.sendis crucial. Parse theerr.response.datafor specific Vonage error codes and messages to understand failures (e.g., invalid number format, insufficient funds, carrier restrictions). Implement retry logic with exponential backoff for transient network errors when sending, if needed.<!-- GAP: Missing comprehensive list of Vonage API error codes and handling strategies (Type: Critical) -->
8. Security considerations
Webhook Security: Our current setup relies on the obscurity of the
ngrokURL. For production:Authorizationheader that you should verify to ensure requests genuinely originate from Vonage. The@vonage/server-sdkprovides methods for JWT verification.Source: Vonage webhook security documentation (developer.vonage.com/en/messages/concepts/signed-webhooks, verified October 2025)
Credential Management: Never commit
.envorprivate.key. Use environment variables provided by your hosting platform or secrets management tools (like HashiCorp Vault, AWS Secrets Manager, Azure Key Vault) in production.Input Validation/Sanitization: If you process the
textfrom incoming SMS messages (e.g., storing in DB, displaying in UI), sanitize it thoroughly to prevent Cross-Site Scripting (XSS), SQL injection, or other injection attacks. Libraries likeDOMPurify(for HTML contexts) or parameterized queries (for SQL) are essential.Rate Limiting: Protect your webhook endpoints from abuse or accidental loops by implementing rate limiting using Express middleware like
express-rate-limit.<!-- GAP: Missing production deployment checklist and environment-specific configurations (Type: Substantive) -->
Source: OWASP security guidelines, Vonage security best practices
9. Database schema and data layer (Conceptual)
<!-- DEPTH: Database section is too conceptual - needs practical ORM setup examples (Priority: Medium) --> This guide doesn't implement a database, but this section provides a conceptual overview. The schema and code snippets below are illustrative examples, not a complete, production-ready implementation.
messages: To store details of both inbound and outbound messages (UUID, direction, sender, recipient, text, timestamp, status, related message UUID for replies).conversations(Optional): To group messages belonging to the same interaction thread./webhooks/inbound: Create a newmessagesrecord with direction "inbound".sendSms: Create a newmessagesrecord with direction "outbound" and status "submitted"./webhooks/status: Find the message byvonage_uuid(message_uuid from webhook) and update itsstatus.prisma migrate dev) to manage schema changes.<!-- EXPAND: Could benefit from complete Prisma schema example with relationships (Type: Enhancement) -->
10. Testing and verification
<!-- DEPTH: Testing section needs concrete test file examples with Jest setup (Priority: High) --> Testing is crucial for a reliable messaging application. The examples here are conceptual starting points.
send-sms.js(or trigger sending via your app logic) -> Verify SMS received on mobile -> Verify server logs status webhook.ngrokInspector: Usehttp://127.0.0.1:4040during development to inspect exact headers and payloads of incoming webhooks, which is invaluable for debugging.vonage.messages.send) by mocking the Vonage API client.sendSms).<!-- GAP: Missing mock data examples for testing webhook handlers (Type: Substantive) -->
Source: Testing best practices, Jest documentation, Vonage developer resources
<!-- GAP: Missing troubleshooting section for common setup and runtime issues (Type: Critical) -->
Additional Resources
Official Documentation:
Frequently Asked Questions
How do I set up two-way SMS messaging with Vonage and Node.js?
Set up two-way SMS messaging by: (1) Create a Vonage account and purchase an SMS-capable number, (2) Create a Vonage Application with Messages API capability and download the private key, (3) Install
@vonage/server-sdkandexpressvia npm, (4) Initialize the Vonage client with your Application ID and private key for JWT authentication, (5) Configure webhook endpoints for inbound messages and status updates using ngrok for local testing, (6) Implement Express POST routes at/webhooks/inboundand/webhooks/statusto receive Vonage webhooks, (7) Link your Vonage number to the Application in the dashboard. The complete setup takes approximately 15–30 minutes and enables both sending outbound SMS viavonage.messages.send()and receiving inbound messages through webhook handlers.What is the difference between Vonage Messages API and SMS API?
Vonage offers two APIs for SMS: the legacy SMS API and the newer Messages API. The Messages API (recommended for new projects) provides unified multi-channel support (SMS, MMS, WhatsApp, Viber, Facebook Messenger), uses JWT authentication with public/private key pairs for enhanced security, delivers richer webhook payloads with more detailed message metadata, and offers better delivery reporting. The SMS API (older) only supports SMS/MMS, uses basic API key/secret authentication, and has limited webhook functionality. Configure your Vonage account to use Messages API via API Settings -> Default SMS Setting -> Use the Messages API in the dashboard. This guide exclusively uses the Messages API for its superior security and features.
How do I authenticate Vonage Messages API requests with JWT?
Authenticate Messages API requests using JWT (JSON Web Token) by initializing the Vonage SDK with your Application ID and private key:
new Vonage({ applicationId: process.env.VONAGE_APPLICATION_ID, privateKey: './private.key' }). The SDK automatically generates short-lived JWT tokens signed with your private key for each API request. Store yourprivate.keyfile securely, add it to.gitignoreto prevent committing it to version control, and use environment variables to reference the key path. For webhook security, verify incoming webhook signatures by extracting the JWT from theAuthorizationheader and usingvonage.credentials.verifySignature(token)to ensure requests genuinely originate from Vonage. JWT authentication is more secure than API key/secret because tokens expire quickly and the private key never leaves your server.What phone number format does Vonage require for SMS?
Vonage requires E.164 international phone number format for all SMS operations. E.164 format consists of:
+prefix, country code (1–3 digits), and subscriber number (up to 15 total digits, no spaces or special characters). Examples:+14155552671(US),+442071838750(UK),+61404123456(Australia). Always include the country code even for domestic messages. Invalid formats (missing+, spaces, hyphens, parentheses) cause message delivery failures with error codes like "Invalid number format." Use validation regex/^\+[1-9]\d{1,14}$/to verify E.164 compliance before sending. Store all phone numbers in E.164 format in your database to ensure consistency. The ITU-T E.164 standard defines this format for international telecommunication numbering.How do I handle inbound SMS webhooks from Vonage?
Handle inbound SMS webhooks by creating an Express POST endpoint at
/webhooks/inboundthat: (1) Parses the JSON request body usingexpress.json()middleware, (2) Extracts key fields (from.number,text,timestamp,message_uuid) fromreq.body, (3) Responds immediately withres.status(200).send()to acknowledge receipt (Vonage retries on non-200 responses or timeouts), (4) Processes the message asynchronously after acknowledging (log to database, trigger business logic, send auto-reply). Make the route handlerasyncif performing asynchronous operations like database writes or sending replies. Verify webhook authenticity in production by validating the JWT signature in theAuthorizationheader usingvonage.credentials.verifySignature(). Use ngrok to expose your local development server for testing:ngrok http 3000provides a public HTTPS URL to configure in your Vonage Application webhook settings.What are the limitations of using ngrok for Vonage webhooks?
ngrok limitations for Vonage webhook development include: (1) Development only – ngrok free tier is unsuitable for production due to reliability and security concerns, (2) Session timeouts – free tier sessions expire after approximately 2 hours requiring restart and webhook URL reconfiguration, (3) Changing URLs – each ngrok restart generates a new random subdomain requiring Vonage Application webhook updates (paid plans offer static URLs), (4) No guaranteed uptime – ngrok sessions can disconnect unexpectedly, (5) Security concerns – free URLs are publicly accessible without authentication (use ngrok's auth features or IP restrictions for added security). For production, deploy your webhook server to a cloud platform (AWS, Google Cloud, Azure, Heroku, Vercel) with a static HTTPS URL and configure that permanent URL in your Vonage Application settings.
How do I send an auto-reply to inbound SMS messages?
Send auto-replies by: (1) Initialize the Vonage client in
server.jswith Application ID and private key, (2) Create a reusablesendSms(to, text)async function usingvonage.messages.send(new MessengerText({ text, to, from: process.env.VONAGE_NUMBER, channel: 'sms' })), (3) In the/webhooks/inboundhandler, respond200 OKimmediately to acknowledge the webhook, (4) Extract the sender's number fromreq.body.from.number, (5) Callawait sendSms(from.number, replyText)to send the reply back to the original sender. Ensure the reply is sent after acknowledging the webhook to prevent Vonage timeouts. Test by sending an SMS to your Vonage number – you should receive the auto-reply within seconds and see both inbound and outbound status webhooks in your server logs.How do I verify webhook signatures from Vonage?
Verify webhook signatures by implementing JWT validation: (1) Extract the JWT token from the
Authorizationheader in webhook requests:const token = req.headers['authorization'], (2) Use the Vonage SDK verification method:vonage.credentials.verifySignature(token)wrapped in a try-catch block, (3) Respond401 Unauthorizedif verification fails:return res.status(401).send('Unauthorized'), (4) Process the webhook only after successful verification. Initialize the Vonage client with both API key/secret and Application ID/private key for signature verification:new Vonage({ apiKey, apiSecret, applicationId, privateKey }). Webhook signature validation prevents unauthorized requests from malicious actors impersonating Vonage, protects against replay attacks, and ensures webhook data integrity. Always implement signature verification in production environments before processing sensitive webhook data.What Node.js version should I use for Vonage Messages API?
Use Node.js v18+ LTS (Long-Term Support) for Vonage Messages API projects as of October 2025. Node.js v18 provides stable async/await support, enhanced performance, modern JavaScript features (ES modules, top-level await), and security updates essential for production messaging applications. The
@vonage/server-sdkofficially supports Node.js v18+ and leverages its features for improved reliability. Avoid older versions (v14, v16) as they approach or have reached end-of-life with no security patches. Check your Node.js version withnode --versionand upgrade via nodejs.org or version managers likenvm(Node Version Manager). Use the same Node.js version in development and production to prevent compatibility issues.How do I test Vonage two-way SMS without deploying to production?
Test Vonage two-way SMS locally using: (1) ngrok – expose your local Express server with
ngrok http 3000, copy the HTTPS URL, and configure it in your Vonage Application webhook settings, (2) Manual testing – send SMS from your mobile phone to your Vonage number and verify inbound webhook logs, run your sending script and verify delivery on your phone, (3) ngrok Inspector – accesshttp://127.0.0.1:4040to inspect webhook payloads, headers, and JWT signatures in real-time, (4) Console logging – add comprehensiveconsole.log()statements in webhook handlers to trace request flow, (5) Unit tests – use Jest or Mocha to test individual functions with mocked Vonage SDK calls, (6) Integration tests – send mock HTTP POST requests to webhook endpoints with valid JWT signatures and assert responses. Test edge cases including long messages (160+ characters), Unicode characters (emojis, non-Latin scripts), invalid E.164 formats, and webhook retry scenarios (non-200 responses).