Frequently Asked Questions
Use Vonage's Messages API and webhooks. Set up a webhook endpoint in your application to receive real-time status updates like 'submitted,' 'delivered,' or 'failed' from Vonage after sending an SMS message via their API.
It's an API that lets you send and receive messages, including SMS, and track their delivery status. You integrate it into your application using the Vonage Node.js SDK or other language-specific libraries. It's more robust than 'fire and forget' methods.
Webhooks provide real-time delivery updates pushed directly to your application. This is more efficient than constantly polling the API and lets you react immediately to changes in message status, building more reliable systems.
Whenever your application needs to send SMS messages reliably and track their delivery status in real time. It's especially important for time-sensitive communications and two-factor authentication.
Use Express.js to create a server and the Vonage Node.js SDK to interact with the Messages API. You'll also need environment variables for your API credentials and webhook URLs. Ngrok helps during development.
A unique identifier for your application within the Vonage platform. It's required for using the Messages API. You create an application in the Vonage Dashboard and note its ID for configuration.
Ngrok creates a public, secure tunnel to your local development server, allowing Vonage to send webhooks to your machine during testing. This is necessary because local servers aren't publicly accessible on the internet.
It guarantees the integrity and authenticity of the webhook requests. By using a shared secret, you confirm requests genuinely originated from Vonage and haven't been tampered with. This is crucial for security.
Use the 'jsonwebtoken' library and the Vonage Signature Secret from your application dashboard. Importantly, use the raw request body, not the parsed JSON, when verifying the signature against the token from the 'Authorization' header.
Typical statuses include 'submitted' (sent to Vonage), 'delivered' (reached handset), 'failed,' 'rejected' (by the carrier), 'accepted' (intermediate carrier status), and 'undeliverable.' Not all statuses are guaranteed due to carrier limitations.
Wrap your webhook handler logic in 'try...catch' blocks to handle errors gracefully. Always acknowledge receipt with a 200 OK response, even if your internal processing fails, to prevent Vonage retries. Log errors thoroughly using a structured logger.
Check server and ngrok logs, verify the webhook URL in your Vonage application settings matches your server's URL, and check for firewall issues. Inspect the Vonage API logs for errors reported by Vonage while trying to reach your webhook.
Vonage uses the raw, unaltered request body to generate the JWT signature, not the parsed JSON. Therefore, you must capture the raw body using middleware like body-parser
before any JSON parsing occurs to ensure accurate signature verification.
Use the + sign followed by the country code and phone number without any spaces or special characters. Example: +14155550101. Validate user-provided numbers strictly to avoid errors.
Tracking the delivery status of SMS messages is crucial for applications that rely on timely communication. Knowing whether a message reached the recipient's handset, was rejected by the carrier, or failed for other reasons enables developers to build more robust and reliable systems. This guide provides a step-by-step walkthrough for building a Node.js application using Express and the Vonage Messages API to send SMS messages and receive real-time delivery status updates via webhooks.
We will build a simple Node.js server that can send an SMS message and expose webhook endpoints to receive delivery status updates from Vonage. This solves the common problem of ""fire and forget"" SMS sending, providing visibility into the message lifecycle after it leaves the Vonage platform. We'll use the Vonage Node.js SDK for seamless API interaction and ngrok for local development testing.
System architecture
Here's a high-level overview of how the components interact:
submitted
,delivered
,failed
) to the configured Status Webhook URL.(Note: A sequence diagram illustrating the component interactions was present here in the original document.)
Prerequisites
Before you begin, ensure you have the following:
1. Setting up the project
Let's create the project directory, initialize Node.js, and install the necessary dependencies.
Create Project Directory: Open your terminal or command prompt and run:
Initialize Node.js Project: This creates a
package.json
file.Install Dependencies: We need
express
for the web server,@vonage/server-sdk
to interact with the Vonage API,dotenv
to manage environment variables, andjsonwebtoken
to verify webhook signatures. We also recommendbody-parser
for reliable webhook verification.Create Project Files: Create the main application file and environment configuration files.
Configure
.gitignore
: Add sensitive files and directories to.gitignore
to prevent committing them to version control.Explanation: We ignore
node_modules
(can be reinstalled),.env
(contains secrets),private.key
(Vonage private key), and log files.Set up Environment Variables: Create a
.env.example
file to list the required variables. This serves as a template.Now, create your actual
.env
file by copying.env.example
and filling in your real credentials.Explanation:
VONAGE_API_KEY
,VONAGE_API_SECRET
: Found on the main page of your Vonage API Dashboard.VONAGE_APPLICATION_ID
: Obtained after creating a Vonage Application (next step).VONAGE_PRIVATE_KEY_PATH
: Path to the private key file downloaded when creating the application. We assume it's in the project root namedprivate.key
.VONAGE_SIGNATURE_SECRET
: Found in your Vonage Application settings under 'Webhook signature'. Used to verify incoming webhooks.VONAGE_NUMBER
: Your purchased Vonage virtual number capable of sending SMS (must be in E.164 format, e.g.,+12015550123
).PORT
: The port your local server will run on.BASE_URL
: The base URL for your webhook endpoints. We'll update this later with the ngrok URL.2. Configuring Vonage
You need a Vonage Application configured for the Messages API to handle sending and status updates.
private.key
file that downloads. Place this file in your project root directory (matchingVONAGE_PRIVATE_KEY_PATH
in.env
). The public key is stored by Vonage..env
file forVONAGE_APPLICATION_ID
.http://example.com/webhooks/inbound
http://example.com/webhooks/status
.env
file forVONAGE_SIGNATURE_SECRET
.3. Setting up the webhook server (Express)
Now, let's write the Node.js code using Express to handle incoming webhooks.
Explanation:
.env
variables..env
. Debug mode is enabled for detailed SDK logs.body-parser
middleware with averify
function is used to capture the raw request body before JSON parsing. TheverifyVonageSignature
middleware then usesjsonwebtoken
and yourVONAGE_SIGNATURE_SECRET
to verify the JWT signature from theAuthorization: Bearer <token>
header. This ensures the request genuinely originated from Vonage. This raw body approach is crucial for reliable production verification./webhooks/status
: This route handles POST requests from Vonage containing delivery status updates. It logs the received data and sends a200 OK
response. This acknowledgment is vital; otherwise, Vonage will retry sending the webhook./webhooks/inbound
: Handles incoming SMS messages (optional).sendSms
function: Encapsulates the logic for sending an SMS usingvonage.messages.send()
. It takes the recipient number (validated for strict E.164 format) and text as arguments. It logs themessageUuid
on successful submission./send-sms
endpoint: A simple POST endpoint to trigger thesendSms
function for testing purposes via cURL or Postman. Includes strict E.164 validation.PORT
.4. Running and testing locally
To receive webhooks from Vonage on your local machine, you need ngrok.
Start ngrok: Open a new terminal window and run ngrok, pointing it to the port your Node.js app is running on (default is 3000).
ngrok will display output similar to this:
Copy the
https://<random-id>.ngrok-free.app
URL. This is your public base URL.Update
.env
: Open your.env
file and set theBASE_URL
to your ngrok Forwarding URL:Update Vonage Application Webhooks: Go back to your application settings in the Vonage Dashboard. Update the Messages capability webhook URLs using your ngrok
BASE_URL
:https://<random-id>.ngrok-free.app/webhooks/inbound
https://<random-id>.ngrok-free.app/webhooks/status
Run the Node.js Application: In your original terminal window (where your project code is), start the server:
You should see output indicating the server is running and listening.
Test Sending SMS: Open another terminal or use a tool like Postman to send a POST request to your local
/send-sms
endpoint. Replace<your_test_phone>
with your actual mobile number in E.164 format (e.g.,+14155550101
).You should see:
messageUuid
.Observe Status Webhooks: Watch your Node.js application terminal. As the message progresses through the delivery lifecycle, Vonage will send POST requests to your
/webhooks/status
endpoint. You should see logs like:Common statuses include
submitted
,delivered
,rejected
,failed
,accepted
(intermediate carrier status),undeliverable
.Inspect with ngrok Web Interface: Open
http://127.0.0.1:4040
in your browser. This interface shows all requests forwarded by ngrok, allowing you to inspect the exact headers (includingAuthorization
) and payloads received from Vonage, which is invaluable for debugging.5. Error handling and logging
Production applications require more robust error handling and logging.
/webhooks/status
,/webhooks/inbound
) intry...catch
blocks to prevent the server from crashing due to unexpected errors in processing the payload. Always ensure a200 OK
is sent back to Vonage unless there's a signature verification failure (which returns401
).console.log
is used in this guide for simplicity, replace it with a structured logger like Winston or Pino in production. This allows for log levels (info, warn, error), formatting (JSON), and easier integration with log management systems.message_uuid
,status
,timestamp
, recipient/sender numbers, and any error codes/reasons.catch
block insendSms
) and webhook processing.200 OK
response within a short timeout (typically a few seconds). Ensure your processing is fast or happens asynchronously (e.g., push the payload to a queue) to avoid timeouts and duplicate processing from retries. Your endpoint must be idempotent if possible.catch
block insendSms
should inspect theerr.response.data
object from the Vonage SDK for detailed error information provided by the API (e.g., invalid number format, insufficient funds).6. Security considerations
Securing your application and webhooks is vital.
VONAGE_SIGNATURE_SECRET
and the raw request body, as implemented in theverifyVonageSignature
middleware. This is the primary mechanism to ensure the request genuinely originated from Vonage and hasn't been tampered with..env
file orprivate.key
to version control. Use.gitignore
..env
file./send-sms
endpoint publicly. The E.164 check is a good start; also consider message lengths and potentially filter content./send-sms
endpoint is exposed, implement rate limiting (e.g., usingexpress-rate-limit
) to prevent abuse.7. Deployment
Deploying this application involves moving beyond ngrok.
VONAGE_API_KEY
,VONAGE_API_SECRET
,VONAGE_APPLICATION_ID
,VONAGE_SIGNATURE_SECRET
,VONAGE_NUMBER
,PORT
,VONAGE_PRIVATE_KEY_PATH
) securely through your hosting provider's interface or secrets management. Ensure theprivate.key
file is deployed securely to the location specified byVONAGE_PRIVATE_KEY_PATH
.https://your-app-name.herokuapp.com
). Update the webhook URLs in your Vonage Application settings to use this production URL (must be HTTPS).Procfile
(Heroku) orDockerfile
(container-based deployments) to define how to start your application (node index.js
).8. Troubleshooting and Caveats
/webhooks/status
(case-sensitive, HTTPS in production).VONAGE_SIGNATURE_SECRET
in your environment variables matches the secret in the Vonage Application settings exactly (no extra spaces, etc.).bodyParser.json({ verify: ... })
is correctly capturing the raw body (req.rawBody
) before theverifyVonageSignature
middleware runs. Use the ngrok web interface (http://127.0.0.1:4040
) to inspect theAuthorization
header on incoming requests.sendSms
function. Look closely aterr.response.data
for specific Vonage error codes and descriptions.to
) is valid and strictly in E.164 format (e.g.,+14155550101
).from
) linked to the Application is SMS-capable for the destination country and correctly formatted in E.164.submitted
oraccepted
statuses usually appear quickly, butdelivered
orfailed
can take longer or sometimes not arrive at all for certain destinations/networks.message_uuid
,status
,timestamp
. Refer to the official Vonage Messages API documentation for the definitive schemas.9. Verification checklist
Before considering the implementation complete, verify the following:
express
,@vonage/server-sdk
,dotenv
,jsonwebtoken
,body-parser
)..env
file created (or environment variables set) with all necessary variables filled.index.js
contains Express server setup, Vonage SDK initialization,bodyParser
with raw body capture,verifyVonageSignature
middleware, and webhook handlers (/webhooks/status
).verifyVonageSignature
middleware is applied to webhook routes and correctly uses the raw body for verification.200 OK
upon successful processing acknowledgment.sendSms
function correctly usesvonage.messages.send
and validates theto
number format (strict E.164).node index.js
)./send-sms
endpoint (or other trigger) succeeds (check logs).submitted
,delivered
, etc.) are received and logged by the/webhooks/status
endpoint.