Frequently Asked Questions
Use the `@vonage/server-sdk` and the `messages.send()` method with your API credentials, application ID, private key, Vonage virtual number, and the recipient's number. The sender's number must be your Vonage virtual number linked to the application. This method handles the API call and provides a message UUID for tracking.
The Vonage Messages API is a unified API for sending and receiving messages across multiple channels like SMS, MMS, and WhatsApp. This tutorial focuses on using it for two-way SMS communication, enabling features such as notifications, customer support, and reminders.
Vonage uses webhooks to deliver incoming SMS messages to your application in real-time. When a message is sent to your Vonage virtual number, Vonage sends an HTTP POST request to your specified webhook URL containing the message details. Your server needs to listen on this URL.
The Vonage Application ID and private key are essential for authenticating with the Messages API, specifically when sending SMS messages or configuring webhooks. They ensure secure communication between your app and the Vonage platform.
Yes, by using ngrok, you can create a secure tunnel to your local development server, providing a public URL that Vonage can use to deliver webhooks. Make sure to update the inbound webhook URL in your Vonage application settings.
Create a POST route (e.g., '/webhooks/inbound') in your Express app. Vonage will send JSON data to this endpoint. Your code should parse this JSON, process the message details, and immediately respond with a 200 OK status. Any time-consuming operations should occur *after* acknowledging the webhook.
The `.env` file stores environment variables, such as API keys, secrets, and configuration settings, keeping them separate from your code. The `dotenv` package loads these variables into `process.env`.
Vonage retries webhooks if it doesn't receive a quick 200 OK. Implement idempotent logic or use the `messageId` to track processed messages, preventing duplicate actions if the webhook is delivered multiple times. Store the `messageId` in your database.
Implement input validation and sanitization, verify webhook signatures, and use rate limiting to protect against abuse. Ensure HTTPS is used for webhooks, and never expose your API secrets or private keys in version control.
In the Vonage dashboard, navigate to your application settings. Under 'Link virtual numbers', find your purchased number and click 'Link'. This directs incoming messages to the application's webhooks.
The Vonage Messages API sends a JSON payload containing the sender's number (`msisdn`), recipient number (`to`), message content (`text`), message ID (`messageId`), timestamp (`message-timestamp`), and other metadata. Ensure your 'Default SMS Setting' is set to Messages API in the Vonage dashboard.
Check your ngrok tunnel is active, verify the webhook URL in the Vonage dashboard matches your server route, confirm the HTTP method is POST, and ensure your server responds with 200 OK quickly. Check for firewall issues and correct credentials in your `.env` file.
This guide provides a comprehensive walkthrough for building a production-ready Node.js application using the Express framework to handle both sending and receiving SMS messages via the Vonage Messages API. We'll cover everything from project setup and core messaging logic to essential production considerations like security, error handling, and deployment.
By the end of this tutorial, you will have a functional application capable of sending SMS messages programmatically and receiving incoming messages via a webhook, enabling two-way communication. This setup is foundational for building features like SMS notifications, customer support chat via SMS, appointment reminders, and more.
Project overview and goals
Goal: To create a Node.js application that can:
Problem Solved: This enables applications to interact with users via the ubiquitous SMS channel, providing a direct and widely accessible communication method without requiring users to install a separate app.
Technologies Used:
@vonage/server-sdk
: The official Vonage Node.js SDK for interacting with the Vonage APIs.dotenv
: A module to load environment variables from a.env
file intoprocess.env
.System Architecture:
The diagram shows a User's Phone communicating bi-directionally with the Vonage Platform via SMS. The Vonage Platform sends incoming messages to Your Node.js App via a Receive SMS Webhook (HTTP POST). Your Node.js App sends outgoing messages via a Send SMS API Call to the Vonage Platform.
Expected Outcome: A running Node.js application that can be triggered to send an SMS and logs incoming SMS messages received on a configured Vonage number.
Prerequisites:
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.
2. Initialize Node.js Project: Initialize the project using npm. This creates a
package.json
file.The
-y
flag accepts the default settings.3. Install Dependencies: We need Express for the web server_ the Vonage SDK_ and
dotenv
for managing environment variables.4. Install Development Dependencies (Optional but Recommended):
nodemon
automatically restarts the server upon file changes_ speeding up development.5. Configure
package.json
Scripts: Add scripts to yourpackage.json
for easily starting the server:6. Create Project Structure: Organize your code for better maintainability.
Create the
src
directory:7. Create
.gitignore
: It's crucial to prevent committing sensitive information and unnecessary files. Create a.gitignore
file in the project root:8. Set Up Environment Variables (
.env
): Create a.env
file in the project root. We'll populate this with credentials obtained from Vonage in the next steps.Using
.env
keeps sensitive credentials out of your source code, making it more secure and easier to manage different configurations for development, staging, and production. Thedotenv
package loads these variables intoprocess.env
when the application starts.2. Integrating with Vonage (Third-Party Service)
Before writing code, we need to configure our Vonage account and obtain the necessary credentials.
1. Obtain API Key and Secret:
.env
file forVONAGE_API_KEY
andVONAGE_API_SECRET
.2. Create a Vonage Application:
Vonage Applications act as containers for your communication configurations (like webhook URLs) and use a private/public key pair for authenticating certain API calls (like sending messages via the Messages API).
private.key
file will be downloaded immediately. Save this file in the root of your project directory. Ensure the path inVONAGE_APPLICATION_PRIVATE_KEY_PATH
in your.env
file matches (./private.key
).http://example.com/webhooks/inbound
andhttp://example.com/webhooks/status
. We will update these..env
file forVONAGE_APPLICATION_ID
.3. Link Your Vonage Number:
14155550100
) and paste it into your.env
file forVONAGE_NUMBER
.4. Set Default SMS API:
Vonage has two APIs for SMS: the older SMS API and the newer Messages API. Their webhook formats differ. Since we're using the Messages API SDK (
@vonage/server-sdk
) for sending, we need to ensure our account is configured to send inbound webhooks using the Messages API format for consistency.5. Fill Remaining
.env
Variables:PORT
to3000
(or another port if you prefer).TO_NUMBER
to your personal mobile phone number (including country code, e.g.,12015550101
) for testing sending messages.Your
.env
file should now be populated with your specific credentials.3. Implementing core functionality: Sending SMS
Let's create the script to send an outbound SMS message using the Vonage Messages API.
File:
src/send-sms.js
Explanation:
require('dotenv').config();
: Loads the variables from your.env
file intoprocess.env
.Vonage
client instance. Crucially, for the Messages API, authentication relies on theapplicationId
andprivateKey
. WhileapiKey
andapiSecret
aren't strictly required for this specific send call, including them is good practice as other SDK features might use them.vonage.messages.send()
: This is the core function from the SDK for sending messages via the Messages API.new SMS(...)
: We create an SMS message object specifying the text content, recipient (to
), and sender (from
). The sender must be your Vonage virtual number linked to the Application ID.async/await
andtry...catch
: We use modern JavaScript asynchronous patterns to handle the API call and implement basic error handling. A successful response includes amessageUuid
, which is useful for tracking.catch
block logs errors. In a production system, you'd integrate this with a proper logging framework (like Winston or Pino) and potentially an error tracking service (like Sentry).To Test Sending: Run the script from your terminal:
You should see output indicating success and receive an SMS on the phone number specified in
TO_NUMBER
. Check your terminal for any error messages if it fails.4. Implementing core functionality: Receiving SMS
Now, let's set up the Express server to listen for incoming SMS messages delivered by Vonage webhooks.
File:
src/server.js
Explanation:
express.json
,express.urlencoded
): Essential for parsing the incoming request body from Vonage, which is typically JSON./webhooks/inbound
Endpoint (POST): This is the route Vonage will call when an SMS arrives at your linked number.msisdn
for sender,to
for recipient,text
,messageId
,message-timestamp
). We use destructuring with renaming ('message-timestamp': timestamp
) to assign the timestamp value to a variable namedtimestamp
. Adjust these fields if using the older SMS API format.res.status(200).end()
: Vonage requires a quick200 OK
response to know the webhook was received successfully. Send this response before doing any lengthy processing (like database writes or external API calls) to avoid timeouts and retries from Vonage./webhooks/status
Endpoint (POST): Optional but recommended. Vonage sends delivery receipts (DLRs) and other status updates about outbound messages to this URL if configured./health
Endpoint (GET): A simple endpoint often used by monitoring systems to check if the application is running.app.listen
: Starts the Express server on the configured port.5. Exposing Local Server with ngrok
Vonage needs a publicly accessible URL to send webhooks. During development,
ngrok
creates a secure tunnel to your local machine.1. Start Your Local Server: If it's not already running, start your Express server:
Using
nodemon
automatically restarts on changes. You should see ""Server listening on port 3000..."".2. Start ngrok: Open another terminal window/tab (leave the server running) and run:
Replace
3000
if you used a different port.3. Get ngrok URL: ngrok will display output similar to this:
https://<random-string>.ngrok.io
URL. This is your public URL. Note: The free ngrok plan provides a temporary URL that changes each time you restart ngrok. Paid plans offer stable URLs.http://127.0.0.1:4040
) is a useful dashboard to inspect webhook requests ngrok receives.4. Update Vonage Application Webhooks:
https://<random-string>.ngrok.io
URL and append/webhooks/inbound
. Example:https://a1b2c3d4e5.ngrok.io/webhooks/inbound
https://<random-string>.ngrok.io
URL and append/webhooks/status
. Example:https://a1b2c3d4e5.ngrok.io/webhooks/status
POST
.6. Verification and Testing
Now, let's test the complete two-way flow.
1. Ensure Services are Running:
npm run dev
) is running in one terminal.ngrok http 3000
is running in another terminal.2. Test Inbound SMS:
VONAGE_NUMBER
).http://127.0.0.1:4040
) to inspect the raw HTTP request received from Vonage.3. Test Outbound SMS (Again):
TO_NUMBER
phone./webhooks/status
endpoint.7. Security Features
While this guide covers basics, production applications require robust security.
express-validator
orjoi
to validate the structure and content ofreq.body
before processing. Sanitize any data before storing it in a database or displaying it to prevent XSS attacks.express-rate-limit
can restrict the number of requests from a single IP address or based on other criteria..env
files or private keys to version control (.gitignore
helps). Use secure methods provided by your deployment platform (e.g., environment variable settings in Heroku, AWS Secrets Manager, etc.) to manage secrets in production.8. Error Handling, Logging, and Retries
Production systems need better error handling and logging than simple
console.log
.winston
orpino
for structured logging (e.g., JSON format). This makes logs easier to parse, filter, and analyze in log aggregation tools (like Datadog, Splunk, ELK stack). Include request IDs to trace requests across services.200 OK
quickly. Your application logic must be idempotent (safe to run multiple times with the same input) or have mechanisms to detect and handle duplicate webhook deliveries (e.g., by checking themessageId
against recently processed messages in your database).sendSms
) to fail. Implement a retry strategy with exponential backoff for transient network errors when sending messages. Libraries likeaxios-retry
(if using axios directly) or custom logic can handle this.9. Database Schema and Data Layer (Conceptual)
For any real application, you'll want to store message history.
Conceptual Schema (
messages
table):id
(Primary Key, e.g., UUID or Auto-incrementing Integer)vonage_message_uuid
(VARCHAR, Unique) - Store themessageId
ormessageUuid
from Vonage. Crucial for tracking and preventing duplicates.direction
(ENUM('inbound', 'outbound')) - Track message flow.sender
(VARCHAR) - Phone number of the sender.recipient
(VARCHAR) - Phone number of the recipient.body
(TEXT) - The message content.status
(VARCHAR, e.g., 'submitted', 'delivered', 'failed', 'received') - Track message status (updated via status webhook for outbound).vonage_timestamp
(TIMESTAMP WITH TIME ZONE) - Timestamp from Vonage webhook (message-timestamp
).created_at
(TIMESTAMP WITH TIME ZONE) - When the record was created in your system.updated_at
(TIMESTAMP WITH TIME ZONE) - When the record was last updated.Implementation:
vonage_message_uuid
).10. Deployment and CI/CD
Deploying your Node.js application involves several steps.
General Steps:
VONAGE_*
credentials,PORT
, and database connection strings to the production environment. Do not deploy your.env
file.npm install --production
on the server to install only necessary dependencies.pm2
to run your Node.js application (node src/server.js
).pm2
handles restarts, clustering (for multi-core utilization), and log management. Example:pm2 start src/server.js --name vonage-sms-app
.https://your-app-domain.com/webhooks/inbound
).CI/CD Pipeline:
eslint
,prettier
).jest
,mocha
).11. Troubleshooting and Caveats
/webhooks/inbound
,/webhooks/status
) and the method isPOST
.200 OK
Response: If your server doesn't respond quickly with200 OK
to webhooks, Vonage will retry, causing duplicate processing. Ensureres.status(200).end()
is called promptly.private.key
. Ensure the key file has the correct read permissions for your Node.js process.Messages API
vsSMS API
Mismatch: Ensure your Vonage account default setting matches the webhook format your code expects (we used Messages API). If you see unexpected webhook body formats, check this setting. The key timestamp field ismessage-timestamp
in the Messages API webhook.12. Complete Code Repository
A repository containing the complete code for this guide can be found here:
https://github.com/sent-dev/vonage-node-express-sms-guide
This guide provides a solid foundation for building two-way SMS interactions using Node.js, Express, and Vonage. Remember to layer in robust security, error handling, logging, and testing as you move towards a production deployment. Consult the official Vonage Messages API documentation for more advanced features and details.