code examples

Sent logo
Sent TeamMay 3, 2025 / code examples / Article

Build SMS Delivery Status Tracking with Vonage, Node.js & Fastify

Complete guide to implementing real-time SMS delivery status webhooks using Vonage Messages API, Node.js, and Fastify. Learn webhook handling, security, and production deployment.

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:

  • Node.js: A JavaScript runtime environment for building server-side applications. Recommended: Node.js 14.x or later (LTS versions).
  • Fastify: A high-performance, low-overhead web framework for Node.js, chosen for its speed and developer experience.
  • Vonage Messages API: A unified API for sending and receiving messages across various channels, including SMS. Use this API for sending messages and receiving status updates.
  • @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 .env file into process.env.

System Architecture:

+-----------------+ +-----------------+ +-----------------+ +-----------------+ +--------------------+ | Your Fastify App| ---> | Vonage Messages | ---> | Carrier Network | ---> | Vonage Platform | ---> | Your Fastify App | | (Send Request) | | API (Sends SMS) | | (Delivers SMS) | | (Receives DLR) | | (Webhook Endpoint) | +-----------------+ +-----------------+ +-----------------+ +-----------------+ +--------------------+ Step 1 Step 2 Step 3 Step 4 Step 5 (Status Update)

Outcome: By the end of this guide, you will have a running Fastify application capable of:

  1. Sending an SMS message via the Vonage Messages API.
  2. Receiving POST requests from Vonage on a specific webhook endpoint (/webhooks/status).
  3. Logging the delivery status information contained in the webhook payload.

