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:
- Send outbound SMS messages using the Vonage Messages API.
- Receive inbound SMS messages sent to a Vonage virtual number via a webhook.
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:
- Node.js: A JavaScript runtime environment for building server-side applications.
- Express: A minimal and flexible Node.js web application framework used to create the webhook endpoint for receiving messages.
- Vonage Messages API: A unified API from Vonage for sending and receiving messages across various channels (SMS, MMS, WhatsApp, etc.). We'll focus on SMS.
@vonage/server-sdk
: The official Vonage Node.js SDK for interacting with the Vonage APIs.- ngrok: A tool to expose local development servers to the internet, necessary for Vonage webhooks to reach our local machine during development.
dotenv
: A module to load environment variables from a.env
file intoprocess.env
.
System Architecture:
+-------------+ SMS +----------------+ Receive SMS Webhook +-----------------+
| User's Phone| <-----------> | Vonage Platform| ----------------> | Your Node.js App|
| | ------------> | (Messages API) | <---------------- | (Express Server)|
+-------------+ +----------------+ (Send SMS API Call)+-----------------+
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:
- A Vonage API account. Sign up here if you don't have one.
- Node.js and npm (or yarn) installed. Download Node.js.
- A Vonage virtual phone number capable of sending/receiving SMS. You can purchase one from the Vonage dashboard.
- ngrok installed. Download ngrok. A free account is sufficient.
- Basic understanding of JavaScript_ Node.js_ and REST APIs.
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.
mkdir vonage-sms-app
cd vonage-sms-app
2. Initialize Node.js Project:
Initialize the project using npm. This creates a package.json
file.
npm init -y
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.
npm install express @vonage/server-sdk dotenv
4. Install Development Dependencies (Optional but Recommended):
nodemon
automatically restarts the server upon file changes_ speeding up development.
npm install --save-dev nodemon
5. Configure package.json
Scripts:
Add scripts to your package.json
for easily starting the server:
// package.json
{
// ... other properties
""scripts"": {
""start"": ""node src/server.js""_
""dev"": ""nodemon src/server.js""_
""send-sms"": ""node src/send-sms.js""
}_
// ... other properties
}
6. Create Project Structure: Organize your code for better maintainability.
vonage-sms-app/
├── node_modules/
├── src/
│ ├── server.js # Express server for receiving SMS
│ └── send-sms.js # Script for sending SMS
├── .env # Environment variables (API keys_ etc.) - DO NOT COMMIT
├── .gitignore # Files/folders to ignore in Git
├── private.key # Vonage application private key - DO NOT COMMIT
└── package.json
Create the src
directory:
mkdir src
7. Create .gitignore
:
It's crucial to prevent committing sensitive information and unnecessary files. Create a .gitignore
file in the project root:
# .gitignore
# Dependencies
node_modules
# Environment Variables
.env
*.env
# Vonage Private Key
private.key
*.key
# Log files
*.log
# OS generated files
.DS_Store
Thumbs.db
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.
# .env
# Vonage API Credentials (Obtained from Vonage Dashboard > API Settings)
VONAGE_API_KEY=YOUR_VONAGE_API_KEY
VONAGE_API_SECRET=YOUR_VONAGE_API_SECRET
# Vonage Application Credentials (Obtained after creating a Vonage Application)
VONAGE_APPLICATION_ID=YOUR_VONAGE_APPLICATION_ID
VONAGE_APPLICATION_PRIVATE_KEY_PATH=./private.key
# Vonage Number (Your purchased virtual number)
VONAGE_NUMBER=YOUR_VONAGE_VIRTUAL_NUMBER
# Server Port
PORT=3000
# Recipient Number (For testing sending SMS)
TO_NUMBER=RECIPIENT_PHONE_NUMBER_WITH_COUNTRY_CODE
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. The dotenv
package loads these variables into process.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:
- Log in to your Vonage API Dashboard.
- Your API Key and API Secret are displayed prominently on the main dashboard page (API settings).
- Copy these values and paste them into your
.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).
- Navigate to Applications > Create a new application in the dashboard.
- Give your application a name (e.g., ""Nodejs SMS App"").
- Click Generate public and private key. A
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
). - Enable the Messages capability.
- You'll see fields for Inbound URL and Status URL. We'll fill these later when we set up ngrok and our server, but for now, you can enter placeholder URLs like
http://example.com/webhooks/inbound
andhttp://example.com/webhooks/status
. We will update these. - Click Generate new application.
- You will be redirected to the application's page. Copy the Application ID.
- Paste the Application ID into your
.env
file forVONAGE_APPLICATION_ID
.
3. Link Your Vonage Number:
- On the same application page, scroll down to Link virtual numbers.
- Find the Vonage virtual number you purchased earlier and click the Link button next to it. This directs incoming messages for that number to this application's configured webhooks.
- Copy your Vonage virtual number (including the country code, e.g.,
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.
- Navigate to your main Vonage Dashboard Account Settings.
- Scroll down to API settings.
- Find the Default SMS Setting.
- Ensure Messages API is selected. If not, select it and click Save changes.
5. Fill Remaining .env
Variables:
- Set
PORT
to3000
(or another port if you prefer). - Set
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
// src/send-sms.js
require('dotenv').config(); // Load environment variables from .env file
const { Vonage } = require('@vonage/server-sdk');
const { SMS } = require('@vonage/messages');
// --- Configuration ---
// Ensure all required environment variables are set
const requiredEnvVars = [
'VONAGE_API_KEY',
'VONAGE_API_SECRET',
'VONAGE_APPLICATION_ID',
'VONAGE_APPLICATION_PRIVATE_KEY_PATH',
'VONAGE_NUMBER',
'TO_NUMBER'
];
for (const envVar of requiredEnvVars) {
if (!process.env[envVar]) {
console.error(`Error: Environment variable ${envVar} is not set.`);
process.exit(1); // Exit if essential config is missing
}
}
const vonageApiKey = process.env.VONAGE_API_KEY;
const vonageApiSecret = process.env.VONAGE_API_SECRET;
const vonageAppId = process.env.VONAGE_APPLICATION_ID;
const vonagePrivateKeyPath = process.env.VONAGE_APPLICATION_PRIVATE_KEY_PATH;
const vonageNumber = process.env.VONAGE_NUMBER; // Sender number (your Vonage virtual number)
const toNumber = process.env.TO_NUMBER; // Recipient number
const messageText = 'Hello from Vonage and Node.js!';
// --- Initialize Vonage Client ---
// We use Application ID and Private Key for authentication with the Messages API
const vonage = new Vonage({
apiKey: vonageApiKey, // Optional but good practice
apiSecret: vonageApiSecret, // Optional but good practice
applicationId: vonageAppId,
privateKey: vonagePrivateKeyPath, // Path to your downloaded private key
});
// --- Send SMS Function ---
async function sendSms() {
console.log(`Attempting to send SMS from ${vonageNumber} to ${toNumber}...`);
try {
const resp = await vonage.messages.send(
new SMS(
messageText, // The text message content
toNumber, // Recipient's phone number
vonageNumber // Sender's phone number (Your Vonage virtual number)
)
);
console.log('SMS submitted successfully!');
console.log('Message UUID:', resp.messageUuid); // Unique identifier for the message
} catch (error) {
console.error('Error sending SMS:');
// Log detailed error information if available
if (error.response) {
console.error('Status:', error.response.status);
console.error('Data:', error.response.data);
} else {
console.error(error.message);
}
// Consider more robust error handling/logging in production
}
}
// --- Execute Sending ---
sendSms();
Explanation:
require('dotenv').config();
: Loads the variables from your.env
file intoprocess.env
.- Environment Variable Check: Ensures all necessary credentials and numbers are present before proceeding.
- Vonage Initialization: We create a
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.- Error Handling: The
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:
npm run send-sms
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
// src/server.js
require('dotenv').config(); // Load environment variables
const express = require('express');
const app = express();
// --- Configuration ---
const port = process.env.PORT || 3000; // Use port from .env or default to 3000
// --- Middleware ---
// Express needs to parse JSON request bodies for Vonage webhooks
app.use(express.json());
// It's also good practice to handle URL-encoded data
app.use(express.urlencoded({ extended: true }));
// --- Webhook Endpoint for Inbound SMS ---
// Vonage will send POST requests to this endpoint when an SMS is received
app.post('/webhooks/inbound', (req, res) => {
console.log('--- Inbound SMS Received ---');
console.log('Request Body:', JSON.stringify(req.body, null, 2)); // Log the entire payload
// --- Basic Validation (Messages API Format) ---
// Check for essential fields expected from Vonage Messages API for an inbound SMS
// Note: We rename 'message-timestamp' to 'timestamp' during destructuring.
const { msisdn, to, text, messageId, 'message-timestamp': timestamp } = req.body;
if (!msisdn || !to || !text || !messageId || !timestamp) {
console.warn('Received incomplete or unexpected webhook payload.');
// Still send 200 OK so Vonage doesn't retry, but log the issue.
return res.status(200).end();
}
// --- Process the Message ---
console.log(`From: ${msisdn}`); // Sender's phone number
console.log(`To: ${to}`); // Your Vonage number
console.log(`Text: ${text}`); // Message content
console.log(`Message ID: ${messageId}`); // Vonage's message identifier
console.log(`Timestamp: ${timestamp}`); // Time message was received by Vonage
// --- !!! IMPORTANT: Acknowledge the Webhook Immediately !!! ---
// Vonage expects a 200 OK response quickly.
// If it doesn't receive one, it will retry sending the webhook,
// potentially leading to duplicate processing.
// Perform any time-consuming logic *after* sending the response.
res.status(200).end();
// --- Further Processing (Example) ---
// Here you would typically:
// 1. Store the message in a database.
// 2. Trigger business logic (e.g., check keywords, route to support).
// 3. Potentially send a reply SMS using the `sendSms` logic from send-sms.js
// (You would refactor sendSms into a reusable function).
console.log('Webhook acknowledged. Further processing can happen here.');
});
// --- Optional: Status Webhook Endpoint ---
// Vonage sends delivery receipts and other status updates here
app.post('/webhooks/status', (req, res) => {
console.log('--- Message Status Received ---');
console.log('Request Body:', JSON.stringify(req.body, null, 2));
// Process status updates (e.g., update message status in database)
// Example: Check for delivered, failed, etc.
res.status(200).end(); // Acknowledge receipt
});
// --- Basic Health Check Endpoint ---
app.get('/health', (req, res) => {
res.status(200).send('OK');
});
// --- Start Server ---
app.listen(port, () => {
console.log(`Server listening on port ${port}`);
console.log(`Webhook endpoints:`);
console.log(` Inbound SMS: POST /webhooks/inbound`);
console.log(` Status: POST /webhooks/status`);
console.log('Waiting for Vonage webhooks...');
});
Explanation:
- Middleware (
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.- Logging: We log the entire request body to see the structure of the data Vonage sends (useful for debugging). The expected format depends on the Default SMS Setting in your Vonage account (we set it to Messages API).
- Basic Validation: We check for key fields expected in the Messages API inbound format (
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. - Processing: We extract and log the relevant information. This is where your application's core logic for handling inbound messages would go.
- Crucial:
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:
npm run dev
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:
ngrok http 3000
Replace 3000
if you used a different port.
3. Get ngrok URL: ngrok will display output similar to this:
ngrok by @inconshreveable (Ctrl+C to quit)
Session Status online
Account Your Name (Plan: Free)
Version x.x.x
Region United States (us-east-1)
Web Interface http://127.0.0.1:4040
Forwarding http://<random-string>.ngrok.io -> http://localhost:3000
Forwarding https://<random-string>.ngrok.io -> http://localhost:3000
Connections ttl opn rt1 rt5 p50 p90
0 0 0.00 0.00 0.00 0.00
- Copy the
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. - The Web Interface (
http://127.0.0.1:4040
) is a useful dashboard to inspect webhook requests ngrok receives.
4. Update Vonage Application Webhooks:
- Go back to your Vonage Application settings in the dashboard (Applications).
- Click Edit on the application you created earlier.
- Update the webhook URLs:
- Inbound URL: Paste your
https://<random-string>.ngrok.io
URL and append/webhooks/inbound
. Example:https://a1b2c3d4e5.ngrok.io/webhooks/inbound
- Status URL: Paste your
https://<random-string>.ngrok.io
URL and append/webhooks/status
. Example:https://a1b2c3d4e5.ngrok.io/webhooks/status
- Inbound URL: Paste your
- Ensure the HTTP Method for both is set to
POST
. - Click Save changes.
6. Verification and Testing
Now, let's test the complete two-way flow.
1. Ensure Services are Running:
- Your Node.js server (
npm run dev
) is running in one terminal. ngrok http 3000
is running in another terminal.
2. Test Inbound SMS:
- Using your personal mobile phone, send an SMS message to your Vonage virtual number (the one stored in
VONAGE_NUMBER
). - Observe the terminal window where your Node.js server is running. You should see log output similar to:
--- Inbound SMS Received ---
Request Body: {
""msisdn"": ""12015550101"",
""to"": ""14155550100"",
""messageId"": ""a1b2c3d4-e5f6-7890-abcd-ef1234567890"",
""text"": ""Hello from my phone!"",
""type"": ""text"",
""keyword"": ""HELLO"",
""message-timestamp"": ""2025-04-20T10:30:00Z""
}
From: 12015550101
To: 14155550100
Text: Hello from my phone!
Message ID: a1b2c3d4-e5f6-7890-abcd-ef1234567890
Timestamp: 2025-04-20T10:30:00Z
Webhook acknowledged. Further processing can happen here.
- You can also check the ngrok web interface (
http://127.0.0.1:4040
) to inspect the raw HTTP request received from Vonage.
3. Test Outbound SMS (Again):
- Run the sending script again:
npm run send-sms
- You should receive the SMS on your
TO_NUMBER
phone. - Check the status webhook logs in your server terminal (if you sent the message while the server was running with the status webhook configured). You might see delivery updates coming into the
/webhooks/status
endpoint.
7. Security Features
While this guide covers basics, production applications require robust security.
- Input Validation/Sanitization: Never trust data received in webhooks. Use libraries like
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. - Webhook Signature Verification (Highly Recommended): Vonage can sign webhook requests using JWT (JSON Web Tokens) or a shared secret signature. Verifying this signature ensures the request genuinely came from Vonage and wasn't tampered with. Consult the Vonage documentation on Signed Webhooks for implementation details. This typically involves comparing a calculated signature based on the payload and your secret with the signature provided in the request header.
- Rate Limiting: Protect your webhook endpoints from abuse or accidental loops by implementing rate limiting. Libraries like
express-rate-limit
can restrict the number of requests from a single IP address or based on other criteria. - Environment Variable Security: Never commit
.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. - HTTPS: Always use HTTPS for your webhook URLs in production (ngrok provides this, and deployment platforms typically handle SSL termination).
8. Error Handling, Logging, and Retries
Production systems need better error handling and logging than simple console.log
.
- Consistent Error Handling: Implement a centralized error handling strategy in Express using error-handling middleware. Log errors with sufficient detail (stack trace, request context).
- Structured Logging: Use libraries like
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. - Vonage Retries: Remember that Vonage will retry sending webhooks if it doesn't receive a
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). - Outbound API Call Retries: Network issues can cause API calls to Vonage (like
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:
- Use an ORM (Object-Relational Mapper) like Sequelize, Prisma, or TypeORM to interact with your chosen database (PostgreSQL, MySQL, etc.).
- Implement functions to insert inbound messages and insert/update outbound messages based on status webhooks.
- Ensure database queries are indexed appropriately (especially on
vonage_message_uuid
).
10. Deployment and CI/CD
Deploying your Node.js application involves several steps.
General Steps:
- Choose a Platform: Heroku, AWS (EC2, Lambda, Elastic Beanstalk), Google Cloud (App Engine, Cloud Run), DigitalOcean App Platform, Vercel, Netlify (for serverless functions), etc.
- Configure Environment Variables: Securely provide your
VONAGE_*
credentials,PORT
, and database connection strings to the production environment. Do not deploy your.env
file. - Build Step (If applicable): If using TypeScript or a build process, run the build command.
- Install Dependencies: Run
npm install --production
on the server to install only necessary dependencies. - Start the Application: Use a process manager like
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
. - Update Vonage Webhooks: Once deployed, update the Inbound and Status URLs in your Vonage Application settings to point to your production server's public URL (e.g.,
https://your-app-domain.com/webhooks/inbound
).
CI/CD Pipeline:
- Set up a pipeline (using GitHub Actions, GitLab CI, Jenkins, etc.) to automate:
- Linting/Formatting: Check code style (
eslint
,prettier
). - Testing: Run unit and integration tests (
jest
,mocha
). - Building: Create a production build (if needed).
- Deploying: Push the code/build artifact to your hosting platform.
- Linting/Formatting: Check code style (
- Implement rollback procedures in case a deployment fails.
11. Troubleshooting and Caveats
- Ngrok Session Expired: Free ngrok URLs are temporary. You'll need to restart ngrok and update Vonage webhooks if your session expires or you restart your computer.
- Incorrect Webhook URL/Method: Ensure URLs in Vonage match your server routes exactly (
/webhooks/inbound
,/webhooks/status
) and the method isPOST
. - Firewall Issues: Firewalls (local or network) might block incoming connections from Vonage or outgoing connections from ngrok.
- Missing
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. - Invalid Credentials/Keys: Double-check API Key/Secret, Application ID, and the path to your
private.key
. Ensure the key file has the correct read permissions for your Node.js process. - Number Not Linked: Make sure the Vonage number is correctly linked to the Vonage Application in the dashboard.
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.- Character Encoding/Limits: Standard SMS are limited (160 GSM-7 characters). Longer messages or those with non-GSM characters (like many emojis) become multi-part Unicode messages, affecting pricing and delivery. The Messages API handles this, but be aware of potential length issues.
- Sender ID Rules: Regulations vary by country. Some countries require pre-registration for alphanumeric sender IDs, while others (like the US) typically require using a virtual number as the sender ID.
- Message Delivery Delays: SMS delivery is not always instantaneous and can be subject to carrier delays. Use Status webhooks to track final delivery.
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.