Frequently Asked Questions
You can send WhatsApp messages programmatically using Node.js, Express.js, and the MessageBird Conversations API. The provided Node.js code demonstrates how to create an API endpoint that sends WhatsApp messages via the MessageBird SDK. This enables automated notifications, customer support, and conversational commerce directly within your app.
The MessageBird Conversations API allows developers to integrate WhatsApp messaging into their applications. It provides a way to send and receive WhatsApp messages, manage conversations, and track message status. This API is used with the MessageBird Node.js SDK for seamless integration with a Node.js backend.
Ngrok creates a public tunnel to your local server, making it accessible from the internet. This is crucial for local development as it allows MessageBird to send webhooks to your application while testing. For production deployments, you'll need a stable, public HTTPS URL.
WhatsApp Message Templates (HSMs) are required to initiate conversations with users outside the 24-hour customer service window. You must submit and have these templates pre-approved by MessageBird through their dashboard before using them in your integration.
Configure your webhook URL (ngrok for development, public HTTPS for production) in your MessageBird Dashboard, under Channels > WhatsApp > Your Channel. Set the event triggers to "message.created" and "message.updated" and provide a strong signing key that is the same value as your MESSAGEBIRD_WEBHOOK_SIGNING_SECRET variable.
The MessageBird Channel ID is a unique identifier for your WhatsApp Business channel. This ID is essential for sending WhatsApp messages and is used within the API requests and should be stored as the MESSAGEBIRD_WHATSAPP_CHANNEL_ID variable.
Yes, you can receive WhatsApp messages by setting up a webhook endpoint in your Node.js application. The MessageBird Conversations API will send an HTTP POST request to your specified URL every time a new message is received on your WhatsApp Business number.
Implement webhook signature verification using the `messagebird-signature` header to ensure the request originates from MessageBird and is not a fraudulent request. The provided Node.js example demonstrates this crucial security practice.
The article utilizes Express.js to create the web server and API endpoints, the MessageBird Node.js SDK for interactions with the MessageBird API, dotenv to load environment variables from a .env file, and ngrok for webhook testing during local development.
Verify the signature by calculating a HMAC-SHA256 hash of the combined timestamp, your signing secret, and the raw request body, then compare this hash with the signature provided in the request header. The webhook handling logic shown in the Node.js example provides a detailed implementation of this verification process.
Integrate WhatsApp messaging by installing the necessary libraries (Express, MessageBird SDK, dotenv) and implementing the API endpoint and webhook handler logic as described in the article. You will need to configure your MessageBird account, obtain your API key and Channel ID, and set up a public URL for the webhook.
Set `MESSAGEBIRD_API_KEY`, `MESSAGEBIRD_WHATSAPP_CHANNEL_ID`, `MESSAGEBIRD_WEBHOOK_SIGNING_SECRET`, and `PORT` as environment variables. Store these in a `.env` file and load them with `dotenv`.
Use the "message.updated" webhook event, which MessageBird sends when a message status changes (e.g., delivered, read). Update your database with the new status by matching it to the unique message identifier.
The E.164 format (+[country code][number]) ensures consistent and internationally recognized phone number representation for WhatsApp. This is crucial to deliver messages correctly.
This guide provides a step-by-step walkthrough for integrating WhatsApp messaging capabilities into your Node.js application using Express and the MessageBird Conversations API. We'll cover everything from initial project setup to sending/receiving messages, handling webhooks securely, managing errors, and preparing for deployment.
By the end of this guide, you will have a functional Node.js service capable of:
This integration solves the problem of programmatically interacting with customers on WhatsApp, enabling automated notifications, customer support interactions, or conversational commerce directly from your backend application.
Project Overview and Goals
Goal: To build a Node.js/Express backend service that can send and receive WhatsApp messages using the MessageBird Conversations API.
Technologies:
messagebird
Node.js SDK: Simplifies interaction with the MessageBird API.dotenv
: A module to load environment variables from a.env
file intoprocess.env
.ngrok
(for local development): A tool to expose your local server to the internet, necessary for receiving MessageBird webhooks during development.System Architecture:
A typical flow involves:
/send-whatsapp
) to your Node.js/Express API./webhook
) to your publicly accessible URL (or ngrok tunnel).Prerequisites:
Channel ID
. (MessageBird WhatsApp Quickstart)ngrok
to create a temporary public tunnel to your machine. For production, you'll need a deployed application with a permanent public HTTPS URL.async
/await
).Final Outcome: A running Node.js server with two primary endpoints: one to trigger outgoing WhatsApp messages and another to receive incoming messages via webhooks.
1. Setting Up the Project
Let's initialize the Node.js project and install the necessary dependencies.
Step 1: Create Project Directory and Initialize
Open your terminal and run the following commands:
This creates a new directory, navigates into it, and initializes a
package.json
file with default settings.Step 2: Install Dependencies
Install Express, the MessageBird SDK, and
dotenv
:express
: The web framework.messagebird
: The official Node.js SDK for interacting with the MessageBird API.dotenv
: To manage environment variables securely.Step 3: Create Project Structure
Create the basic files and directories:
Your initial structure should look like this:
Step 4: Configure
.gitignore
Add
node_modules
and.env
to your.gitignore
file to prevent committing them to version control:Step 5: Set Up Environment Variables
Open the
.env
file and add the following variables. You'll need to retrieve these values from your MessageBird Dashboard.MESSAGEBIRD_API_KEY
: Go to your MessageBird Dashboard -> Developers -> API access. Use your Live API key.MESSAGEBIRD_WHATSAPP_CHANNEL_ID
: Go to Channels -> WhatsApp -> Click on your configured channel. The Channel ID is displayed there.MESSAGEBIRD_WEBHOOK_SIGNING_SECRET
: Create a strong, unique secret (e.g., using a password generator). You will later configure this exact secret in the MessageBird dashboard when setting up your webhook. This is crucial for security.PORT
: The local port your Express server will listen on.Step 6: Basic Express Server Setup
Open
server.js
and add the initial Express server configuration:Explanation:
require('dotenv').config();
loads the variables from your.env
file. It's crucial to call this early.require('messagebird')(process.env.MESSAGEBIRD_API_KEY)
initializes the MessageBird client with your API key.express()
creates an Express application instance.app.use(express.json());
enables the server to parse incoming JSON payloads, which we'll use for our API endpoint and webhook handler./health
endpoint is included as good practice for monitoring.app.listen
starts the server. We add a check to ensure the essential environment variables are loaded.You can now run your basic server:
You should see
Server listening on port 3000
and confirmation that the environment variables loaded.2. Implementing Core Functionality: Sending WhatsApp Messages
Now, let's create the logic to send an outgoing WhatsApp message via MessageBird.
Step 1: Create the Sending Function
Add a function within
server.js
to handle the API call to MessageBird. We'll use theconversations.start
method for simplicity, which can initiate a conversation (often requires a pre-approved template if outside the 24-hour customer service window) or send a freeform message if within the window.Explanation:
sendWhatsAppMessage
function takes therecipient
number andtext
message.params
object required by the MessageBirdconversations.start
method.to
: The destination WhatsApp number (must be in E.164 format).from
: Your unique MessageBird WhatsApp Channel ID (loaded from.env
).type
: Specifies the message type (text
for now). Other types includehsm
(for templates),image
,video
,audio
,file
,location
.content
: An object containing the message payload, specific to thetype
. Fortext
, it's{ text: '...' }
.messagebird.conversations.start
initiates the API call. It uses a callback pattern. We wrap it in aPromise
for easier use withasync/await
in our API endpoint later.err.errors
could be implemented.Important Note on Templates (HSMs):
type
to'hsm'
and modify thecontent
object accordingly. Example:text
type, assuming you're either replying within the 24-hour window or testing with a number where you've initiated contact manually first.3. Building the API Layer to Send Messages
Let's create an HTTP endpoint that triggers the
sendWhatsAppMessage
function.Step 1: Add the POST Endpoint
Add the following route handler in
server.js
before theapp.listen
call:Explanation:
app.post('/send-whatsapp', ...)
defines a route that listens for POST requests on the/send-whatsapp
path.const { recipient, message } = req.body;
extracts the phone number and message content from the JSON request body.recipient
andmessage
are provided and that therecipient
looks like an E.164 number. You should implement more robust validation in a production environment (e.g., using libraries likeexpress-validator
).sendWhatsAppMessage
function is called usingawait
.200 OK
response is sent back with the MessageBird message ID and status.catch
), a500 Internal Server Error
is returned with error details (be cautious about exposing too much detail in production errors).Step 2: Test the Sending Endpoint
Restart your server: If it's running, stop it (
Ctrl+C
) and restart:node server.js
.Use
curl
or Postman: Send a POST request to your running server. Replace+YOUR_TEST_WHATSAPP_NUMBER
with a real WhatsApp number you can check (in E.164 format).Using
curl
:Expected Response (Success):
Expected Response (Error - e.g., Bad Number Format):
Check WhatsApp: You should receive the message on the test number shortly after a successful API call. Check your server logs (
console.log
) for output from thesendWhatsAppMessage
function and the MessageBird API response.4. Integrating Third-Party Service: Receiving Messages via Webhooks
To receive incoming WhatsApp messages sent to your MessageBird number, you need to configure and handle webhooks. MessageBird will send an HTTP POST request to a URL you specify whenever a new message arrives.
Step 1: Expose Local Server with
ngrok
(Development Only)During development, your
localhost
server isn't accessible from the public internet.ngrok
creates a secure tunnel. Remember,ngrok
provides temporary URLs suitable only for development; a permanent public URL is needed for production.Install
ngrok
: Follow instructions at ngrok.com.Run
ngrok
: Open a new terminal window (keep your Node server running) and startngrok
, pointing it to the port your Node app is running on (default 3000):Copy the HTTPS URL:
ngrok
will display forwarding URLs. Copy thehttps
URL (e.g.,https://random-string.ngrok-free.app
). This is your public webhook URL for now.Step 2: Configure Webhook in MessageBird Dashboard
https://
URL fromngrok
, followed by the path for your webhook handler (we'll use/webhook
). Example:https://random-string.ngrok-free.app/webhook
message.created
andmessage.updated
(for status updates).MESSAGEBIRD_WEBHOOK_SIGNING_SECRET
in your.env
file.Step 3: Implement the Webhook Handler in Node.js
Add the following code to
server.js
to create the/webhook
endpoint and handle incoming requests. This includes crucial signature verification.Explanation of Webhook Handler & Verification:
crypto
Module: Imported for cryptographic functions (HMAC-SHA256).express.raw({ type: 'application/json' })
: This middleware is applied only to the/webhook
route. It preventsexpress.json()
(which we applied globally earlier) from parsing the body for this route. We need the raw, unparsed request body (as a Buffer) to correctly verify the signature.messagebird-signature
andmessagebird-request-timestamp
headers sent by MessageBird.timestamp + ""."" + signing_key + ""."" + raw_request_body_as_utf8_string
.signedPayloadString
using yoursigningSecret
as the cryptographic key. Digest the result as a hexadecimal string.crypto.timingSafeEqual
to compare the signature from the header with the one you calculated. This method runs in constant time, preventing attackers from inferring the correct signature based on response time differences. Crucially, both inputs must be Buffers of the same length. Handle potential errors if the provided signature is not valid hex.JSON.parse(requestBody.toString('utf8'))
.message.created
: Extract message details (sendermessage.from
, contentmessage.content
, typemessage.type
). This is where you add your core logic to respond or act on the incoming message (e.g., save to DB, call other services, send a reply).message.updated
: Log status updates likedelivered
orread
for messages you sent previously. Use themessage.id
to update the status in your database.200 OK
response back to MessageBird promptly. MessageBird expects a quick acknowledgment. Any time-consuming processing (database writes, external API calls) should ideally be done asynchronously (e.g., push the event data to a queue) after sending the 200 OK, to avoid timeouts.403 Forbidden
,400 Bad Request
).Step 4: Test Receiving Messages
node server.js
.ngrok
is running: Check the terminal where you startedngrok
.node server.js
is running. You should see logs indicating:message.created
).ngrok
Logs: Thengrok
terminal (or its web interface, usually athttp://localhost:4040
) also shows incoming requests and their status codes, which is useful for debugging connection issues or seeing if MessageBird is hitting your endpoint.5. Implementing Proper Error Handling and Logging
While basic
console.log
andconsole.error
are used, production applications need more robust logging and error handling.Error Handling Strategy:
Specific API Errors: In the
sendWhatsAppMessage
callback/promise rejection, inspect theerr.errors
array provided by the MessageBird SDK for detailed error codes and descriptions. Log these details. Decide whether specific errors are retryable.Webhook Errors: Log signature verification failures clearly, including the received signature if possible (be mindful of logging sensitive data). Handle JSON parsing errors gracefully.
Centralized Express Error Handler: Implement Express middleware to catch unhandled errors in your routes. This should be the last middleware added.
Logging:
Replace
console.log/error
: Use a dedicated logging library likewinston
orpino
. These offer structured logging (JSON format is common), different log levels (info, warn, error, debug), and transport options (console, file, external logging services).Retry Mechanisms (Conceptual):
sendWhatsAppMessage
, implement a retry strategy.async-retry
orp-retry
to simplify this implementation.6. Creating a Database Schema and Data Layer (Conceptual)
Storing message history, user consent, and conversation state is often necessary for real applications.
Schema Example (Conceptual - using PostgreSQL syntax):
Data Layer:
saveIncomingMessage(messageData)
: Called from the webhook handler (message.created
) to insert a new record into themessages
table withdirection = 'incoming'
.saveOutgoingMessage(messageData)
: Called before or after callingsendWhatsAppMessage
to insert a record withdirection = 'outgoing'
and the initialstatus
(e.g., 'pending' or 'sent'). Store themessagebird_message_id
returned by the API.updateMessageStatus(messagebirdMessageId, status, timestamp)
: Called from the webhook handler (message.updated
) to update thestatus
andstatus_updated_at
fields for an existing message based on itsmessagebird_message_id
.checkConsent(contactMsisdn)
: Check thewhatsapp_consent
table before sending proactive messages (especially templates).recordConsent(contactMsisdn, optedIn)
: Update the consent status.Integrating this data layer involves calling these functions within your
/send-whatsapp
endpoint and/webhook
handler. Remember to handle database errors appropriately.