Frequently Asked Questions
Use the Vonage Messages API and Node.js SDK. After setting up a Vonage application and linking a virtual number, you can send SMS messages programmatically via the `/send-sms` endpoint, providing the recipient's number and the message text in the request body. The Vonage SDK simplifies the API interaction, handling authentication and request formatting.
The Vonage Messages API is a unified platform for sending and receiving messages across multiple channels, including SMS, MMS, WhatsApp, and more. This guide focuses on using the API for sending SMS messages and receiving delivery status updates, enabling reliable two-way communication.
Ngrok creates a secure tunnel from your local development environment to the public internet. This allows Vonage's webhooks (inbound SMS and delivery receipts) to reach your local server during development, as these webhooks require a publicly accessible URL.
Database integration is highly recommended for production applications. A database (like PostgreSQL or MongoDB) helps persist message data (inbound, outbound, delivery statuses), enabling tracking, analytics, and features like message history or user opt-out management.
Yes, you can receive inbound SMS messages to your Vonage virtual number. By configuring the inbound webhook URL in your Vonage Application settings, Vonage will forward incoming messages to your application as POST requests, allowing you to process and respond to them.
Vonage sends delivery status updates (DLRs) via webhooks to the Status URL configured in your Vonage Application. The payload contains the `message_uuid` and `status` (e.g., 'delivered', 'failed'). You can use this information to update message status in your database and implement logic based on delivery outcomes.
A Vonage Application acts as a container for your communication configuration, including API keys, webhook URLs, and linked virtual numbers. It centralizes the settings for your project and simplifies integration with Vonage services.
Key security measures include using HTTPS for all communication, securing your `.env` file containing API credentials, validating input to prevent vulnerabilities, and implementing rate limiting on public endpoints to protect against abuse.
Implement `try...catch` blocks around `vonage.messages.send()` to handle API errors. Inspect the `err` object for details. For temporary failures (e.g., network issues), consider implementing retry logic with exponential backoff using a library like `async-retry`.
Log key events (app start, SMS sent/received, status updates), errors with stack traces, `message_uuid` for tracking, and any configuration issues. Use a dedicated logging library (like `pino` or `winston`) for structured logging and advanced features.
Verify `ngrok` is running (if in development) and forwarding to the correct port. Ensure the webhook URLs in your Vonage Application settings match the `ngrok` URL and paths (`/webhooks/inbound`, `/webhooks/status`). Check your firewall and application logs for errors.
Confirm the Status URL is correctly set in your Vonage Application. Note that delivery receipts (DLRs) are carrier-dependent and not always reliable. Some networks/countries might not provide `delivered` status. The message might still be delivered even if the status is not 'delivered'.
E.164 is an international telephone number format (e.g., +14155550101). Always use this format for recipient numbers (`to`) when sending SMS with Vonage. This ensures correct routing and avoids issues with regional number variations.
Vonage handles concatenation of long messages exceeding the standard SMS character limit. Be aware that longer messages are split into segments, and you are billed per segment. Consider message length and costs when designing your application.
This guide provides a complete walkthrough for building a production-ready Node.js application using Express to send SMS messages, receive inbound SMS messages, and handle delivery status updates (callbacks) via the Vonage Messages API. We will cover everything from project setup and core implementation to error handling, security, deployment, and testing.
Technologies: Node.js, Express, Vonage Messages API, Vonage Node SDK, ngrok (for development)
Project Overview and Goals
What We'll Build: A Node.js application using the Express framework that can:
Problem Solved: This application addresses the need for reliable, two-way SMS communication within a Node.js environment. It provides mechanisms to not only send messages but also to confirm their delivery status and react to messages sent by users, enabling interactive SMS applications, notification systems with delivery confirmation, and more.
Technologies Used:
@vonage/server-sdk
): Simplifies interaction with Vonage APIs within a Node.js application..env
file intoprocess.env
, keeping sensitive credentials out of source code.System Architecture:
Final Outcome & Prerequisites: By the end of this guide, you will have a functional Node.js Express application capable of sending SMS, receiving SMS, and tracking delivery statuses. The application will be structured with considerations for security, error handling, and deployment.
Prerequisites:
ngrok config add-authtoken YOUR_TOKEN
).1. Setting Up the Project
Let's initialize our Node.js project and install the necessary dependencies.
Create Project Directory: Open your terminal or command prompt and create a new directory for your project.
Initialize Node.js Project: Initialize the project using npm (or yarn). The
-y
flag accepts default settings.This creates a
package.json
file.Install Dependencies: Install Express for the web server, the Vonage Node SDK for interacting with the API, and
dotenv
for managing environment variables.Set Up Project Structure: Create a basic structure for clarity.
src/app.js
: Main application code.config/
: (Optional) For more complex configurations later..env
: Stores sensitive credentials (API keys, etc.). Never commit this file..env.example
: A template showing required environment variables. Commit this file..gitignore
: Specifies files/directories Git should ignore.private.key
: Placeholder file for the Vonage Application private key. This file will be replaced by the one you download from Vonage later.Configure
.gitignore
: Add common Node.js ignores and ensure.env
and the private key are excluded.Define Environment Variables (
.env.example
): List the variables needed. Copy this content into both.env
and.env.example
. You'll fill.env
with actual values later.Why
.env
? Storing credentials and configuration separate from code is crucial for security and flexibility across different environments (development, staging, production).2. Integrating with Vonage (Third-Party Service)
Now, let's configure our Vonage account and application to enable SMS sending and receiving with callbacks.
Log in to Vonage Dashboard: Access your account at dashboard.nexmo.com.
Set Default SMS API to ""Messages API"":
Create a Vonage Application: Vonage Applications act as containers for your communication configurations, including keys and webhooks.
private.key
file. Save this file in the root of your project directory, replacing the placeholderprivate.key
file you created earlier. The public key is stored by Vonage. These keys are used for authenticating your requests to the Messages API.ngrok
comes in.Start
ngrok
: Open a new terminal window in your project directory and startngrok
, telling it to forward traffic to the port your Express app will run on (defaulting to 3000 from our.env.example
).ngrok
will provideForwarding
URLs (http and https). Copy thehttps
URL. It will look something likehttps://random-subdomain.ngrok.io
.Configure Webhook URLs:
ngrok
https URL into the webhook fields, appending specific paths for clarity:YOUR_NGROK_HTTPS_URL/webhooks/inbound
YOUR_NGROK_HTTPS_URL/webhooks/status
Note Application ID: After creation, you'll be taken to the application details page. Copy the Application ID — you'll need it for your
.env
file.Link Your Vonage Number:
Update
.env
File: Now, open your.env
file (the one without.example
) and fill in the values you obtained:Security: Remember,
.env
contains secrets. Ensure it's in your.gitignore
and never commit it to version control. Use environment variable management systems provided by your deployment platform in production.3. Implementing Core Functionality (Code)
Let's write the Node.js/Express code to handle sending and receiving SMS.
File:
src/app.js
Code Explanation:
dotenv
,express
,Vonage SDK
,path
).dotenv.config()
loads variables from.env
.JSON
andURL-encoded
request bodies, crucial for handling webhook payloads.applicationId
andprivateKey
path from environment variables. Includes error checking for missing variables and key file reading issues. Usespath.resolve
to ensure the path toprivate.key
is correct regardless of where the script is run from./send-sms
Route: APOST
endpoint to trigger sending an SMS. It expectsto
(recipient number) andtext
(message content) in the JSON body. It performs basic validation and callsvonage.messages.send()
using thesms
channel. Success/error responses are logged and returned to the client. Includes a strong warning about needing protection in production./webhooks/inbound
Route: APOST
endpoint matching the Inbound URL set in the Vonage Application. Vonage sends data here when your virtual number receives an SMS. The code logs the payload (req.body
), includes aTODO
for user logic, and immediately sends a200 OK
response. Crucially, send the 200 OK quickly before doing heavy processing./webhooks/status
Route: APOST
endpoint matching the Status URL. Vonage sends delivery status updates here for messages you sent. It logs the payload, includes aTODO
, and sends200 OK
./health
Route: A simple endpoint often used by monitoring systems to check if the application is running.APP_PORT
(defaulting to 3000) and starts the Express server only if the script is run directly (usingif (require.main === module)
). Includes helpful reminders aboutngrok
configuration during development.app
instance so it can be imported by testing frameworks like Supertest.4. Running and Testing Locally
Ensure
ngrok
is running (from Step 2.4) and forwarding to the correct port (e.g., 3000). Double-check the HTTPS URL matches the one in your Vonage Application webhook settings.Ensure your
.env
file is correctly filled with your Vonage Application ID, number, and the correct path toprivate.key
.Start the Node.js Application: In the terminal where you created the project (not the
ngrok
one), run:You should see ""Server listening at http://localhost:3000"" and the
ngrok
reminder messages.Test Sending SMS: Use a tool like
curl
or Postman to send a POST request to your local/send-sms
endpoint. ReplaceYOUR_PHONE_NUMBER
with your actual mobile number in E.164 format (e.g.,14155550101
).node src/app.js
should log ""Attempting to send SMS..."" and then ""Message sent successfully: ..."" with amessage_uuid
.curl
command should receive a JSON response like{""message_uuid"":""..."",""status"":""Message submitted""}
.Test Receiving Status Updates:
node src/app.js
should log ""--- Delivery Status Update Received ---"" followed by the JSON payload from Vonage. Look for themessage_uuid
matching the sent message and astatus
field (e.g.,delivered
,accepted
,buffered
). The exact status depends on carrier support and network conditions.Test Receiving Inbound SMS:
node src/app.js
should log ""--- Inbound SMS Received ---"" followed by the JSON payload containing the message details (from
,text
, etc.).5. Implementing Error Handling, Logging, and Retries
Production applications need robust error handling and logging.
Error Handling:
try...catch
block aroundvonage.messages.send()
demonstrates handling specific API call errors. Inspect theerr
object (especiallyerr.response.data
orerr.message
) for details from the Vonage API./webhooks/inbound
,/webhooks/status
) intry...catch
blocks. Log any errors, but still ensure you send a200 OK
response to Vonage if possible, unless the request itself is fundamentally invalid (e.g., malformed JSON, although Express middleware often handles this). If processing fails, log the error and potentially queue the task for retry later.app.use((err, req, res, next) => {...})
) catches errors not caught in specific routes.Logging:
console.log
is used here for simplicity. For production, use a dedicated logging library likepino
orwinston
.message_uuid
), configuration issues.Retries:
200 OK
. Your primary responsibility is to respond quickly with200 OK
.5xx
error from Vonage), you might want to implement a retry strategy with exponential backoff. Libraries likeasync-retry
can help.200 OK
(e.g., database write fails), you should log the error and consider using a background job queue (like BullMQ, Kue, RabbitMQ) to retry the processing task independently of the webhook request.6. Database Integration (Optional but Recommended)
For persistence and tracking, integrate a database.
messages
table:message_uuid
(VARCHAR/TEXT, PRIMARY KEY/UNIQUE) - From Vonage response/webhooksdirection
(ENUM/VARCHAR: 'outbound', 'inbound')to_number
(VARCHAR)from_number
(VARCHAR)body
(TEXT)status
(VARCHAR, e.g., 'submitted', 'delivered', 'failed', 'read') - Updated via status webhookvonage_timestamp
(TIMESTAMP WITH TIME ZONE) - From webhook payloaderror_code
(VARCHAR, nullable) - From status webhook if failed/rejectedcreated_at
(TIMESTAMP WITH TIME ZONE)updated_at
(TIMESTAMP WITH TIME ZONE)vonage.messages.send()
, insert a record into the DB with themessage_uuid
,direction='outbound'
,to
,from
,text
,status='submitted'
, and timestamps.req.body.message_uuid
and update itsstatus
,vonage_timestamp
,error_code
(if present), andupdated_at
. Handle cases where themessage_uuid
might not be found (log an error).direction='inbound'
, details fromreq.body
,status='received'
, and timestamps.7. Adding Security Features
Protect your application and user data.
Webhook Security:
https
URLs for your webhooks./webhook
). The paths/webhooks/inbound
and/webhooks/status
used here are reasonably specific.Secrets Management:
.env
locally, platform-provided secrets in production). Do not commit.env
orprivate.key
.Input Validation:
/send-sms
). Ensure required fields are present, types are correct, and potentially check formats (e.g., E.164 for phone numbers). Use libraries likeexpress-validator
orjoi
.dompurify
(if rendering in HTML) or proper database parameterization are key.Rate Limiting:
/send-sms
if exposed) from abuse. Use middleware likeexpress-rate-limit
.HTTPS:
ngrok
provides this locally. Use a load balancer or reverse proxy (like Nginx, Caddy, or platform services like AWS ALB/Cloudflare) to handle SSL termination in production.8. Handling Special Cases
Real-world SMS involves nuances.
+14155550101
) when sendingto
numbers to Vonage. Vonage typically providesfrom
numbers in webhooks in a similar format (sometimes without the+
). Standardize formats in your database if necessary.delivered
status updates (DLRs). The status might remainaccepted
orbuffered
. Design your application logic to handle this uncertainty.failed
orrejected
statuses are usually reliable indicators of non-delivery.timestamp
) are typically UTC (often ISO 8601 format). Store timestamps in your database consistently (UTC is recommended) and convert to local time zones only for display purposes.9. Implementing Performance Optimizations
Ensure your webhook handlers are fast and efficient.
200 OK
response immediately.200 OK
. A separate worker process handles the jobs from the queue.message_uuid
) are indexed in your database..env
).10. Adding Monitoring, Observability, and Analytics
Understand how your application is performing and diagnose issues.
/health
endpoint is a basic start. Production monitoring systems (like AWS CloudWatch, Datadog, Prometheus/Grafana, UptimeRobot) can ping this endpoint to ensure the app is live.send
calls)prom-client
for Prometheus or integrate with APM tools (Datadog APM, New Relic).11. Troubleshooting and Caveats
Common issues and their solutions.
ngrok
running? Is it forwarding to the correct port? Has the URL expired (freengrok
URLs are temporary)?ngrok
HTTPS URL + path (/webhooks/inbound
,/webhooks/status
)?req.body
correctly? Ensureexpress.json()
middleware is used. Log the raw body to see what Vonage is sending.res.status(200).end()
reliably and quickly? Check the Vonage Dashboard (Logs section) for webhook retry attempts.VONAGE_APPLICATION_ID
and the path/content ofVONAGE_PRIVATE_KEY_PATH
in.env
. Ensure the private key file is readable by the Node process.VONAGE_NUMBER
correctly set in.env
and is it linked to the Vonage Application used for initialization? Is the number SMS capable for the destination?err
object from thecatch
block in/send-sms
—err.response.data
often contains detailed error codes and messages from Vonage./webhooks/status
not hit):vonage.messages.send
), and webhook structures. Configuring webhooks in the main "Settings" page applies to the legacy SMS API and uses a different format than the Application-specific webhooks for the Messages API.12. Deployment and CI/CD
Moving from local development to production.
.env
file on server if managed securely). Do not commit production secrets to Git.private.key
file to your production environment. Options include:VONAGE_PRIVATE_KEY_BASE64
) and decoding it into a temporary file at runtime.pm2
or rely on the platform's built-in service management (e.g., systemd, Heroku dynos) to keep your Node.js app running, handle restarts, and manage logs.