Frequently Asked Questions
Set up a Vonage application, enable the Messages API, and configure the Status URL to point to your Fastify webhook endpoint (e.g., 'https://your-app.com/webhooks/status'). Use ngrok for local development to expose your server publicly. Ensure the Messages API is the default in your Vonage settings for correct webhook handling. Finally, link your Vonage number to the application to receive delivery receipts.
The Vonage Messages API status webhook sends a JSON payload to your specified endpoint. This payload includes details such as 'message_uuid', 'to', 'from', 'timestamp', 'status', 'usage', 'client_ref', and 'error' (if any). Each field provides information about the delivery status of your sent message, including success, failure reasons, and cost information.
Vonage retries sending the status webhook if it doesn't receive a 2xx HTTP response (like 200 OK) from your server within a certain timeframe. This ensures your application receives crucial delivery updates even if there are temporary network issues or server downtime. It's essential to always reply with 200 OK, even if processing the status update fails internally. Handle such failures asynchronously after acknowledging the webhook receipt.
Use the Messages API when you need unified messaging capabilities across different channels (SMS, WhatsApp, Viber, etc.) or require the latest features and status reporting. The Messages API provides a more flexible and robust platform, while the SMS API is the older, simpler version primarily for basic SMS functionalities. The tutorial specifically uses the Messages API and its associated webhooks.
Always respond with a 200 OK status to Vonage, even if your internal processing encounters errors. Log the error details for debugging, and implement asynchronous mechanisms (like queues) to retry processing the failed status updates later. This prevents Vonage from continuously retrying the webhook delivery and allows you to manage failures gracefully.
Ngrok creates a secure tunnel that exposes your locally running Fastify server to the public internet. This is necessary for Vonage to deliver webhooks to your development environment, as your local machine typically doesn't have a publicly accessible IP address or HTTPS setup.
Use the Vonage Node.js SDK (@vonage/server-sdk) with your API credentials and call vonage.messages.send(). Provide the recipient's number, your Vonage virtual number, and the message text. The example code includes a '/send-test-sms' route that demonstrates this functionality using environment variables for configuration. You can access this route to trigger a test SMS easily.
A Vonage Application ID is a unique identifier for your application within the Vonage platform. It's created when you set up an application in the Vonage API Dashboard. You'll need this ID, along with your private key, to authenticate with the Vonage APIs, including the Messages API used for sending SMS and receiving status updates. The private key is downloaded when creating the application.
Yes, you can use any available port. Update the PORT environment variable and the ngrok command accordingly (e.g., ngrok http 8080). Also, ensure your Fastify server listens on the correct port and that the Vonage Status URL is updated to reflect the new port and ngrok URL if used locally.
While Vonage doesn't offer built-in signature verification for status webhooks like it does for inbound messages, you can improve security through obscurity. Use a long, random, hard-to-guess path for your webhook endpoint (e.g., /webhooks/status/xyz123). While not foolproof, this makes accidental discovery or simple scans less likely. It's crucial to secure your API credentials and private key.
Common reasons include: ngrok not running, incorrect Status URL in the Vonage Application settings, firewalls blocking incoming connections, Vonage number not linked to the application, or using the legacy SMS API instead of the Messages API, which results in a different webhook format. Check logs for clues or consult Vonage documentation.
This guide provides a step-by-step walkthrough for building a Node.js application using the Fastify framework to receive real-time delivery status updates for SMS messages sent via the Vonage Messages API. Knowing the final delivery status is crucial for understanding message reachability and ensuring reliable communication.
You'll cover setting up the project, configuring Vonage, implementing the webhook handler in Fastify, sending a test message, and verifying the status updates. You'll also learn about essential aspects like error handling, security, and deployment.
Project Overview and Goals
Goal: Create a reliable webhook endpoint using Fastify that listens for and processes SMS delivery status updates sent by the Vonage Messages API.
Problem Solved: When you send an SMS message using an API, the initial success response only confirms that the message was accepted by the platform (Vonage) for sending. It doesn't guarantee delivery to the recipient's handset. This application solves that by providing real-time feedback from the carrier network about the message's final status (e.g., delivered, failed, rejected).
Technologies Used:
@vonage/server-sdk: The official Vonage Node.js SDK for interacting with the Vonage APIs. This guide uses version 3.x (latest: 3.25.1 as of September 2025), which provides promise-based interactions and supports both CommonJS and ES modules.ngrok(for local development): A tool to expose local servers to the public internet, necessary for Vonage webhooks to reach your development machine.dotenv: A module to load environment variables from a.envfile intoprocess.env.System Architecture:
Outcome: By the end of this guide, you will have a running Fastify application capable of:
POSTrequests from Vonage on a specific webhook endpoint (/webhooks/status).Prerequisites:
node -v.npm -voryarn -v.ngrok: Installed and authenticated (a free account is sufficient). Download from ngrok.com. Note:ngrokprovides a temporary public URL for local development. For production, you will need a stable, permanent public URL for your server.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 your project, then navigate into it.
Initialize Node.js Project: This creates a
package.jsonfile to manage dependencies and project metadata.(Alternatively, use
yarn init -yif you prefer yarn)Install Dependencies: We need Fastify, the Vonage SDK, and
dotenvfor managing environment variables. We also install@fastify/formbodyas good practice, although Messages API status webhooks typically use JSON.(Alternatively, use
yarn add fastify @vonage/server-sdk dotenv @fastify/formbody)Create Project Files: Create the main application file and files for environment variables and git ignore rules.
Configure
.gitignore: Prevent sensitive files and unnecessary directories from being committed to version control. Add the following to your.gitignorefile:Set Up Environment Variables (
.env): Create a file named.envin the root of your project. This file will store your sensitive credentials and configuration. Never commit this file to Git.VONAGE_API_KEY&VONAGE_API_SECRET: Found at the top of your Vonage API Dashboard.VONAGE_APPLICATION_ID&VONAGE_PRIVATE_KEY_PATH: Obtained in the next section when creating a Vonage Application. Theprivate.keyfile will be downloaded. Important: Ideally, store this file outside your project directory for better security (e.g., in~/.vonage/private.key) and update theVONAGE_PRIVATE_KEY_PATHin your.envfile accordingly. Storing it in the project root is possible but less secure, even with.gitignore.VONAGE_NUMBER: The virtual phone number you purchased in the Vonage Dashboard under 'Numbers'.YOUR_PHONE_NUMBER: Your actual mobile number to receive test messages.PORT: The port your Fastify server will run on locally (e.g.,3000).2. Configuring Vonage for Messages API and Webhooks
To receive delivery status updates, you need a Vonage Application configured correctly for the Messages API.
Ensure Messages API is Default:
SMS API, switch it to Messages API.Create a Vonage Application:
Fastify SMS Status App).private.keyfile. Save this file securely according to the recommendation in the previous section (ideally outside the project folder) and note its path for the.envfile. Vonage does not store this private key..envfile asVONAGE_APPLICATION_ID.ngrokin the next step. Leave them blank for now, but keep this page open or be ready to edit the application later.Expose Local Server with
ngrok:ngrokto forward traffic to the port your Fastify app will run on (defined asPORTin.env, default3000).ngrokwill display output including aForwardingURL usinghttps. Copy thehttpsURL (e.g.,https://random-subdomain.ngrok-free.app). This is your public URL for local testing.Configure Webhook URLs in Vonage Application:
ngrokhttpsURL into the Status URL field and append/webhooks/status. Example:https://random-subdomain.ngrok-free.app/webhooks/status/webhooks/inbound(for receiving messages, not covered in detail here). Example:https://random-subdomain.ngrok-free.app/webhooks/inboundLink Your Vonage Number:
VONAGE_NUMBERin your.env).Your Vonage account and application are now configured to send SMS status updates to your
ngrokURL, which forwards them to your local machine.3. Implementing the Fastify Webhook Handler
Now, let's write the Fastify code to receive and process these status updates.
Edit your
index.jsfile with the following code:Code Explanation:
dotenv,fastify,@fastify/formbody,@vonage/server-sdk).logger: true).VONAGE_APPLICATION_ID) and Private Key path (VONAGE_PRIVATE_KEY_PATH) from.env. This authentication method is standard for the Messages API./webhooks/status):POSThandler matching the URL configured in the Vonage Application.request.body) for debugging. Fastify automatically parsesJSONpayloads.@fastify/formbodyis registered to handle form-encoded data if needed, although status webhooks useJSON.message_uuid,status)..numberaccess fortoandfrom, as the format can vary slightly.TODOplaceholder where you would add your application-specific logic (database updates, etc.).200 OKresponse back to Vonage. This is critical to prevent Vonage from retrying the webhook delivery./send-test-sms):GETendpoint for convenience. Accessing this URL triggers the sending of an SMS message using the credentials from.env.vonage.messages.send()specifyingchannel: 'sms',message_type: 'text', and the requiredto,from, andtext.message_uuidreturned by Vonage upon successful submission.asyncfunction. Usesfastify.listenwithhost: '0.0.0.0'to make the server accessible externally (necessary forngrokand deployments).startfunction to launch the application.4. Verification and Testing
Let's run the application and test the workflow.
Ensure
ngrokis Running: Verify that yourngrok http 3000process is still active in its terminal window and that thehttpsForwarding URL matches the one configured in your Vonage Application's Status URL.Start the Fastify Server: In your primary terminal window (in the
fastify-vonage-statusdirectory), run:You should see log output indicating the server is listening on port
3000.Send a Test SMS: Open your web browser or use a tool like
curlto access the test endpoint:message_uuid.YOUR_PHONE_NUMBER.Observe the Status Webhook:
node index.js). You should see new log entries starting withStatus Webhook Received. Payload:.statusfield (e.g.,submitted,delivered,accepted). The final status is usuallydelivered.message_uuidin the webhook payload matches the one logged when you sent the SMS.Example Vonage Status Payload (logged by
fastify.log.info(request.body)):(Note: You will also see surrounding log lines from Fastify/Pino showing log level, time, etc., but the above JSON is the core
request.bodycontent)Check
ngrokInterface (Optional): Openhttp://localhost:4040(or the address shown in thengrokterminal) in your browser. This web interface shows requests forwarded byngrok. You can inspect the exactPOSTrequest sent by Vonage to/webhooks/statusand the200 OKresponse sent by your Fastify app.If you see the status webhook logs in your Fastify terminal, congratulations! You have successfully implemented SMS delivery status handling.
5. Error Handling and Logging
logger: true). For production, you might configure log levels (level: 'info') and potentially transport logs to a dedicated logging service./send-test-smsroute includes atry...catchblock to handle errors during the sending process (e.g., invalid credentials, network issues).message_uuid,status). Robust error handling would involve more thorough validation (e.g., using JSON Schema validation with Fastify) depending on how critical the data is.2xxresponse (like our200 OK). Ensure your handler always returns200 OKquickly, even if processing fails internally (log the error and return 200). Handle the processing failure asynchronously if needed.6. Troubleshooting and Caveats
ngrokis running and thehttpsURL is correct in the Vonage Application Status URL setting.3000).ngrokis forwarding to.SMS APIsetting results in a different webhook format and configuration method (via the main Settings page, not the Application).VONAGE_APPLICATION_IDandVONAGE_PRIVATE_KEY_PATHin your.envare correct and the private key file exists at that path and is readable by the application.application/json. Fastify handles this automatically. If you suspect issues, logrequest.headers['content-type'].submittedoraccepted.fastify-rate-limitto prevent abuse.7. Security Considerations
/webhooks/status/a7f3b9z2k1x0). While not true security (security through obscurity), it makes accidental discovery or simple scans less likely. Update this path in both your Fastify route and the Vonage Application settings.VONAGE_API_KEY,VONAGE_API_SECRET,VONAGE_APPLICATION_ID,VONAGE_PRIVATE_KEY_PATH) are kept highly secure..envfor local development only. In production, use secure secret management solutions provided by your hosting platform (e.g., AWS Secrets Manager, Google Secret Manager, Heroku Config Vars, Docker secrets). Never commit.envfiles or private keys to version control.@fastify/rate-limitto prevent abuse and protect against potential DoS attacks.8. Deployment
ngrokwith a permanent public URL for your deployed application (e.g., from Heroku, Render, AWS EC2 with a domain, etc.).https://your-app-domain.com/webhooks/status). Remember to use your secure, hard-to-guess path if implemented.VONAGE_API_KEY,VONAGE_API_SECRET,VONAGE_APPLICATION_ID,VONAGE_PRIVATE_KEY_PATH, etc.) securely using your deployment platform's tools. Do not commit your.envfile orprivate.keydirectly into your repository if it's public. Consider storing the private key content securely as an environment variable itself, or using a secure file storage mechanism accessible to your production environment.pm2in production to handle Node.js application lifecycle (restarts, clustering).Conclusion
You have now successfully built a Node.js application using Fastify to receive and log SMS delivery status updates from the Vonage Messages API. This provides crucial visibility into message deliverability, enabling you to build more robust and reliable communication features.
From here, you can extend the application by:
/webhooks/inboundendpoint to receive SMS replies.Remember to consult the official Vonage Messages API documentation for detailed information on status codes and payload formats).