Frequently Asked Questions
Set up a webhook endpoint in your NestJS application to receive DLRs. Configure your Vonage account to send POST requests to this endpoint. Use ngrok for local development to create a publicly accessible URL for your webhook.
DLRs are real-time status updates from Vonage about the delivery status of your SMS messages. They provide information about whether a message was delivered, failed, or expired, enabling better communication and troubleshooting.
NestJS provides a structured, scalable, and maintainable environment for building server-side applications. Its modular architecture, dependency injection, and TypeScript support make it ideal for handling webhook integrations.
Use ngrok during local development to expose your local server to the internet, as Vonage requires a publicly accessible URL for webhooks. For production, deploy to a server and use a permanent URL.
Create a DTO (Data Transfer Object) representing the DLR payload structure. Use `class-validator` and `class-transformer` to validate and transform incoming webhook data. Enable a global `ValidationPipe` in your NestJS application.
The Vonage SMS API is used for sending SMS messages and providing delivery receipt webhooks. You'll typically use the `@vonage/server-sdk` within your NestJS application to *send* the initial messages.
Create a `.env` file in your project's root directory. Store your `VONAGE_API_KEY`, `VONAGE_API_SECRET`, `VONAGE_NUMBER`, and other configurations in this file. Use `@nestjs/config` module to load these variables into your application.
These libraries help validate incoming webhook data against a defined DTO (Data Transfer Object) and transform the data into the DTO's format, improving type safety and preventing errors.
Logging helps track incoming requests, monitor the status of message deliveries, identify potential issues, and debug errors. Use different log levels for varying degrees of detail.
While standard DLRs lack cryptographic signature verification, you can use techniques like IP whitelisting (if available from Vonage) and secrets in the URL path to improve security, but be aware of their limitations. Use HTTPS in production and consider rate limiting.
Use unit tests to test your service logic, integration tests for combined controller-service behavior, and end-to-end tests to verify the full flow using ngrok, a real phone number, and actual SMS messages.
Use HTTPS, manage environment variables securely, update the webhook URL in Vonage settings to the permanent production URL, and implement monitoring, alerting, and scaling strategies.
Use a try-catch block to handle errors. Log all errors and return a 200 OK to Vonage to acknowledge receipt even on processing failure, unless you specifically want Vonage to retry (in which case, throw an HttpException). Consider using background job queues for heavy processing tasks.
Log the failure details, including the error code. Implement specific error handling based on your application needs, such as notifying support, attempting retries, or marking the message as permanently failed.
Use HTTPS, validate incoming DLRs with DTOs, implement IP whitelisting and rate limiting. For stronger security, consider using Vonage Messages API which supports JWT verification for webhooks, if your use-case allows it.
Track the delivery status of your SMS messages in real-time by integrating Vonage Delivery Receipts (DLRs) into your NestJS application. This guide provides a step-by-step walkthrough for setting up a robust webhook endpoint to receive and process these status updates.
Knowing whether your messages reach their destination is crucial for many applications, enabling better customer communication, troubleshooting, and analytics. This guide solves the problem of reliably capturing and handling these delivery statuses within a modern Node.js backend framework.
Technologies Used:
@vonage/server-sdk
: The official Vonage Node.js SDK. While this guide focuses on receiving DLRs (which doesn't directly use the SDK), you would typically use this SDK within your NestJS application to send the initial SMS messages that generate these DLRs.@nestjs/config
: For managing environment variables.class-validator
&class-transformer
: For validating incoming webhook data.System Architecture:
Final Outcome & Prerequisites:
By the end of this guide, you will have a NestJS application capable of:
Prerequisites:
npm install -g @nestjs/cli
).git
installed.1. Setting Up the Vonage Account and Local Environment
Before writing code, configure your Vonage account and prepare your local development environment.
A. Vonage Account Configuration:
API Key
andAPI Secret
found on the main dashboard page. You'll need these later for environment variables (primarily for sending SMS, and for basic DLR verification).Numbers
->Buy Numbers
. Search for and purchase a number with SMS capabilities in your desired country. Note this number down.Settings
in the left-hand menu.SMS settings
section.Delivery receipts (DLR) webhooks
field.ngrok
to create this public URL for your local machine. We will set upngrok
in the next step and then come back here to paste the generated URL.POST
and the type isJSON
. You can leave the URL field blank for now or enter a temporary placeholder.B. Local Development Environment Setup (ngrok):
ngrok
creates a secure tunnel from the public internet to your local development machine.3000
with the port your NestJS app will run on (NestJS default is 3000):ngrok
will display session information, including aForwarding
URL ending in.ngrok-free.app
or similar (e.g.,https://random-subdomain.ngrok-free.app
). Copy thehttps
version of this URL. This is your temporary public address.Settings
->SMS settings
.ngrok
Forwarding URL into theDelivery receipts (DLR) webhooks
field./api/webhooks/vonage/delivery-receipts
.https://random-subdomain.ngrok-free.app/api/webhooks/vonage/delivery-receipts
.POST
andJSON
are selected next to the URL field.Save changes
.2. Initializing the NestJS Project
Now, let's create the NestJS application.
src/
: Contains your application code.main.ts
: Entry point, bootstraps the application.app.module.ts
: Root module of the application.app.controller.ts
: Default example controller (we'll remove/replace this).app.service.ts
: Default example service (we'll remove/replace this)..env
: (We will create this) Stores environment variables.nest-cli.json
,package.json
,tsconfig.json
, etc.: Configuration files.Architectural Decision: We will use NestJS modules to organize features. We'll create a dedicated
WebhookModule
to handle incoming webhooks from Vonage.3. Configuring Environment Variables
Securely manage your Vonage credentials and other configurations.
Create
.env
file: In the root directory of your project, create a file named.env
.Add Variables: Add the following, replacing placeholders with your actual Vonage details:
VONAGE_API_KEY
: Your key from the Vonage dashboard.VONAGE_API_SECRET
: Your secret from the Vonage dashboard.VONAGE_NUMBER
: The Vonage virtual number you purchased.PORT
: The port the application will listen on (should match the ngrok port).Create
.env.example
: Copy.env
to.env.example
and remove the sensitive values. Commit.env.example
to git, but add.env
to your.gitignore
file to avoid committing secrets.Integrate
ConfigModule
: Modify yoursrc/app.module.ts
to load environment variables globally:Update
main.ts
to use the PORT and Global Validation: Modifysrc/main.ts
to respect thePORT
environment variable and enable global validation pipes.ValidationPipe
globally to automatically validate incoming request bodies against our DTOs (Data Transfer Objects).app.setGlobalPrefix('api')
to match the/api/...
path structure used in the webhook URL.4. Implementing the Webhook Handler
Create the module, controller, and service to receive and process the DLR webhooks.
Generate Webhook Module, Controller, and Service: Use the Nest CLI:
--flat
prevents creating a dedicated directory for each. Adjust if you prefer nested structures.webhook.module.ts
,webhook.controller.ts
, andwebhook.service.ts
in thesrc
directory (orsrc/webhook
if not using--flat
). EnsureWebhookModule
is imported inapp.module.ts
as shown previously.Define the DLR Data Structure (DTO): Create a Data Transfer Object (DTO) to represent the expected payload from Vonage and apply validation rules. Create a file
src/webhook/dto/vonage-dlr.dto.ts
:class-validator
, and improve type safety.@ApiProperty
for potential Swagger integration.Implement the Webhook Controller: Define the endpoint that matches the URL configured in Vonage (
/api/webhooks/vonage/delivery-receipts
).@Controller('webhooks/vonage')
: Sets the base path relative to the global prefix/api
.@Post('delivery-receipts')
: Defines the specific route.@Body() dlrData: VonageDlrDto
: Injects the parsed and validated request body.@HttpCode(HttpStatus.OK)
: Ensures a200 OK
status is sent back to Vonage upon successful handling before thereturn
statement is evaluated, unless an exception is thrown.Implement the Webhook Service: Contains the business logic for handling the DLR data.
ConfigService
to retrieve theVONAGE_API_KEY
.5. Error Handling and Logging
NestJS provides built-in mechanisms, but let's refine them.
ValidationPipe
automatically throws aBadRequestException
(400) if the incoming data doesn't match theVonageDlrDto
. This response is sent back to Vonage. Since validation failures usually mean the request structure is wrong, a 400 is appropriate, and Vonage typically won't retry these.try...catch
block in the controller logs internal processing errors but still returns200 OK
to Vonage. This acknowledges receipt and prevents Vonage from retrying indefinitely due to bugs in your processing logic. If you want Vonage to retry (e.g., temporary database outage), you should throw anHttpException
(e.g.,ServiceUnavailableException
- 503) from the controller or service.Logger
. For production, configure a more robust logging strategy:log
,debug
,warn
,error
).6. Security Considerations
Webhook endpoints are publicly accessible, so security is paramount.
ngrok
provides HTTPS locally. Ensure your production deployment environment (e.g., via load balancer, reverse proxy) enforces HTTPS.VonageDlrDto
and the globalValidationPipe
. This is crucial to prevent processing malformed data.WebhookService
provides minimal verification. As noted, the key is part of the payload itself./api/webhooks/vonage/dlr/SOME_RANDOM_SECRET_TOKEN
). Check this token in your controller. This is security through obscurity and less robust than signatures.@nestjs/throttler
module is excellent for this.app.module.ts
:processDeliveryReceipt
logic is efficient. Avoid long-running synchronous operations. Offload heavy or potentially slow tasks (e.g., complex database updates, third-party API calls) to background job queues (like BullMQ, RabbitMQ) managed by separate workers.7. Testing the Implementation
Thorough testing ensures reliability.
A. Unit Testing: Test the service logic in isolation.
Modify Service Test: Update
src/webhook/webhook.service.spec.ts
:beforeEach
,afterEach
) to correctly mock the logger andconsole.log
.B. Integration Testing: Test the controller and service together.
@nestjs/testing
) to create a testing module that includes theWebhookModule
.supertest
to send mock HTTP POST requests to the/api/webhooks/vonage/delivery-receipts
endpoint with various valid and invalid DLR payloads.WebhookService.processDeliveryReceipt
method to ensure it's called with the correct data.C. End-to-End (E2E) Testing:
npm run start:dev
).ngrok http 3000
(or your port) is running.ngrok
URL +/api/webhooks/vonage/delivery-receipts
.@vonage/server-sdk
) to send an SMS message from your configured Vonage number to a real test phone number you have access to.ngrok
console for incoming POST requests to your webhook path.delivered
).failed
orrejected
DLR).8. Deployment Considerations
.env
files with production secrets.ngrok
one.Conclusion
You have successfully implemented a NestJS webhook endpoint to receive and process Vonage SMS Delivery Receipts. This allows your application to track message statuses effectively. Remember to implement robust error handling, security measures (especially source verification limitations), and thorough testing before deploying to production. Further enhancements could include storing DLR data in a database, triggering notifications based on status changes, and integrating with analytics platforms.