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:
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.
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.env
file 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.json
file to manage project details and dependencies.Install Dependencies: Install
express
for the web server,@vonage/server-sdk
to interact with the Vonage API, anddotenv
for managing environment variables.Project Structure: Create the following basic structure:
Configure
.gitignore
: Create a.gitignore
file 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
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
.Select Messages API for SMS:
SMS Settings
, ensureDefault SMS Setting
is 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-sdk
methods 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.key
file that downloads. Move this file into your project's root directory (where.gitignore
will prevent committing it).Messages
capability.YOUR_NGROK_URL/webhooks/inbound
(We'll getYOUR_NGROK_URL
later using ngrok).YOUR_NGROK_URL/webhooks/status
http://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 orManage
button next to the number.Forwarding
orApplication
section, 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
.env
file 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
Create the script (
send-sms.js
) to send an outbound SMS.Explanation:
require('dotenv').config();
: Loads variables from your.env
file intoprocess.env
.@vonage/server-sdk
: Imports the necessary Vonage SDK components. We useVonage
for the client andMessengerText
as a helper class for constructing the SMS payload correctly for the Messages API.new Vonage(...)
: Initializes the client. Crucially, for the Messages API, we provideapplicationId
andprivateKey
. The SDK handles JWT generation for authentication behind the scenes.sendSms
function:to
) and message content (text
) as arguments.from
number (your Vonage number) from environment variables.vonage.messages.send()
which is the method for the Messages API.new MessengerText({...})
, specifyingchannel: 'sms'
.async/await
for cleaner asynchronous code.messageUuid
on success, which is useful for tracking.try...catch
for robust error handling, logging the detailed error response from Vonage if available.sendSms
.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.
ngrok
creates 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.
ngrok
will display output similar to this:Copy the
https://<RANDOM_SUBDOMAIN>.ngrok-free.app
URL. This isYOUR_NGROK_URL
.2. Update Vonage Application Webhooks:
Applications
-> Your Application Name).ngrok
Forwarding URL into the webhook fields:https://<RANDOM_SUBDOMAIN>.ngrok-free.app/webhooks/inbound
https://<RANDOM_SUBDOMAIN>.ngrok-free.app/webhooks/status
Save changes
.3. Create the Express Server (
server.js
):Explanation:
express.json()
,express.urlencoded()
): Essential for parsing the incoming JSON and form-encoded data that Vonage sends in webhook requests./webhooks/inbound
Endpoint (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/status
Endpoint (POST):message_uuid
,status
("delivered", "failed", etc.),timestamp
, andto
(recipient). Includes basic validation.200 OK
response.app.listen
): Starts the Express server on the specifiedPORT
(defaulting to 3000 to matchngrok
).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.js
is running. You should see the "--- Inbound SMS Received ---" log, followed by the JSON payload of the incoming message.ngrok
is running. You should seePOST /webhooks/inbound 200 OK
indicating the request was received and forwarded.ngrok
web interface (usuallyhttp://127.0.0.1:4040
).6. Test Status Updates:
node send-sms.js
.node server.js
terminal. After a short delay, you should see the "--- Message Status Update Received ---" log with the delivery status (likely "delivered" or "accepted" initially).ngrok
logs/interface forPOST /webhooks/status 200 OK
.6. Putting it together: Basic two-way messaging (Auto-Reply)
Let's modify the
server.js
to automatically reply to incoming messages.Refactor Sending Logic: Move the
sendSms
function fromsend-sms.js
intoserver.js
(or a separate utility file) so the webhook handler can call it. Ensure@vonage/server-sdk
,dotenv
, and theVonage
client initialization are also present inserver.js
.Modify
/webhooks/inbound
Handler:Explanation of Changes:
sendSms
function and Vonage client initialization are now part ofserver.js
./webhooks/inbound
handler 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.Retest:
Ctrl+C
thennode server.js
.7. Error handling and logging
Our current setup uses basic
console.log
andconsole.error
. For production:message_uuid
s if necessary, especially if writing to a database. Always respond200 OK
quickly.try...catch
block aroundvonage.messages.send
is crucial. Parse theerr.response.data
for 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.8. Security considerations
Webhook Security: Our current setup relies on the obscurity of the
ngrok
URL. For production:Authorization
header that you should verify to ensure requests genuinely originate from Vonage. The@vonage/server-sdk
provides methods for JWT verification.Source: Vonage webhook security documentation (developer.vonage.com/en/messages/concepts/signed-webhooks, verified October 2025)
Credential Management: Never commit
.env
orprivate.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
text
from 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
.Source: OWASP security guidelines, Vonage security best practices
9. Database schema and data layer (Conceptual)
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 newmessages
record with direction "inbound".sendSms
: Create a newmessages
record 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.10. Testing and verification
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.ngrok
Inspector: Usehttp://127.0.0.1:4040
during 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
).Source: Testing best practices, Jest documentation, Vonage developer resources
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-sdk
andexpress
via 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/inbound
and/webhooks/status
to 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.key
file securely, add it to.gitignore
to 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 theAuthorization
header 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/inbound
that: (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 handlerasync
if performing asynchronous operations like database writes or sending replies. Verify webhook authenticity in production by validating the JWT signature in theAuthorization
header usingvonage.credentials.verifySignature()
. Use ngrok to expose your local development server for testing:ngrok http 3000
provides 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.js
with 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/inbound
handler, respond200 OK
immediately 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
Authorization
header 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 Unauthorized
if 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-sdk
officially 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 --version
and 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:4040
to 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).