Frequently Asked Questions
Build a dedicated Fastify endpoint that receives POST requests containing message delivery status updates from MessageBird. This endpoint should verify the request authenticity, parse the payload, log the status, and respond with a 200 OK status.
It's a secret key provided by MessageBird, distinct from your API key, specifically for verifying the authenticity of webhook requests. It's used to generate and compare signatures, ensuring requests originate from MessageBird.
Webhooks provide real-time delivery status updates, crucial for applications needing confirmation beyond the initial API response. They push updates to your application, enabling you to react to changes immediately.
Use ngrok during development to create a public HTTPS URL for your local server, allowing MessageBird to send webhooks to your application while it's running locally.
Yes, the MessageBird Node.js SDK allows you to send test SMS messages. You'll need your Live API Key, and the code example in the article provides a guide for implementing a send endpoint in your Fastify application.
Retrieve the signature, timestamp, and raw body from the request headers and implement HMAC-SHA256 verification using your webhook signing key. The article provides code for generating the expected signature and performing a timing-safe comparison for security.
This MessageBird webhook event triggers whenever the status of a message changes, such as when it's sent, buffered, delivered, failed, or expired, allowing your application to track these updates in real time.
Implement signature verification using the provided code example to authenticate requests. Use HTTPS, input validation, and rate limiting to further secure your endpoint from misuse or abuse. A firewall based on MessageBird's IP ranges can supplement security.
Store the message ID, status, status timestamp, and recipient from the webhook payload. It's also recommended to record the time your server received the update ('receivedAt') for tracking potential delays. Use the provided schema guide for database design.
Double-check for incorrect signing keys, raw body modification before verification, or potential timestamp skew. Ensure the signed payload construction matches MessageBird's requirements precisely.
Implement idempotent processing logic. Check if the incoming status and timestamp are newer than the current status and timestamp for that message in your database before updating, preventing duplicates from affecting data integrity.
Check your firewall, ensure the webhook URL in the MessageBird dashboard is correct, verify your server is running and publicly accessible, ensure correct DNS, and make sure the webhook configuration includes the "message.updated" event type.
Rely on the 'statusDatetime' from the webhook payload, not the 'receivedAt' time on your server. Ensure your database update logic prevents overwriting newer statuses with older ones based on the correct timestamp order from MessageBird.
Respond with 200 OK immediately, then process the webhook asynchronously using a job queue for long-running tasks like database updates. Optimize database queries with appropriate indexing, and employ caching where relevant.
Implement health checks, track key metrics like received webhooks, signature verifications, processing errors, and latency, integrate error tracking, and centralize logging for analysis and alerting. Use relevant Fastify plugins and monitoring tools.
Learn how to build a robust webhook endpoint using Fastify to receive and process real-time message delivery status updates from MessageBird. This guide provides a complete walkthrough, from project setup to deployment and verification, ensuring you can reliably track the status of your sent messages.
This guide focuses specifically on handling incoming Delivery Status Reports (DLRs) via webhooks from MessageBird. We'll build a dedicated Fastify endpoint that securely receives these updates, logs them, and acknowledges receipt to MessageBird, forming a crucial part of any application that relies on message delivery confirmation.
Project Overview and Goals
What We'll Build:
A Node.js application using the Fastify framework that exposes a secure webhook endpoint. This endpoint will:
POST
requests from MessageBird containing message delivery status updates.200 OK
status code to acknowledge receipt.Problem Solved:
When sending SMS or messages via other channels through an API like MessageBird, you often need confirmation that the message was actually delivered, failed, or experienced some other status change. Relying solely on the initial API response isn't enough. MessageBird provides webhooks to push these status updates to your application in real-time, enabling you to update your internal systems, notify users, or trigger further actions based on delivery success or failure.
Technologies Used:
.env
file intoprocess.env
, keeping sensitive keys out of source code.crypto
module: Used for verifying webhook signatures.System Architecture:
Prerequisites:
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 the project, then navigate into it.
Initialize Node.js Project: This creates a
package.json
file.Install Dependencies: We need Fastify for the web server and
dotenv
for environment variables.Set Up Project Structure: Create a basic structure for clarity.
Configure
.gitignore
: Prevent sensitive files and unnecessary directories from being committed to version control. Add the following to your.gitignore
file:Configure
.env
: Add placeholders for your MessageBird credentials and server configuration. We'll obtain theMESSAGEBIRD_WEBHOOK_SIGNING_KEY
later from the MessageBird dashboard.PORT
: The port your Fastify server will listen on.HOST
: The network interface to bind to (0.0.0.0
makes it accessible from outside Docker containers or VMs if needed, defaults to127.0.0.1
otherwise).MESSAGEBIRD_WEBHOOK_SIGNING_KEY
: The secret key provided by MessageBird specifically for verifying webhook signatures. This is NOT your API Key. You must replace the placeholder value with the actual key obtained from MessageBird.Initial Fastify Server Setup (
src/server.js
): Create a basic Fastify server instance that loads environment variables.Add Start Script to
package.json
: Modify yourpackage.json
to include a convenient start script. Note thatnpm install
will add the specific dependency versions, so replace^x.x.x
with the actual installed versions if desired.Run the Server:
You should see output indicating the server is running, likely on port 3000. You can visit
http://localhost:3000
in your browser to see the{ ""hello"": ""world"" }
response. Stop the server withCtrl+C
.2. Implementing the Core Webhook Handler
Now, let's create the endpoint that will receive MessageBird's status updates.
Define the Webhook Route (
src/server.js
): MessageBird sends status updates viaPOST
request. We'll define a route,/webhooks/messagebird
, to handle these. Add the following code insidesrc/server.js
, before thestart
function definition.200 OK
response. Important: Long-running processes should be handled asynchronously (e.g., pushed to a queue) after sending the 200 OK, to avoid MessageBird timeouts and retries.Understanding the Payload: MessageBird's delivery status webhook payload typically looks something like this (structure may vary slightly based on event type):
Possible statuses include
sent
,buffered
,delivered
,failed
,expired
, etc. We are primarily interested inid
,status
,statusDatetime
, and potentiallyrecipient
orreference
to correlate the update with our original message.Processing the Payload: Let's add basic processing logic to log the key information. Update the
/webhooks/messagebird
route handler insrc/server.js
:This code now attempts to parse the
id
,status
,statusDatetime
, andrecipient
from the request body and logs them.3. Building a Complete API Layer (Optional Send Endpoint)
While the core goal is handling incoming webhooks, adding an endpoint to send a message via MessageBird helps in testing the full loop. This requires the official MessageBird Node.js SDK.
Install MessageBird SDK:
Add API Key to
.env
: You'll need your Live API Key from the MessageBird Dashboard (Developers -> API access (REST)). Add it to your.env
file.Remember to replace the placeholder with your actual key.
Implement Send Endpoint (
src/server.js
): Add a new route to trigger sending an SMS. Add this code insidesrc/server.js
, afterrequire('dotenv').config();
and before the webhook route.messagebird
SDK.recipient
andmessage
from the POST body.messagebird.messages.create
to send the SMS.originator
to a valid sender ID or Virtual Mobile Number registered in your MessageBird account. Alphanumeric IDs have restrictions.reportUrl
parameter inmessages.create
can override the default webhook URL set in the dashboard for specific messages, but we'll rely on the dashboard setting primarily.Testing the Send Endpoint: Once your server is running (restart it after adding the new code and API key), you can use
curl
or a tool like Postman:4. Integrating with MessageBird (Webhook Configuration)
This is the crucial step to connect MessageBird's status updates to your running application.
Expose Your Local Server: MessageBird needs a publicly accessible URL to send webhooks to. During development, tools like
ngrok
are perfect.ngrok
will provide a public URL (e.g.,https://<random_string>.ngrok.io
). Copy this HTTPS URL.Configure Webhook in MessageBird Dashboard:
message.updated
. This event triggers for status changes like sent, delivered, failed, etc.https://<random_string>.ngrok.io/webhooks/messagebird
POST
is selected.Update
.env
with Signing Key: Paste the Webhook Signing Key (NOT your API key) you copied from the MessageBird dashboard into your.env
file:Restart your Fastify server for the new environment variable to be loaded (
Ctrl+C
thennpm start
).5. Implementing Error Handling and Logging
We already enabled Fastify's basic logger. Let's enhance error handling, especially around webhook processing.
try...catch
in the webhook handler is a good start. Ensure critical errors (like signature failure, added later) prevent further processing and potentially return a non-200 status (though MessageBird generally prefers 200 OK for acknowledgment, logging the failure is key).info
,warn
,error
,debug
, etc.). Use them appropriately:info
: Standard operations (webhook received, status processed).warn
: Non-critical issues (payload missing optional fields, unexpected status).error
: Critical failures (signature mismatch, database errors, unhandled exceptions).2xx
response within a certain timeout (usually several seconds). Your primary goal is to respond quickly with200 OK
.async-retry
or push the job to a queue (like BullMQ, RabbitMQ) that handles retries with exponential backoff. This is beyond the scope of the basic handler but essential for production robustness.6. Creating a Database Schema and Data Layer (Conceptual)
Storing message statuses is vital. Here's a conceptual schema and how the webhook interacts with it.
Conceptual ERD:
Data Layer Interaction (Pseudocode within webhook handler): This pseudocode shows where database interaction would fit inside the webhook handler's
try
block, after signature verification (added in the next step).messageId
from the webhook payload as the primary key or foreign key to find and update your message record.status
andstatusTimestamp
.receivedAt
) to track potential delays.7. Adding Security Features
Securing your webhook endpoint is paramount.
Webhook Signature Verification (CRITICAL): This confirms the request genuinely came from MessageBird.
src/server.js
, after instantiatingfastify
.src/server.js
(e.g., before the route handlers)./webhooks/messagebird
route handler to call the verification function.verifyMessageBirdSignature
function using Node'scrypto
module.messagebird-signature
,messagebird-request-timestamp
) and therawBody
we stored earlier via the content type parser.timestamp.rawBody
).MESSAGEBIRD_WEBHOOK_SIGNING_KEY
.crypto.timingSafeEqual
to prevent timing attacks when comparing signatures.401 Unauthorized
response, stopping further processing. It also includes a check to ensure the signing key environment variable is actually configured and not left as a placeholder.Input Validation: While signature verification is key, basic checks on the payload structure (as shown in the processing step: checking for
payload.id
andpayload.status
) help prevent errors if MessageBird's payload format changes unexpectedly. For more complex validation, consider using Fastify's built-in schema validation capabilities.Rate Limiting: Protect your endpoint from abuse or misconfigured retries.
npm install @fastify/rate-limit
start
function insrc/server.js
, beforefastify.listen
:This adds basic IP-based rate limiting. Configure
max
andtimeWindow
based on expected legitimate traffic from MessageBird.HTTPS: Always use HTTPS for your webhook endpoint.
ngrok
provides this automatically for development. In production, ensure your deployment environment (e.g., behind a load balancer or reverse proxy) terminates TLS/SSL.8. Handling Special Cases
Real-world scenarios require handling edge cases.
statusTimestamp
is older than the last recorded update for that message.statusTimestamp
from the payload to determine the actual sequence of events, not the time your server received the webhook (receivedAt
). When updating your database, ensure you don't overwrite a newer status with an older one based on thestatusTimestamp
.sent
,delivered
,buffered
,failed
,expired
,delivery_failed
, etc.) appropriately in your logic. Failures might require specific alerting or cleanup actions. Consult MessageBird documentation for a full list of statuses.statusDatetime
is typically in ISO 8601 format with a UTC offset (often+00:00
). Store timestamps consistently in your database, preferably as UTC (e.g., using JavaScriptDate
objects or database timestamp types that handle time zones), and handle time zone conversions only when displaying data to users.9. Implementing Performance Optimizations
Webhook endpoints need to be fast to respond to MessageBird promptly.
200 OK
response before doing any potentially slow processing (like complex database updates, external API calls). Signature verification should be fast enough to happen before the response.200 OK
. A separate worker process consumes jobs from the queue and handles the database updates or other logic.messages
table (or equivalent) is indexed onmessageId
(or whatever field you use to look up messages based on the webhook payload) for fast lookups when processing updates.10. Adding Monitoring, Observability, and Analytics
Know what your webhook handler is doing in production.
/webhooks/messagebird
requests).fastify-metrics
can help expose Prometheus metrics.setErrorHandler
can be used to capture unhandled errors globally and report them.11. Troubleshooting and Caveats
https://
and the correct path (/webhooks/messagebird
). Verify that yourngrok
tunnel (or production URL) is active and pointing to the running Fastify application.MESSAGEBIRD_WEBHOOK_SIGNING_KEY
in your.env
file exactly matches the key generated and displayed in the MessageBird dashboard for that specific webhook URL. Ensure there are no extra spaces or characters. Remember to restart your server after updating.env
.verifyMessageBirdSignature
function accessesrequest.rawBody
. TheaddContentTypeParser
setup should preserve it correctly.signedPayload
construction inverifyMessageBirdSignature
matches MessageBird's requirement:${timestamp}.${rawBody.toString()}
.2xx
status code within their timeout period (typically a few seconds). Focus on responding quickly (Step 9) and ensuring signature verification is correct. Check MessageBird's webhook delivery logs in their dashboard for details on failures.