Frequently Asked Questions
Send SMS messages using Node.js, Express, and the Vonage Messages API by creating a POST request to the /send-sms endpoint with the recipient's number and the message text in the request body. The server-side code then uses the Vonage SDK to handle sending the SMS through the Vonage API.
The Vonage Messages API is a multi-channel communication platform for sending and receiving messages across various channels, including SMS. This tutorial focuses on using the API for sending and receiving text messages (SMS) within a Node.js application.
Setting up Vonage involves purchasing a virtual number, creating a Vonage application, and linking the number to your application. You will need to generate public and private keys, save the private key securely and enable the messages capability and configure webhook URLs for inbound messages and delivery statuses.
ngrok creates a secure tunnel to your local server, providing a temporary public HTTPS URL. This allows Vonage to deliver webhooks to your local development environment during testing, simulating how real-world webhooks function when deployed.
Vonage uses webhooks to send real-time updates to your server. Inbound SMS messages to your Vonage number trigger a webhook to the '/webhooks/inbound' endpoint. Delivery status updates for your sent SMS messages trigger a webhook to the '/webhooks/status' endpoint.
Receive incoming SMS messages sent to your Vonage virtual number by configuring a webhook URL in your Vonage application settings that points to the '/webhooks/inbound' endpoint of your Node.js application.
Delivery status updates are received via webhooks sent to the '/webhooks/status' endpoint of your server. The webhook request will include the message UUID, the status of the delivery (e.g., 'submitted', 'delivered', 'rejected'), and a timestamp.
The project utilizes several Node.js libraries: 'express' for the web framework, '@vonage/server-sdk' for interacting with the Vonage API, 'dotenv' for managing environment variables, and optionally 'jsonwebtoken' for webhook signature verification in a production environment.
You can install the necessary dependencies (Express, the Vonage Server SDK, and dotenv) by using either 'npm install express @vonage/server-sdk dotenv' or 'yarn add express @vonage/server-sdk dotenv' in your terminal within the project directory.
Safeguard the private key by never committing it to version control and securing file permissions. In production environments, utilize environment variables provided by your hosting platform to handle sensitive information like API keys.
Webhook signature verification, especially with JWT (JSON Web Tokens), is crucial in production for confirming the authenticity of incoming webhooks. This process ensures that the incoming requests originate from Vonage and haven't been tampered with.
Common errors include ngrok timing out, credentials not being set correctly in .env, webhook URLs being wrong, private keys being handled incorrectly, and servers not running.
Vonage expects a 2xx response from your webhook endpoint to acknowledge that your server received it. Without a 2xx response, the system may assume the webhook failed and retry the request, potentially causing duplicate messages or other issues.
This guide provides a step-by-step walkthrough for building a robust Node.js application using the Express framework to send SMS messages, receive inbound SMS, and handle delivery status updates via webhooks using the Vonage Messages API.
We will cover everything from project setup and core implementation to security considerations and deployment advice, aiming to provide a solid foundation for a production-ready solution.
Goal: Build a Node.js Express server that can:
Technologies Used:
.env
file.System Architecture:
Prerequisites:
ngrok authtoken your-token
after signing up for an ngrok account.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: Initialize the project using npm (or yarn). This creates a
package.json
file.Install Dependencies: Install Express for the web server, the Vonage SDK, and
dotenv
for managing environment variables.Create Project Structure: Create the main application file and a file for environment variables.
Configure
.gitignore
: Addnode_modules
and.env
to your.gitignore
file to prevent committing sensitive information and dependencies.Note: We also add
private.key
preemptively. It's good practice to ensure this sensitive file, which you might download directly into the project folder later, is never accidentally committed to version control.Set Up Environment Variables (
.env
): Create a.env
file in the project root. We will populate this with credentials obtained from Vonage in a later step. Use clear variable names and avoid quotes unless the value contains spaces or special characters.VONAGE_APPLICATION_ID
: Identifies your specific Vonage application.VONAGE_PRIVATE_KEY_PATH
: The local path to the private key file associated with your Vonage application, used for authenticating API requests.VONAGE_NUMBER
: Your Vonage virtual phone number used for sending SMS.MY_NUMBER
: Your personal mobile number for testing purposes (ensure it's in E.164 format).PORT
: The port your local Express server will listen on.2. Integrating with Vonage
Before writing code, we need to configure Vonage by creating an Application and linking a number. This provides the necessary credentials and sets up webhook URLs.
Log in to Vonage Dashboard: Access your Vonage API Dashboard. (Note: Dashboard layout may change over time).
Purchase a Virtual Number: If you don't have one, navigate to the section for managing numbers (e.g.,
Numbers
->Buy numbers
). Find a number with SMS capabilities in your desired country and purchase it. Note this number down. Consult the Vonage documentation if you cannot easily locate this section.Create a Vonage Application:
Applications
->Create a new application
).Generate public and private key
. Immediately save theprivate.key
file that downloads. Place this file in your project's root directory (or updateVONAGE_PRIVATE_KEY_PATH
in.env
if you save it elsewhere). The public key is stored by Vonage.Messages
capability.Inbound URL
andStatus URL
. We will fill these shortly using ngrok. For now, you can enter temporary placeholders likehttp://example.com/webhooks/inbound
andhttp://example.com/webhooks/status
. We must update these later. Ensure the HTTP Method is set toPOST
for both.Get Application ID: After creation, you'll be taken to the application's page or overview. Copy the
Application ID
displayed.Link Your Virtual Number:
Linked numbers
.Link
your virtual number and select the number you purchased earlier.Update
.env
File: Now, populate your.env
file with theApplication ID
and your VonageVirtual Number
. EnsureVONAGE_PRIVATE_KEY_PATH
points correctly to where you saved theprivate.key
file. Add your personal number toMY_NUMBER
.Start ngrok: We need a publicly accessible URL to receive webhooks from Vonage on our local machine. Open a new terminal window in your project directory and run ngrok, pointing it to the port your Express server will use (defined in
.env
as 3000).ngrok will display a
Forwarding
URL (e.g.,https://randomstring.ngrok.io
). Copy this HTTPS URL.Configure Webhook URLs in Vonage Application:
Applications
-> Your App Name ->Edit
or similar).Messages
capability URLs:/webhooks/inbound
. Example:https://randomstring.ngrok.io/webhooks/inbound
/webhooks/status
. Example:https://randomstring.ngrok.io/webhooks/status
POST
.Save changes
.Why these specific URLs?
Inbound URL
: Vonage sends data about incoming SMS messages (sent to your Vonage number) to this endpoint.Status URL
: Vonage sends delivery status updates (e.g.,submitted
,delivered
,rejected
) for messages you send to this endpoint.3. Implementing Core Functionality (Express Server)
Now let's write the Node.js code in
server.js
to handle sending, receiving, and status updates.Code Explanation:
dotenv
, importsexpress
andVonage
, initializes Express, sets up middleware for parsing request bodies.Vonage
instance using theapplicationId
andprivateKey
path from.env
. Includes error handling if the key file is missing or credentials are not set./send-sms
(POST):to
(recipient number) andtext
(message content) in the JSON request body.!to || !text
). Note: Production applications need more robust validation (see Section 6).vonage.messages.send()
with appropriate parameters (message_type
,to
,from
,channel
,text
).message_uuid
upon successful submission to Vonage.try...catch
for robust error handling, logging errors, and returning appropriate HTTP status codes (400 for bad input, 500 or specific Vonage status for API errors)./webhooks/inbound
(POST):Inbound URL
configured in Vonage.VONAGE_NUMBER
, Vonage makes a POST request here.from
), recipient (to
), message content (text
), and the full request body. Note: Production applications should verify the webhook signature (see Section 6).200 OK
status usingres.status(200).end()
. If Vonage doesn't receive a 2xx response, it will assume the webhook failed and retry, potentially leading to duplicate processing./webhooks/status
(POST):Status URL
configured in Vonage.submitted
,delivered
,failed
,rejected
), Vonage makes a POST request here.message_uuid
(linking it back to the sent message), thestatus
, timestamp, any potential error details, and the full request body. Note: Production applications should verify the webhook signature (see Section 6).200 OK
status to acknowledge receipt.4. Running and Testing the Application
Ensure ngrok is Running: Keep the terminal window where you started
ngrok http 3000
open. Confirm the HTTPS URL is correctly configured in your Vonage Application settings.Start the Node.js Server: In the terminal window for your project directory (where
server.js
is), run:You should see output indicating the server is listening and the Vonage SDK initialized.
Test Sending SMS: Open a new terminal window or use a tool like Postman or Insomnia to send a POST request to your
/send-sms
endpoint. ReplaceYOUR_PERSONAL_MOBILE_NUMBER
with the actual value from your.env
file (MY_NUMBER
).Using
curl
:curl
: You should get a JSON response like{""message"":""SMS sent successfully"",""message_uuid"":""some-uuid-string""}
.server.js
: You'll see logs for the/send-sms
request and ""Message sent successfully..."".server.js
: Soon after, you should see logs from/webhooks/status
showing the status changing (e.g.,submitted
, then potentiallydelivered
). Check themessage_uuid
to match the sent message.Test Receiving Inbound SMS:
VONAGE_NUMBER
(the one configured in.env
).server.js
: You should see logs from/webhooks/inbound
showing the message details (From:
,To:
,Text:
)./webhooks/inbound
.Test Delivery Failure (Optional): Try sending an SMS to an invalid or non-existent number via the
/send-sms
endpoint. Observe the/webhooks/status
logs in yourserver.js
terminal – you should receive statuses likefailed
orrejected
with corresponding error codes/reasons.5. Error Handling and Logging
/send-sms
route includestry...catch
to handle errors during the Vonage API call. It attempts to parse and return specific error details from the Vonage response./inbound
,/status
) encounter an error processing the request before sending the200 OK
, Vonage will retry. Implement robust internal error handling within these routes if you perform complex logic (e.g., database lookups).console.log
andconsole.error
. For production, this is insufficient. You should implement a structured logging library likewinston
orpino
. This allows for better log formatting (e.g., JSON), writing logs to files or external services, setting different log levels (debug, info, warn, error), and easier log analysis. This guide does not include the implementation of structured logging.2xx
status code response from your webhook endpoints within a reasonable time (usually a few seconds) to consider the delivery successful. Design your webhook handlers to be fast and acknowledge receipt quickly, performing heavier processing asynchronously if needed (e.g., using a message queue).6. Security Considerations
.env
file or yourprivate.key
file to source control. Use a.gitignore
file as shown. In production, use your hosting provider's mechanism for managing environment variables securely.private.key
file like a password. Ensure its file permissions restrict access (e.g.,chmod 400 private.key
on Linux/macOS).jsonwebtoken
in your Node.js app (npm install jsonwebtoken
)./webhooks/inbound
and/webhooks/status
routes to verify theAuthorization: Bearer <token>
header of incoming requests against your configured secret before processing the payload. This guide does not include the JWT verification implementation. Refer to the official Vonage documentation on ""Signed Webhooks"" for implementation details./send-sms
endpoint currently only checks ifto
andtext
exist. This is insufficient for production. You should add more robust validation:to
number against the E.164 standard using a library likelibphonenumber-js
(npm install libphonenumber-js
).text
content could potentially come from user input elsewhere in your system, sanitize it to prevent cross-site scripting (XSS) or other injection attacks, depending on how you use the text later. This guide does not include robust input validation implementation./send-sms
endpoint (either accidental or malicious), implement rate limiting. Use middleware likeexpress-rate-limit
(npm install express-rate-limit
) to restrict the number of requests a user can make in a given time window. Check Vonage's own API rate limits as well. This guide does not include rate limiting implementation.7. Troubleshooting and Caveats
Inbound URL
andStatus URL
.Error initializing Vonage SDK: Private key file not found...
: VerifyVONAGE_PRIVATE_KEY_PATH
in.env
is correct relative to where you runnode server.js
, and the file exists.VONAGE_APPLICATION_ID
is correct. Ensure theprivate.key
file content hasn't been corrupted.Logs
->API Logs
(or similar section) for errors related to webhook delivery failures from Vonage's side.node server.js
) and didn't crash. Check server logs for errors./webhooks/inbound
,/webhooks/status
) return a200 OK
or204 No Content
status quickly. Check server logs for errors within these handlers. Failure to respond quickly or with a 2xx status will cause Vonage to retry.delivered
) Not Received:delivered
status updates back to Vonage. You will usually receivesubmitted
, butdelivered
is not guaranteed.Status URL
is correctly configured and your/webhooks/status
endpoint is working and returning200 OK
.15551234567
- the SDK often handles adding the+
if needed, but being explicit is safer) forto
andfrom
numbers.8. Deployment Considerations
VONAGE_APPLICATION_ID
,VONAGE_PRIVATE_KEY_PATH
,VONAGE_NUMBER
,PORT
, etc.) securely using your hosting provider's tools (e.g., Heroku Config Vars, AWS Secrets Manager, .env files managed securely on the server). Do not include the.env
file in your deployment package/repository. You will need to securely transfer or provide theprivate.key
file to your production server and ensure theVONAGE_PRIVATE_KEY_PATH
environment variable points to its location on the server.pm2
to keep your Node.js application running reliably in production.pm2
handles automatic restarts on crashes, manages logs, enables clustering for better performance, and more.9. Verification Checklist
Before considering this production-ready:
/send-sms
endpoint?/send-sms
request in the server console?/webhooks/status
update (at leastsubmitted
) in the server console?/webhooks/inbound
message in the server console?200 OK
or204 No Content
quickly?.env
,private.key
) excluded from Git?This guide provides a solid foundation for sending, receiving, and tracking SMS messages using Node.js and Vonage. Remember to enhance logging, error handling, and especially security (webhook verification, input validation, rate limiting) based on your specific production requirements. Refer to the official Vonage Messages API documentation for further details and advanced features.