Frequently Asked Questions
Track MessageBird SMS delivery status using webhooks. Set up a webhook endpoint in your NestJS application that receives real-time status updates from MessageBird, such as 'sent', 'delivered', or 'failed'. This allows you to monitor message delivery beyond just the initial send confirmation.
The reportUrl
parameter in the MessageBird API tells MessageBird where to send delivery status updates for a specific message. It should point to your webhook endpoint, which is typically structured as your-base-url/status-endpoint
. This directs the updates to the correct location in your application.
A UUID (Universally Unique Identifier) is crucial for correlating status updates back to the original message. It acts as a unique reference ID, allowing you to link incoming webhook data with the corresponding message you sent, ensuring accurate tracking even with asynchronous delivery.
Secure your webhook endpoint by using HTTPS and considering additional measures like IP whitelisting (restricting access to MessageBird's IP addresses) or a shared secret embedded in the reportUrl
and verified upon webhook receipt. Always sanitize incoming webhook data.
The originator is the sender ID that recipients see when they receive your SMS message. It can be a phone number or an alphanumeric string (depending on regulations and MessageBird configuration), and is set using the originator
parameter when sending messages.
ngrok is useful during development to expose your local server to the internet so MessageBird can reach your webhook endpoint. For production, use your public server's URL, as ngrok is not suitable for long-term production use cases.
Store your MessageBird API Key securely as an environment variable (e.g., MESSAGEBIRD_API_KEY
). Use @nestjs/config
to access and use this key in your NestJS application, ensuring you do not expose sensitive information directly in your code.
Use the MessageBird Node.js SDK along with NestJS to send SMS messages. Create a service that interacts with the SDK and a controller with a /send
endpoint to handle requests. Ensure to include the reportUrl
for status updates and a reference
(UUID) for tracking.
The 'accepted' status in MessageBird means that your SMS message has been accepted by MessageBird's platform for processing. It does not guarantee delivery to the recipient but indicates that MessageBird has received and will attempt to send the message. Further status updates will follow.
If webhooks aren't firing, check your ngrok setup for HTTPS URLs, ensure your CALLBACK_BASE_URL
and reportUrl
are correct, verify your application and endpoint are accessible (firewalls, etc.), and confirm your /status
endpoint is defined as a POST method and returns a 200 OK quickly.
Ensure you are correctly passing the unique reference
(UUID) when sending the SMS via the MessageBird API. This reference is essential for matching incoming webhook data to the correct outgoing message in your application.
MessageBird expects a swift 200 OK from your webhook endpoint to confirm receipt of the status update. If your endpoint takes too long to respond, MessageBird might retry, potentially leading to duplicate processing. Offload any time-consuming operations to a background queue.
A simple schema with a table to track messages and their latest status is often sufficient. Include fields for a unique ID, the MessageBird reference, recipient, body, status, timestamps, and optionally the raw webhook payload for debugging.
Make your status update processing idempotent, meaning it's safe to run multiple times with the same input without causing unintended side effects. Check the current status in the database before updating or use database constraints to prevent duplicates.
Sending an SMS successfully is just the first step. To build reliable communication workflows, debug issues, and provide accurate feedback to users or internal systems, you need to know when and if that message reaches the recipient's handset. This MessageBird NestJS guide shows you how to implement SMS delivery tracking with webhooks and callbacks to receive and process real-time status updates.
You'll build a NestJS application that sends SMS messages via MessageBird and includes a dedicated webhook endpoint to receive status updates like
sent
,delivered
, orfailed
. This guide covers MessageBird webhook configuration, sending messages with callback parameters, handling incoming status callbacks, storing delivery status updates, and production-ready security best practices.Project Overview and Goals
Goal: Create a NestJS application that sends SMS messages using the MessageBird API and reliably tracks their delivery status through webhooks.
Problem Solved: Gain visibility into SMS delivery beyond the initial API confirmation. Track whether a message was buffered by MessageBird, successfully delivered to the carrier, accepted by the recipient's handset, or failed along the way.
Technologies:
@nestjs/config
: Manages environment variables securely.ngrok
(development): Exposes your local server to the internet for webhook testing.System Architecture:
Prerequisites:
npm install -g @nestjs/cli
ngrok
or similar tunneling service for local development testingWhat You'll Build:
A production-ready NestJS application with:
1. Setting Up the Project
Initialize your NestJS project and install the necessary dependencies.
1. Create NestJS Project:
Open your terminal and run:
This creates a new NestJS project with strict TypeScript configuration and uses npm.
2. Install Dependencies:
Package Versions Note: The
messagebird
package (v4.0.1 as of 2024) is the official Node.js SDK. For TypeORM integration, ensure@nestjs/typeorm
version 10.x or later is used withtypeorm
0.3.x for compatibility.3. Environment Variables Setup:
Use environment variables for sensitive information like API keys. Configure
@nestjs/config
to manage these securely.Create a
.env
file in the project root:Important: Replace
YOUR_LIVE_API_KEY
,YOUR_MESSAGEBIRD_NUMBER
, andYOUR_NGROK_OR_PUBLIC_URL
with your actual values. Obtain the API key from your MessageBird Dashboard (Developers -> API Access -> Live Key). Purchase a number under the "Numbers" section if you haven't already. TheCALLBACK_BASE_URL
will be provided byngrok
later.Add
.env
to your.gitignore
file to prevent accidentally committing secrets.4. Configure ConfigModule and TypeOrmModule (Optional):
Import and configure
ConfigModule
in your main application module (src/app.module.ts
). If using a database, configureTypeOrmModule
.5. Project Structure:
The initial structure created by
nest new
is suitable. We will add a dedicatedmessaging
module to handle all MessageBird interactions.2. Implementing Core Functionality (Messaging Module)
We'll create a module responsible for sending messages and handling status callbacks.
1. Generate Module, Service, Controller:
This creates
src/messaging/messaging.module.ts
,src/messaging/messaging.service.ts
, andsrc/messaging/messaging.controller.ts
.2. Implement
MessagingService
:This service will contain the logic for interacting with the MessageBird SDK.
Key Points:
onModuleInit
): Initializes the SDK usingConfigService
.uuidv4
): Critical for correlating status updates. Generated for each message.reportUrl
Parameter: Tells MessageBird where to POST status updates for this specific message.async/await
usage.processStatusUpdate
: Handles incoming webhook data. Extracts key fields, logs them, and includes placeholder logic for database updates. Normalization of the recipient number is noted as a potential requirement.3. Implement
MessagingController
:Defines API endpoints for sending messages and receiving status updates.
Key Points:
/send
Endpoint: Accepts POST requests, validates input usingSendMessageDto
, calls the service, returns202 Accepted
with thereference
./status
Endpoint: Accepts POST requests from MessageBird. It passes data to the service and must return200 OK
quickly. Slow processing should be handled asynchronously (background job queue recommended)./send
request body adheres toSendMessageDto
.4. Create DTO (Data Transfer Object):
Defines the expected request body for the
/send
endpoint.3. Building a Complete API Layer
Our core API endpoints (
/messaging/send
and/messaging/status
) are defined. Let's refine them.Authentication/Authorization:
reportUrl
and verify it.reference
for correlation. Rigorously sanitize input. If available and feasible, implement signature verification or IP whitelisting.Request Validation:
Already implemented for
/send
usingclass-validator
.API Documentation:
Consider using
@nestjs/swagger
to generate OpenAPI documentation for the/send
endpoint.Testing Endpoints:
/send Endpoint:
/status Endpoint: Test by sending a real message and observing logs/DB, or simulate a callback:
4. Integrating with MessageBird
Configuration:
.env
(MESSAGEBIRD_API_KEY
)..env
(MESSAGEBIRD_ORIGINATOR
, E.164 format).reportUrl
):reference
defined when sending AND a status report URL set viareportUrl
(per-message) or configured globally in your account settings.ngrok http 3000
. Copy HTTPS URL ->.env
(CALLBACK_BASE_URL
). Full URL is${CALLBACK_BASE_URL}/messaging/status
.https://api.yourdomain.com
) asCALLBACK_BASE_URL
.reportUrl
per message (as implemented) offers more control and flexibility.Environment Variables Summary:
MESSAGEBIRD_API_KEY
: Authenticates API requests.MESSAGEBIRD_ORIGINATOR
: Sender ID for outgoing SMS.CALLBACK_BASE_URL
: Public base URL for constructing thereportUrl
.Fallback Mechanisms:
sendMessage
call in case of transient MessageBird API errors./status
endpoint fails to return2xx
quickly.5. Error Handling, Logging, and Retry Mechanisms
Error Handling Strategy:
sendMessage
. Log details. Return appropriate HTTP errors from/send
. Optionally update DB status to indicate sending failure.try...catch
inprocessStatusUpdate
. Log internal errors (DB issues, etc.) but always return200 OK
to MessageBird. Handle the failure internally (log, dead-letter queue).ValidationPipe
for/send
(returns 400).Logging:
Logger
.log
,warn
,error
).Retry Mechanisms:
/status
fast and reliable. MessageBird handles retries if needed. Use a background queue for complex processing.6. Creating a Database Schema and Data Layer (Optional)
Persistence is needed for tracking. Here's a simplified TypeORM/PostgreSQL example.
Simplified Schema:
For the scope of this guide, a single entity to track the message and its latest status is often sufficient.
2. TypeORM Entity:
3. Integrate with Service:
Inject the
Message
repository (@InjectRepository(Message)
) intoMessagingService
constructor. Uncomment and adapt the database interaction logic withinsendMessage
andprocessStatusUpdate
as shown in the service code comments (Section 2).4. Migrations:
Strongly recommended for production. Avoid
synchronize: true
. Use TypeORM migrations.(Note: Setting up TypeORM CLI and DataSource is beyond this guide's scope, refer to TypeORM docs)
7. Adding Security Features
/send
(DTO +ValidationPipe
). Sanitize webhook data before use./send
(API Key/JWT Guard).HTTPS Required: Always use HTTPS for webhook endpoints in production.
Webhook Signature Verification (Recommended): MessageBird supports HMAC-SHA256 signature verification for webhooks. When configuring your webhook with a
signingKey
, MessageBird includesMessageBird-Signature
andMessageBird-Request-Timestamp
headers in webhook requests. Verify these signatures to ensure authenticity:Shared Secret: As an alternative, add a secret query parameter to
reportUrl
and verify it.IP Whitelisting: Allow only MessageBird's webhook IPs (requires infrastructure setup).
Reference Validation: Always verify that incoming webhook
reference
IDs exist in your system before processing.8. Handling Special Cases
Status Meanings: MessageBird provides three complementary levels of status information that should be analyzed together:
scheduled
,sent
,buffered
,delivered
,expired
,delivery_failed
)statusReason
in webhook payload)statusErrorCode
in webhook payload)Common status values and their meanings:
accepted
: Message accepted by MessageBird APIsent
: Message sent to carrier networkbuffered
: Temporarily held (usually due to carrier issues)delivered
: Successfully delivered to recipient's deviceexpired
: Message expired before delivery (check validity period)delivery_failed
: Delivery failed (checkstatusReason
andstatusErrorCode
for details)Time Zones: MessageBird usually provides UTC timestamps. Store in DB using
timestamptz
(Postgres) or equivalent. Handle time zone conversions carefully.Duplicate Webhooks: Design
processStatusUpdate
to be idempotent (safe to run multiple times with the same input). Check current status before updating, or use DB constraints.Missing References: Log and monitor webhooks arriving without a
reference
. This signals an issue.9. Implementing Performance Optimizations
/status
returns200 OK
quickly. Offload slow tasks (DB writes, external calls) to a background queue (BullMQ, RabbitMQ).reference
,status
,messageBirdId
as shown in the entity.async/await
correctly, avoid blocking the event loop./send
and simulated/status
endpoints under load (k6, artillery).10. Adding Monitoring, Observability, and Analytics
@nestjs/terminus
for a/health
endpoint (check DB connection, etc.)./send
,/status
), status distribution, queue lengths (Prometheus, Datadog).@sentry/node
) or similar for detailed error reporting.11. Troubleshooting and Caveats
ngrok
(HTTPS),CALLBACK_BASE_URL
,reportUrl
in API call, app accessibility, firewalls,/status
endpoint definition (POST), MessageBird dashboard logs, quick200 OK
response.reference
is passed correctly in API call. Check raw webhook payload.reference
exists.