Prerequisites:

  • Node.js: Installed (LTS version recommended). Check with node -v.
  • npm or yarn: Included with Node.js. Check with npm -v or yarn -v.
  • Vonage API Account: Sign up for a free account if you don't have one. You'll get free credit to start.
  • Vonage Phone Number: Purchase an SMS-capable number from the Vonage Dashboard (Numbers -> Buy Numbers).
  • ngrok: Installed and authenticated (a free account is sufficient). Download from ngrok.com. Note: ngrok provides a temporary public URL for local development. For production, you will need a stable, permanent public URL for your server.
  • Basic Terminal/Command Line Knowledge: Creating directories, running commands.

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.

    bash
    mkdir fastify-vonage-status
    cd fastify-vonage-status
  2. Initialize Node.js Project: This creates a package.json file to manage dependencies and project metadata.

    bash
    npm init -y

    (Alternatively, use yarn init -y if you prefer yarn)

  3. Install Dependencies: We need Fastify, the Vonage SDK, and dotenv for managing environment variables. We also install @fastify/formbody as good practice, although Messages API status webhooks typically use JSON.

    bash
    npm install fastify @vonage/server-sdk dotenv @fastify/formbody

    (Alternatively, use yarn add fastify @vonage/server-sdk dotenv @fastify/formbody)

  4. Create Project Files: Create the main application file and files for environment variables and git ignore rules.

    bash
    touch index.js .env .gitignore
  5. Configure .gitignore: Prevent sensitive files and unnecessary directories from being committed to version control. Add the following to your .gitignore file:

    Code
    # Dependencies
    node_modules/
    
    # Environment Variables
    .env*
    !.env.example
    
    # Logs
    *.log
    npm-debug.log*
    yarn-debug.log*
    yarn-error.log*
    
    # Vonage Private Key (if stored directly, better to use path)
    private.key
    
    # OS generated files
    .DS_Store
    Thumbs.db
  6. Set Up Environment Variables (.env): Create a file named .env in the root of your project. This file will store your sensitive credentials and configuration. Never commit this file to Git.

    dotenv
    # .env
    
    # Vonage API Credentials (Found on Vonage Dashboard homepage)
    VONAGE_API_KEY=YOUR_VONAGE_API_KEY
    VONAGE_API_SECRET=YOUR_VONAGE_API_SECRET
    
    # Vonage Application Credentials (Generated when creating a Vonage Application)
    VONAGE_APPLICATION_ID=YOUR_VONAGE_APPLICATION_ID
    # Path to the private key file downloaded during application setup
    VONAGE_PRIVATE_KEY_PATH=./private.key # Example: /Users/youruser/.vonage/private.key
    
    # Vonage Number (Purchased from Vonage Dashboard)
    VONAGE_NUMBER=YOUR_VONAGE_VIRTUAL_NUMBER
    
    # Your Personal Phone Number (For testing SMS sending)
    # Use E.164 format (e.g., 15551234567)
    YOUR_PHONE_NUMBER=YOUR_PERSONAL_PHONE_NUMBER
    
    # Server Port
    PORT=3000
    • How to get these values:
      • 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. The private.key file will be downloaded. Important: Ideally, store this file outside your project directory for better security (e.g., in ~/.vonage/private.key) and update the VONAGE_PRIVATE_KEY_PATH in your .env file 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.

  1. Ensure Messages API is Default:

    • Navigate to your Vonage API Dashboard.
    • Go to Settings in the left-hand menu.
    • Scroll down to API keys > SMS settings.
    • Ensure the toggle is set to Messages API. If it's set to SMS API, switch it to Messages API.
    • Click Save changes. This is crucial for using the correct webhook format.
  2. Create a Vonage Application:

    • In the Vonage Dashboard, navigate to Applications -> Create a new application.
    • Enter an Application name (e.g., Fastify SMS Status App).
    • Click Generate public and private key. This will automatically download a private.key file. Save this file securely according to the recommendation in the previous section (ideally outside the project folder) and note its path for the .env file. Vonage does not store this private key.
    • Note the Application ID displayed on the page. Add it to your .env file as VONAGE_APPLICATION_ID.
    • Enable Capabilities -> Messages.
    • You will see fields for Inbound URL and Status URL. We need a public URL for these, which we'll get from ngrok in the next step. Leave them blank for now, but keep this page open or be ready to edit the application later.
  3. Expose Local Server with ngrok:

    • Open a new terminal window (keep the first one for running the app later).
    • Run ngrok to forward traffic to the port your Fastify app will run on (defined as PORT in .env, default 3000).
    bash
    ngrok http 3000
    • ngrok will display output including a Forwarding URL using https. Copy the https URL (e.g., https://random-subdomain.ngrok-free.app). This is your public URL for local testing.
  4. Configure Webhook URLs in Vonage Application:

    • Go back to your Vonage Application settings page (or find it under Applications -> Your Application Name -> Edit).
    • In the Messages capability section:
      • Paste your ngrok https URL into the Status URL field and append /webhooks/status. Example: https://random-subdomain.ngrok-free.app/webhooks/status
      • Optionally, do the same for the Inbound URL appending /webhooks/inbound (for receiving messages, not covered in detail here). Example: https://random-subdomain.ngrok-free.app/webhooks/inbound
    • Scroll down and click Generate new application or Save changes.
  5. Link Your Vonage Number:

    • On the application details page, scroll down to the Linked numbers section.
    • Find the Vonage number you purchased earlier (make sure it matches VONAGE_NUMBER in your .env).
    • Click the Link button next to the number.

Your Vonage account and application are now configured to send SMS status updates to your ngrok URL, 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.js file with the following code:

javascript
// index.js
'use strict';

// 1. Import Dependencies
require('dotenv').config(); // Load .env variables into process.env
const Fastify = require('fastify');
const formbody = require('@fastify/formbody');
const { Vonage } = require('@vonage/server-sdk');

// 2. Initialize Fastify
const fastify = Fastify({
  logger: true, // Enable built-in Pino logger (good for development)
});

// Register formbody plugin in case Vonage sends form-encoded data (though status should be JSON)
fastify.register(formbody);

// 3. Initialize Vonage Client (for sending SMS later)
// Note: Uses Application ID and Private Key for authentication with Messages API
const vonage = new Vonage({
  applicationId: process.env.VONAGE_APPLICATION_ID,
  privateKey: process.env.VONAGE_PRIVATE_KEY_PATH,
});

// --- Webhook Endpoint for Delivery Status ---

// 4. Define the Status Webhook Route
// Vonage sends POST requests to the Status URL you configured
fastify.post('/webhooks/status', async (request, reply) => {
  fastify.log.info(`Status Webhook Received. Payload:`);
  fastify.log.info(request.body); // Log the entire payload (which is the Vonage status object)

  const params = request.body;

  // Basic validation - Check for essential fields
  if (!params.message_uuid || !params.status) {
    fastify.log.warn('Received incomplete status data.');
    // Still reply 200 OK so Vonage doesn't retry an invalid request
    return reply.code(200).send({ message: 'Incomplete data received' });
  }

  // Process the status
  const messageId = params.message_uuid;
  const status = params.status;
  const timestamp = params.timestamp;
  const to = params.to?.number || params.to; // Handle potential object/string format
  const from = params.from?.number || params.from; // Handle potential object/string format

  fastify.log.info(`---> Message ID: ${messageId}`);
  fastify.log.info(`     Status: ${status}`);
  fastify.log.info(`     Timestamp: ${timestamp}`);
  fastify.log.info(`     To: ${to}`);
  fastify.log.info(`     From: ${from}`);

  if (params.error) {
    fastify.log.error(`     Error Code: ${params.error.code}`);
    fastify.log.error(`     Error Reason: ${params.error.reason}`);
  }

  // TODO: Add your business logic here
  // - Update a database record with the status
  // - Trigger notifications
  // - Add to an analytics system

  // 5. Send 200 OK Response
  // IMPORTANT: Vonage expects a 200 OK response to acknowledge receipt.
  // If it doesn't receive one, it will retry sending the webhook.
  reply.code(200).send();
});

// --- Route for Sending a Test SMS ---

// 6. Define a simple route to trigger sending an SMS
// Access this via GET http://localhost:3000/send-test-sms in your browser or curl
fastify.get('/send-test-sms', async (request, reply) => {
  const fromNumber = process.env.VONAGE_NUMBER;
  const toNumber = process.env.YOUR_PHONE_NUMBER;
  const textMessage = `Hello from Fastify and Vonage! Test at ${new Date().toLocaleTimeString()}`;

  if (!fromNumber || !toNumber) {
    fastify.log.error('VONAGE_NUMBER or YOUR_PHONE_NUMBER not set in .env');
    return reply.code(500).send({ error: 'Server configuration missing.' });
  }

  fastify.log.info(`Sending SMS from ${fromNumber} to ${toNumber}`);

  try {
    const resp = await vonage.messages.send({
      channel: 'sms',
      message_type: 'text',
      to: toNumber,
      from: fromNumber,
      text: textMessage,
    });
    fastify.log.info(`SMS Submitted: Message UUID: ${resp.message_uuid}`);
    reply.send({ success: true, message_uuid: resp.message_uuid });
  } catch (err) {
    fastify.log.error('Error sending SMS:', err);
    reply.code(500).send({ success: false, error: 'Failed to send SMS', details: err.message });
  }
});

// --- Start the Server ---

// 7. Define Server Start Function
const start = async () => {
  try {
    const port = process.env.PORT || 3000;
    // Listen on all available network interfaces, important for Docker/deployment
    await fastify.listen({ port: port, host: '0.0.0.0' });
    fastify.log.info(`Server listening on port ${port}. Waiting for webhooks at /webhooks/status`);
    fastify.log.info(`Send test SMS via: GET http://localhost:${port}/send-test-sms`);

  } catch (err) {
    fastify.log.error(err);
    process.exit(1);
  }
};

// 8. Run the Server
start();

Code Explanation:

  1. Imports: Loads necessary modules (dotenv, fastify, @fastify/formbody, @vonage/server-sdk).
  2. Fastify Init: Creates a Fastify instance with logging enabled (logger: true).
  3. Vonage Init: Initializes the Vonage SDK using the Application ID (VONAGE_APPLICATION_ID) and Private Key path (VONAGE_PRIVATE_KEY_PATH) from .env. This authentication method is standard for the Messages API.
  4. Status Webhook Route (/webhooks/status):
    • Defines a POST handler matching the URL configured in the Vonage Application.
    • Logs the entire incoming request body (request.body) for debugging. Fastify automatically parses JSON payloads. @fastify/formbody is registered to handle form-encoded data if needed, although status webhooks use JSON.
    • Performs basic validation checking for required fields (message_uuid, status).
    • Extracts key information from the payload. Note the potential .number access for to and from, as the format can vary slightly.
    • Logs the extracted details.
    • Includes a TODO placeholder where you would add your application-specific logic (database updates, etc.).
  5. 200 OK Response: Sends an empty 200 OK response back to Vonage. This is critical to prevent Vonage from retrying the webhook delivery.
  6. Test SMS Route (/send-test-sms):
    • Provides a simple GET endpoint for convenience. Accessing this URL triggers the sending of an SMS message using the credentials from .env.
    • Uses vonage.messages.send() specifying channel: 'sms', message_type: 'text', and the required to, from, and text.
    • Logs the message_uuid returned by Vonage upon successful submission.
  7. Server Start Function: Encapsulates the server start logic in an async function. Uses fastify.listen with host: '0.0.0.0' to make the server accessible externally (necessary for ngrok and deployments).
  8. Run Server: Calls the start function to launch the application.

4. Verification and Testing

Let's run the application and test the workflow.

  1. Ensure ngrok is Running: Verify that your ngrok http 3000 process is still active in its terminal window and that the https Forwarding URL matches the one configured in your Vonage Application's Status URL.

  2. Start the Fastify Server: In your primary terminal window (in the fastify-vonage-status directory), run:

    bash
    node index.js

    You should see log output indicating the server is listening on port 3000.

  3. Send a Test SMS: Open your web browser or use a tool like curl to access the test endpoint:

    bash
    # Using curl
    curl http://localhost:3000/send-test-sms
    
    # Or open in your browser:
    # http://localhost:3000/send-test-sms
    • Check the terminal running the Fastify server. You should see logs indicating the SMS was submitted and its message_uuid.
    • You should receive the SMS on the phone number specified in YOUR_PHONE_NUMBER.
  4. Observe the Status Webhook:

    • Wait a few seconds (delivery times vary).
    • Watch the terminal running the Fastify server (node index.js). You should see new log entries starting with Status Webhook Received. Payload:.
    • Examine the logged JSON payload directly following that message. This is the Vonage status object. Look for the status field (e.g., submitted, delivered, accepted). The final status is usually delivered.
    • Verify the message_uuid in the webhook payload matches the one logged when you sent the SMS.

    Example Vonage Status Payload (logged by fastify.log.info(request.body)):

    json
    {
      "message_uuid": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
      "to": { "type": "sms", "number": "15551234567" },
      "from": { "type": "sms", "number": "18885550000" },
      "timestamp": "2024-04-20T18:00:00.000Z",
      "status": "delivered",
      "usage": { "currency": "USD", "price": "0.0075" },
      "client_ref": "optional-client-ref-if-sent"
    }

    (Note: You will also see surrounding log lines from Fastify/Pino showing log level, time, etc., but the above JSON is the core request.body content)

  5. Check ngrok Interface (Optional): Open http://localhost:4040 (or the address shown in the ngrok terminal) in your browser. This web interface shows requests forwarded by ngrok. You can inspect the exact POST request sent by Vonage to /webhooks/status and the 200 OK response 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

  • Fastify Logging: We enabled Fastify's built-in Pino logger (logger: true). For production, you might configure log levels (level: 'info') and potentially transport logs to a dedicated logging service.
  • Vonage SDK Errors: The /send-test-sms route includes a try...catch block to handle errors during the sending process (e.g., invalid credentials, network issues).
  • Webhook Payload Errors: The webhook handler includes basic checks for essential fields (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.
  • Vonage Retries: Remember, Vonage will retry sending the webhook if it doesn't receive a 2xx response (like our 200 OK). Ensure your handler always returns 200 OK quickly, even if processing fails internally (log the error and return 200). Handle the processing failure asynchronously if needed.

6. Troubleshooting and Caveats

  • Webhook Not Received:
    • Verify ngrok is running and the https URL is correct in the Vonage Application Status URL setting.
    • Check your Fastify server is running and listening on the correct port (e.g., 3000).
    • Ensure no firewalls are blocking incoming connections to the port ngrok is forwarding to.
    • Check the Vonage Dashboard Logs (Logs -> Messages) for errors related to webhook delivery attempts.
  • Incorrect API / Webhook Format:
    • Crucially, double-check that Messages API is selected under Settings -> API keys -> SMS settings in the Vonage Dashboard. Using the legacy SMS API setting results in a different webhook format and configuration method (via the main Settings page, not the Application).
  • Authentication Errors (Sending):
    • Ensure VONAGE_APPLICATION_ID and VONAGE_PRIVATE_KEY_PATH in your .env are correct and the private key file exists at that path and is readable by the application.
  • Parsing Issues:
    • The Messages API status webhook should send application/json. Fastify handles this automatically. If you suspect issues, log request.headers['content-type'].
  • Delayed or Missing Receipts:
    • Delivery receipts (DLRs) depend on downstream carriers providing the information back to Vonage. Delays can occur.
    • Not all carriers or regions provide reliable DLRs. Check Vonage's country-specific feature documentation if deliverability in a specific region is critical. Status might remain submitted or accepted.
  • Rate Limiting: If your endpoint receives high traffic, consider adding rate limiting using plugins like fastify-rate-limit to prevent abuse.

7. Security Considerations

  • Webhook Security: Verifying the authenticity of Vonage webhooks requires careful consideration. Understanding the authentication mechanisms is crucial for production deployments.
    • JWT Signature Verification (Messages API): The Vonage Messages API uses JWT (JSON Web Token) signatures for webhook authentication. Each webhook request includes a JWT token that you should verify using your application's public key. While implementation details can be complex, verifying these signatures ensures requests genuinely originate from Vonage.
    • Best Practice (Application-Level Auth): Use your Vonage Application's public key (corresponding to the private key you generated) to verify webhook signatures. The @vonage/server-sdk v3.x provides helper methods for JWT verification.
    • IP Allowlisting (Additional Layer): While not a complete solution, you can add IP filtering as an additional security layer. Vonage's webhook requests originate from specific IP ranges, though these can change. Check Vonage's current documentation for the latest IP ranges if implementing this approach.
    • Obscurity (Hard-to-Guess URL): Use a long, unpredictable, randomly generated path for your webhook endpoint (e.g., /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.
    • HTTPS Only: Always use HTTPS in production. Vonage requires HTTPS endpoints for webhook URLs in production applications. This encrypts webhook payloads in transit.
    • Focus on Sending Security: Ensure your sending credentials (VONAGE_API_KEY, VONAGE_API_SECRET, VONAGE_APPLICATION_ID, VONAGE_PRIVATE_KEY_PATH) are kept highly secure.
  • Secret Management: Use .env for 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 .env files or private keys to version control.
  • Input Validation: Sanitize and validate any data extracted from the webhook payload before using it in database queries or other sensitive operations. While the risk is lower since data originates from Vonage, always validate message_uuid format, status values against expected enums, and sanitize any user-generated content that might appear in error messages.
  • Rate Limiting: Implement rate limiting on your webhook endpoints using Fastify plugins like @fastify/rate-limit to prevent abuse and protect against potential DoS attacks.

8. Deployment

  1. Obtain a Public URL: Replace ngrok with a permanent public URL for your deployed application (e.g., from Heroku, Render, AWS EC2 with a domain, etc.).
  2. Update Vonage Status URL: Go back to your Vonage Application settings and update the Status URL to your production webhook endpoint (e.g., https://your-app-domain.com/webhooks/status). Remember to use your secure, hard-to-guess path if implemented.
  3. Environment Variables: Configure your production environment variables (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 .env file or private.key directly 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.
  4. Deployment Strategy: Choose a suitable deployment method (e.g., Platform as a Service like Heroku/Render, containerization with Docker on services like AWS ECS/Fargate or Google Cloud Run, traditional VM).
  5. Process Management: Use a process manager like pm2 in production to handle Node.js application lifecycle (restarts, clustering).
  6. Logging: Configure production logging to capture output effectively (e.g., sending logs to a centralized service like Datadog, Logtail, or CloudWatch).

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:

  • Storing the status updates in a database alongside the original message details.
  • Building dashboards to visualize delivery rates.
  • Triggering alerts or alternative actions for failed messages.
  • Implementing the /webhooks/inbound endpoint to receive SMS replies.

Remember to consult the official Vonage Messages API documentation for detailed information on status codes and payload formats).

Frequently Asked Questions

How to set up Vonage SMS status webhooks with Fastify?

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.

What is the Vonage Messages API status webhook payload format?

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.

Why does Vonage retry sending status webhooks?

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.

When should I use the Vonage Messages API instead of the SMS API?

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.

How to handle Vonage webhook delivery failures in Fastify?

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.

What is the purpose of ngrok in Vonage webhook development?

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.

How to send a test SMS message using the Vonage Messages API?

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.

What is a Vonage Application ID, and where can I find it?

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.

Can I use a different port for my Fastify server with Vonage webhooks?

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.

How to secure Vonage status webhooks in production?

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.

What are common reasons for not receiving Vonage SMS status updates?

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.