Frequently Asked Questions
Use MessageBird's webhooks and a RedwoodJS function to capture real-time delivery updates. Set up a webhook endpoint in your Redwood app and configure it in your MessageBird dashboard. The webhook will send an HTTP POST request to your app whenever a message status changes, allowing you to update its status in your database.
Prisma acts as an ORM (Object-Relational Mapper), simplifying database interactions. It allows you to define your database schema (like the Message model), manage migrations, and access data in a type-safe way using Prisma Client. This streamlines database operations within your RedwoodJS application.
Serverless functions are ideal for handling asynchronous events like webhooks. They are cost-effective because you only pay for the compute time used when a webhook is received. RedwoodJS integrates seamlessly with serverless providers, making webhook implementation straightforward.
The APP_BASE_URL
is critical for webhook functionality and must be updated whenever your application's public URL changes. This includes using tools like ngrok for local development or deploying to a production environment. Ensure this variable accurately reflects the reachable address of your application.
Yes, you can use Alphanumeric Sender IDs, but be mindful of country-specific restrictions. Instead of a phone number, you can use an approved alphanumeric string (e.g., your brand name) as the sender. Consult MessageBird's documentation for supported countries and regulations.
RedwoodJS utilizes a .env
file at the root of your project. In this file, you'll store sensitive information like your MESSAGEBIRD_API_KEY
, MESSAGEBIRD_SIGNING_KEY
, DATABASE_URL
, and APP_BASE_URL
. Remember to replace placeholders with your actual credentials and keep this file secure.
The correct path is /.redwood/functions/messagebirdWebhook
. This corresponds to the Redwood function you create to handle incoming webhook requests from MessageBird. Make sure this path matches exactly in your service function and webhook handler.
The MessageBird Node.js SDK provides the WebhookSignatureJwt
class for signature verification. You'll initialize it with your MESSAGEBIRD_SIGNING_KEY
and use it to verify the signature in the messagebirdWebhook
function. This ensures that incoming webhook requests genuinely originate from MessageBird.
Setting bodyParser: false
in the messagebirdWebhook
function's config
object prevents RedwoodJS from automatically parsing the incoming request body as JSON. This is crucial because MessageBird's signature verification requires the raw, unparsed request body.
Your updateMessageStatusFromWebhook
service function will receive the status
from the MessageBird webhook payload. Use this value to update the corresponding message record in your database. Common statuses include 'sent', 'delivered', 'delivery_failed', and 'expired'.
The 'queued' status indicates that the message has been created in your database and is awaiting delivery via MessageBird. It's the initial state before the message is actually sent to the MessageBird API. The status will change to 'sent' once MessageBird accepts the message for delivery.
Implement error handling within the sendMessage
service function to catch potential issues during the MessageBird API call. Update the message status in the database accordingly (e.g., 'failed_to_send') and log the error for debugging. You might also want to notify users or retry the send operation based on the error type.
The messageBirdId
is essential for correlating webhook updates with existing messages in your database. If the payload is missing this ID, you cannot process the update. Log a warning and return an appropriate response to MessageBird (e.g., a 400 Bad Request).
The index on the status
field in your Prisma schema improves the performance of queries that filter by message status. This optimization is beneficial when you need to quickly retrieve messages with specific statuses, such as 'delivered' or 'failed'.
This comprehensive guide shows you how to build a production-ready SMS delivery tracking system using MessageBird webhooks in RedwoodJS. You'll learn how to send SMS messages through the MessageBird API, implement secure webhook signature verification, and track real-time delivery status updates in your Node.js application. We'll cover everything from project setup and database schema design to webhook security, error handling, and GraphQL API integration.
By the end of this MessageBird webhook integration tutorial, you will have a RedwoodJS application capable of:
This solution enables applications to reliably inform users or internal systems about the delivery success or failure of critical SMS communications.
MessageBird SMS Delivery Tracking: Project Overview and Goals
Problem: Many applications send SMS notifications for order confirmations, two-factor authentication (2FA), and alerts, but lack visibility into actual delivery status. Relying on the initial MessageBird API
sent
confirmation is insufficient—network issues, invalid phone numbers, or carrier blocks can prevent delivery without your knowledge.Solution: This guide shows you how to implement MessageBird's webhook delivery status callbacks in RedwoodJS. MessageBird sends HTTP POST requests to your webhook endpoint whenever message status changes—from
sent
todelivered
,delivery_failed
, orexpired
. Your application will securely verify webhook signatures using JWT, then update delivery status in real-time in your PostgreSQL database via Prisma ORM.<!-- EXPAND: Could benefit from a real-world use case comparison table showing when status tracking is critical vs optional (Type: Enhancement) -->
Technologies Used:
<!-- GAP: Missing version compatibility matrix for technology stack (Type: Substantive) -->
System Architecture:
<!-- DEPTH: Architecture diagram lacks explanation of error handling flows and retry mechanisms (Priority: High) -->
Prerequisites:
npm install -g redwoodjs
ngrok
or a similar tool to expose your local webhook endpoint to the internet for MessageBird callbacks.<!-- GAP: Missing step-by-step instructions for obtaining MessageBird credentials from dashboard (Type: Critical) --> <!-- GAP: Missing PostgreSQL setup instructions and recommended configuration (Type: Substantive) -->
1. Setting up Your RedwoodJS Project for MessageBird Integration
Let's create a new RedwoodJS application and install the MessageBird Node.js SDK for SMS API integration.
Create RedwoodJS App: Open your terminal and run:
This command scaffolds a new RedwoodJS project named
messagebird-redwood-status
using TypeScript.Navigate to Project Directory:
Install Dependencies: We need the MessageBird Node.js SDK and
dotenv
for managing environment variables on the API side.<!-- DEPTH: Missing explanation of why dotenv is needed when RedwoodJS has built-in env support (Priority: Medium) -->
Configure Environment Variables: RedwoodJS uses a
.env
file at the project root for environment variables. Create this file (touch .env
) and add the following_ replacing placeholders with your actual credentials:DATABASE_URL
: Points to your PostgreSQL instance. Ensure the database exists.DATABASE_URL
uses a weak password (password
). Always use strong, unique passwords for your databases, even during development. Consider using a password manager.MESSAGEBIRD_API_KEY
: Your live API key from the MessageBird dashboard (Developers -> API access). Treat this like a password.MESSAGEBIRD_SIGNING_KEY
: Found below your API keys in the MessageBird dashboard. Used to verify webhook authenticity. Treat this securely.MESSAGEBIRD_ORIGINATOR
: The 'From' number or name for outgoing SMS. Must be a number purchased/verified in MessageBird or an approved Alphanumeric Sender ID (check MessageBird documentation for country support).APP_BASE_URL
: Crucial for constructing thestatusReportUrl
sent to MessageBird and the URL you configure in the MessageBird dashboard. Remember to update this when usingngrok
or deploying.<!-- GAP: Missing instructions for setting up ngrok and obtaining the ngrok URL for local development (Type: Critical) --> <!-- EXPAND: Could benefit from a table showing different APP_BASE_URL values for different environments (Type: Enhancement) -->
Initialize MessageBird Client: Create a reusable MessageBird client instance. Create the file
api/src/lib/messagebird.ts
:.env
.<!-- DEPTH: Missing guidance on testing the MessageBird connection and verifying API key validity (Priority: High) -->
2. Implementing SMS Delivery Status Tracking (Database Schema & Services)
To track SMS delivery status, we need a database schema to store message details and real-time status updates from MessageBird webhooks.
Define Database Schema: Open
api/db/schema.prisma
and define aMessage
model:id
: Standard CUID primary key.messageBirdId
: Stores the ID returned by MessageBird when a message is created. This is crucial for correlating webhook updates. It's nullable initially and marked unique.recipient
,body
: Basic message details.status
: Tracks the delivery status. Defaults topending
.status
Type: UsingString
provides flexibility if MessageBird introduces new status values not defined in your code. However, using a Prismaenum
(e.g.,enum MessageStatus { PENDING, QUEUED, SENT, DELIVERED, FAILED, EXPIRED }
) offers better type safety and clearly defines allowed states within your application code. Choose based on whether you prioritize flexibility or strict type checking.statusDetails
: Can store additional error information from failed delivery webhooks.statusUpdatedAt
: Timestamp from the webhook event.createdAt
,updatedAt
: Standard Prisma timestamps.relatedRecordId
/relatedRecord
: Shows how you might link this message back to another entity in your app (like anOrder
orUser
).@@index([status])
: Added an index to thestatus
field for potentially faster queries filtering by status.<!-- EXPAND: Could benefit from a table comparing String vs Enum for status field with pros/cons (Type: Enhancement) --> <!-- GAP: Missing guidance on additional indexes for common query patterns (e.g., by recipient, by date) (Type: Substantive) -->
Apply Database Migrations: Create and apply the migration to your database:
Follow the prompts. This creates the
Message
table in your PostgreSQL database and applies the index.<!-- DEPTH: Missing explanation of what to do if migration fails or database doesn't exist (Priority: High) -->
Generate GraphQL SDL and Services: Use Redwood's generators to create the boilerplate for GraphQL types, queries, mutations, and the service file for the
Message
model:This command creates:
api/src/graphql/messages.sdl.ts
: Defines GraphQL types (Message
), queries (messages
,message
), and mutations (createMessage
,updateMessage
,deleteMessage
).api/src/services/messages/messages.ts
: Contains Prisma logic for the generated queries/mutations.api/src/services/messages/messages.scenarios.ts
: For defining seed data for tests.api/src/services/messages/messages.test.ts
: Basic test structure.Implement Message Sending Service: Modify the generated
api/src/services/messages/messages.ts
to handle sending SMS via MessageBird and integrate the webhook logic. We'll replace the defaultcreateMessage
with a customsendMessage
mutation and add a function to handle webhook updates.sendMessage
:recipient
andbody
as input.statusReportUrl
usingAPP_BASE_URL
and the exact path to the Redwood function we'll create (/.redwood/functions/messagebirdWebhook
).Message
record in the DB with statusqueued
.messagebird.messages.create
, passing thestatusReportUrl
.Promise
wrapper around the callback-based SDK method for better async/await flow.messageBirdId
from the response and sets status tosent
.config_error
,failed_to_send
,send_issue
).updateMessageStatusFromWebhook
:MessageBirdWebhookPayload
(which is now exported).Message
record using the uniquemessageBirdId
.status
,statusDetails
, andstatusUpdatedAt
fields.P2025
(Record Not Found) Prisma error as a non-critical warning. Returns a boolean indicating success/failure.createMessage
is removed/commented out with warnings.updateMessage
anddeleteMessage
are kept but include warnings and TODOs for adding authorization, as direct manipulation might bypass business logic or security checks.<!-- GAP: Missing E.164 phone number validation implementation details and library recommendations (Type: Critical) --> <!-- DEPTH: Code lacks comprehensive error code mapping from MessageBird API errors (Priority: Medium) --> <!-- EXPAND: Could benefit from a flowchart showing the complete message lifecycle with all status transitions (Type: Enhancement) -->
3. Building the GraphQL API for SMS Operations
RedwoodJS generates GraphQL schema boilerplate. We'll customize it to expose a
sendMessage
mutation for sending SMS with delivery tracking enabled.Update GraphQL Schema Definition: Modify
api/src/graphql/messages.sdl.ts
:SendMessageInput
type for our custom mutation.createMessage
mutation withsendMessage(input: SendMessageInput!): Message!
.Message
type, queries (messages
,message
), and other mutations (updateMessage
,deleteMessage
).@requireAuth
is a basic check, might need role-based access) if the standardupdate/delete
mutations are exposed.scalar DateTime
definition is present (often included by default).<!-- GAP: Missing explanation of @requireAuth directive and how to implement custom authorization rules (Type: Substantive) --> <!-- EXPAND: Could benefit from pagination examples for the messages query (Type: Enhancement) -->
Testing the Mutation (Conceptual): You can use the Redwood GraphQL Playground (usually available at
http://localhost:8911/graphql
when runningyarn rw dev
) to test thesendMessage
mutation.Example GraphQL Mutation:
sendMessage
service function, create a DB record, call the MessageBird API, update the record with themessageBirdId
andsent
status, and return the result.<!-- DEPTH: Missing detailed instructions on accessing and using the GraphQL Playground (Priority: High) --> <!-- GAP: Missing example queries for checking message status and filtering by status (Type: Substantive) -->
4. Implementing MessageBird Webhook Handler for Delivery Status Callbacks
The webhook handler is essential for receiving real-time SMS delivery status updates from MessageBird. We'll create a dedicated RedwoodJS serverless function to handle incoming POST requests, verify webhook signatures, and update message status in the database.
Create Redwood Function: Use the Redwood generator:
This creates
api/src/functions/messagebirdWebhook.ts
.Implement Webhook Handler and Signature Verification: Edit
api/src/functions/messagebirdWebhook.ts
to verify the signature and process the payload:WebhookSignatureJwt
from the MessageBird SDK.MESSAGEBIRD_SIGNING_KEY
from.env
.export const config = { api: { bodyParser: false } };
to ensureevent.body
contains the raw request string needed for verification.verifier
outside the handler.POST
method and the presence of theMessageBird-Signature-JWT
header.requestUrl
usingAPP_BASE_URL
andevent.path
. This is a critical step and needs careful testing in deployment.verifier.verify
with the signature, reconstructed URL, query parameters (event.queryStringParameters
), and the raw body (event.body
).401 Unauthorized
if verification fails.rawBody
into a JSON object only after successful signature verification.payload
to ensure required fields exist.400 Bad Request
if parsing or validation fails.updateMessageStatusFromWebhook
service function with the validatedpayload
.200 OK
if the service function indicates success (returnstrue
).500 Internal Server Error
if the service function indicates failure (returnsfalse
) or if an unexpected error occurs, potentially prompting MessageBird to retry.<!-- GAP: Missing instructions for configuring the webhook URL in MessageBird dashboard (Type: Critical) --> <!-- DEPTH: Webhook handler lacks discussion of retry behavior and exponential backoff from MessageBird (Priority: High) --> <!-- EXPAND: Could benefit from a table showing all possible HTTP status codes and their meanings in webhook context (Type: Enhancement) --> <!-- GAP: Missing guidance on testing webhook handler locally with sample payloads (Type: Substantive) -->
Related Resources
Looking to expand your MessageBird SMS integration? Check out these related guides:
MessageBird Webhook Integration FAQ
How do I verify MessageBird webhook signatures in RedwoodJS?
Use the
WebhookSignatureJwt
class from the MessageBird SDK. Import it withimport { WebhookSignatureJwt } from 'messagebird/lib/webhook-signature-jwt'
, then instantiate it with your signing key and callverifier.verify()
with the signature header, request URL, query parameters, and raw body. You must disable RedwoodJS's automatic body parser by exportingconfig = { api: { bodyParser: false } }
in your function file to access the raw request body required for verification.What is the correct way to initialize the MessageBird SDK in Node.js?
Use
initClient()
from the MessageBird SDK:import { initClient } from 'messagebird'; const messagebird = initClient('YOUR_API_KEY');
. The SDK uses a callback-based API, so wrap methods in Promises for async/await support:await new Promise((resolve, reject) => { messagebird.messages.create(params, (err, resp) => err ? reject(err) : resolve(resp)); })
.How does MessageBird's statusReportUrl parameter work?
The
statusReportUrl
parameter tells MessageBird where to send HTTP POST requests when your message's delivery status changes. Include it when creating a message:messagebird.messages.create({ originator, recipients, body, statusReportUrl })
. MessageBird will send webhooks to this URL with status updates likedelivered
,delivery_failed
, orexpired
.Why do I need to set bodyParser: false in RedwoodJS functions?
MessageBird's JWT signature verification requires the raw, unparsed request body to validate the signature. RedwoodJS normally parses JSON bodies automatically, which modifies the body string and breaks signature verification. Setting
export const config = { api: { bodyParser: false } }
in your function file prevents automatic parsing, letting you accessevent.body
as the original raw string needed for verification.What MessageBird message statuses should I handle in my webhook?
Handle these key statuses:
sent
(message left MessageBird's platform),buffered
(queued for delivery),delivered
(successfully received by handset),delivery_failed
(permanent failure), andexpired
(validity period exceeded). The webhook payload includesstatus
,statusReason
, and optionallystatusErrorCode
for detailed failure information.<!-- GAP: Missing detailed explanation of each status code and when it occurs in the message lifecycle (Type: Substantive) -->
How do I test MessageBird webhooks locally with RedwoodJS?
Use RedwoodJS's
mockSignedWebhook
testing utility to simulate webhooks without external tools:const event = mockSignedWebhook({ payload, signatureType: 'sha256Verifier', signatureHeader: 'MessageBird-Signature-JWT', secret: 'YOUR_SIGNING_KEY' })
. This creates a properly signed test event you can pass to your handler in unit tests. For live testing, use ngrok to expose your local server and configure the ngrok URL in MessageBird's dashboard.<!-- DEPTH: Testing section lacks complete test examples and unit test structure (Priority: High) -->
What happens if MessageBird sends duplicate webhook deliveries?
MessageBird may retry webhooks up to 10 times if it doesn't receive a 200 OK response. Implement idempotency by using MessageBird's unique message ID (
messageBirdId
) as your database lookup key. Prisma'supdate
with awhere
clause on the uniquemessageBirdId
field ensures duplicate webhooks safely update the same record without creating duplicates.How do I configure the webhook URL in MessageBird's dashboard?
MessageBird doesn't provide a programmatic API for webhook URL configuration. You must configure it manually through the Flow Builder dashboard. For status reports, pass the
statusReportUrl
parameter when creating each message. For inbound messages, configure the webhook URL in Flow Builder using the "Forward to URL" step.<!-- GAP: Missing step-by-step screenshots or detailed navigation instructions for MessageBird dashboard configuration (Type: Critical) -->
What security measures should I implement for MessageBird webhooks?
Always verify the
MessageBird-Signature-JWT
header using your signing key before processing payloads. Return 401 Unauthorized for invalid signatures. Validate the webhook payload structure before database operations. Consider implementing rate limiting to prevent abuse. Store your signing key securely in environment variables, never in code. Use HTTPS in production to prevent man-in-the-middle attacks.<!-- EXPAND: Could benefit from a security checklist and rate limiting implementation example (Type: Enhancement) -->
How do I handle webhook failures and retries in RedwoodJS?
Return appropriate HTTP status codes: 200 OK when successfully processed (stops retries), 401 Unauthorized for signature failures (stops retries), and 500 Internal Server Error for temporary issues (triggers MessageBird retries). Log all webhook attempts with
logger.info()
for debugging. MessageBird will retry failed webhooks up to 10 times with exponential backoff, so design your handler to be idempotent.<!-- GAP: Missing details on MessageBird's retry schedule and exponential backoff timings (Type: Substantive) --> <!-- GAP: Missing production deployment considerations (environment-specific configurations, monitoring, logging best practices) (Type: Critical) --> <!-- EXPAND: Could benefit from a troubleshooting section with common issues and solutions (Type: Enhancement) -->