Frequently Asked Questions
Handle Infobip SMS delivery reports by setting up a webhook endpoint in your Node.js/Express application. This endpoint, specified by the notifyUrl
parameter in your Infobip API request, receives real-time delivery updates in JSON format. Your application should then process this data, updating internal systems and triggering actions based on the delivery status (e.g., delivered, failed).
The notify URL is a crucial parameter in the Infobip SMS API. It's the publicly accessible URL of your application's webhook endpoint, where Infobip sends real-time delivery reports (DLRs). This URL must be reachable by Infobip for your application to receive status updates.
Infobip needs a callback URL (the notifyUrl
) to send your application asynchronous updates about the delivery status of your SMS messages. This enables your system to react to successful deliveries, failures, or other status changes without constantly polling the Infobip API.
Use ngrok during local development with Infobip to create a publicly accessible URL for your webhook endpoint. Since Infobip needs to reach your local server for DLR callbacks, ngrok provides a tunnel from a public URL to your localhost, making testing and development easier.
You can implement retry mechanisms for failed Infobip SMS messages, but only for initial failures from the Infobip API (like network or 5xx errors), not for final statuses like 'UNDELIVERABLE'. Use exponential backoff to avoid overloading the system, but don't retry messages marked as permanently undeliverable by the carrier or Infobip.
The Infobip SMS notify content type is 'application/json'. This parameter specifies that delivery reports sent to your webhook endpoint will be in JSON format, making parsing and processing the data within your application more structured and efficient.
Set up Infobip DLR with Node.js by creating an Express.js server with two key endpoints: /send-sms
to initiate message sending and /infobip-dlr
to receive callbacks. The infobipService.js
file contains the logic for sending SMS and handling API interactions using Axios.
The express.json()
middleware in Express.js is essential for handling Infobip DLR callbacks. It parses incoming JSON payloads in POST requests, making the data accessible via req.body
. This allows you to easily process delivery reports and update your application's internal state.
Dotenv helps manage environment variables securely when integrating with the Infobip API. It loads credentials like your API key from a .env
file, keeping sensitive information out of your source code and version control.
To send SMS with the Infobip API and Node.js, use the sendSms
function from infobipService.js
. This function requires the recipient's phone number in international format and the message text. It handles constructing the API request, including headers, and making the request using Axios.
To test Infobip webhooks locally, expose your development server using ngrok and update the notifyUrl
in your Infobip API requests to point to the generated ngrok HTTPS URL. This allows Infobip to reach your local server with DLR callbacks during development.
Structure a database for Infobip DLRs with a table for outgoing messages. Include fields for message details, Infobip message ID, recipient number, status, error codes, and timestamps. Index the infobip_message_id
column for efficient lookup during callback processing.
Common status groups in Infobip DLRs include 'PENDING', 'UNDELIVERABLE', 'DELIVERED', 'EXPIRED', and 'REJECTED'. These groups provide a broad categorization of the message delivery status. More detailed status names within each group provide further information.
Best practices for logging Infobip DLRs include logging the full incoming request body to understand its structure and content. Log specific fields from each report, including messageId, status, recipient, and errors. Use structured logging and external logging services for improved analysis and alerting.
Implement security for your Infobip webhook endpoint by using HTTPS for secure communication. Optional security measures could include signature validation (if supported by Infobip) to verify the sender, and IP whitelisting if feasible. Basic input validation is also important for security and preventing unexpected input.
Developer Guide: Handling Infobip SMS Delivery Status Webhooks with Node.js and Express
This guide provides a complete walkthrough for building a Node.js application using the Express framework to send SMS messages via the Infobip API and handle real-time delivery status updates through webhooks (callbacks).
By the end of this tutorial, you will have a functional application capable of:
This enables developers to build more robust messaging workflows, providing visibility into message delivery success and enabling actions based on status updates — such as retrying failed messages or logging delivery confirmations.
Target Audience: Developers familiar with Node.js, Express, and basic API concepts.
System Architecture
The system involves the following components:
notifyUrl
for callbacks./infobip-dlr
) to receive delivery reports from Infobip.notifyUrl
provided by the application.Prerequisites
1. Setting up the Project
Let's initialize our Node.js project and install the necessary dependencies.
1.1 Create Project Directory
Open your terminal or command prompt and create a new directory for the project.
1.2 Initialize Node.js Project
Initialize the project using npm (or yarn). This creates a
package.json
file.-y
? This flag accepts the default settings during initialization, speeding up the process. You can omit it to customize project details.1.3 Install Dependencies
We need
express
for our web server,axios
to make HTTP requests to the Infobip API, anddotenv
to manage environment variables securely.express
: A minimal and flexible Node.js web application framework.axios
: A popular promise-based HTTP client for making requests to external APIs like Infobip's.dotenv
: Loads environment variables from a.env
file intoprocess.env
, keeping sensitive information like API keys out of your source code.1.4 Create Project Structure
Create the basic files and folders:
Your initial structure should look like this:
1.5 Configure
.gitignore
Add
node_modules
and.env
to your.gitignore
file. This prevents committing dependencies and sensitive credentials to version control..env
? It contains sensitive API keys and credentials. Never commit this file to public or shared repositories. Team members should create their own.env
files based on a template or secure sharing mechanism.1.6 Set up Environment Variables (
.env
)Open the
.env
file and add placeholders for your Infobip credentials and application settings. You will replace these placeholders later.INFOBIP_API_KEY
: Your secret key for authenticating API requests.INFOBIP_BASE_URL
: The specific domain assigned to your Infobip account for API access.PORT
: The port your Express application will listen on.APP_BASE_URL
: The base URL where your application is accessible from the internet. Infobip needs this to send callbacks. For local development, this will be your ngrok tunnel URL.2. Implementing Core Functionality: Sending SMS
Let's create a service to interact with the Infobip API for sending SMS messages.
2.1 Create Infobip Service File
This step was included in the project structure setup (1.4). You should have an
infobipService.js
file.2.2 Implement
sendSms
FunctionAdd the following code to
infobipService.js
. This code adapts the logic from the Infobip developer blog post, adding the crucialnotifyUrl
parameter.async/await
? It simplifies working with promises returned byaxios
, making the code cleaner and easier to read than using.then()
and.catch()
chains directly for complex flows.notifyUrl
andnotifyContentType
? These parameters instruct Infobip where and how (JSON format) to send the delivery report once the final status of the message is known. This is the core mechanism for enabling callbacks.APP_BASE_URL
? The callback URL must be absolute and publicly accessible. We construct it using the base URL defined in our environment variables.3. Building the API Layer and Handling Callbacks
Now, let's set up our Express server, create an endpoint to trigger SMS sending, and critically, an endpoint to receive the delivery reports from Infobip.
3.1 Basic Express Server Setup
Modify your
index.js
file:require('dotenv').config()
first? This line must execute before any code that accessesprocess.env
variables (like ourinfobipService.js
) to ensure those variables are loaded from the.env
file.express.json()
middleware? Infobip sends delivery reports as JSON payloads in the POST request body. This middleware parses that JSON and makes it available asreq.body
. It's also needed for our/send-sms
endpoint to parse the incoming JSON request./send-sms
endpoint? This provides a clean interface for other parts of your system (or external clients) to request an SMS without needing direct access to the Infobip service logic./infobip-dlr
endpoint? This is the publicly accessible listener that Infobip will call. Its sole purpose is to receive, acknowledge, and process incoming delivery status updates.res.sendStatus(200)
in/infobip-dlr
? You must respond to Infobip's webhook request with a success status (2xx) quickly. This acknowledges receipt. If you respond with an error (4xx, 5xx) or time out, Infobip will likely retry sending the report according to their retry schedule, potentially leading to duplicate processing if not handled carefully. Do complex processing asynchronously if needed.req.body.results
? Based on Infobip documentation and common webhook patterns, delivery reports often come batched within aresults
array in the JSON payload. Always inspect the actual payload you receive during testing to confirm the structure.4. Integrating with Infobip: Configuration
Let's get the necessary credentials from Infobip and configure our
.env
file.4.1 Obtain Infobip API Key and Base URL
Node DLR App Key
).xxxxxx.api.infobip.com
.4.2 Update
.env
FileOpen your
.env
file and replace the placeholders with the actual values you obtained:4.3 Set Up ngrok for Local Development
Infobip needs to reach your
/infobip-dlr
endpoint over the public internet.ngrok
creates a secure tunnel from a public URL to your local machine.Download and install ngrok.
Authenticate ngrok if you have an account (optional but recommended for longer sessions).
In your terminal (in a separate window from your running Node app)_ start ngrok_ pointing it to the port your Express app is running on (defined by
PORT
in.env
_ default 3000).ngrok will display output including lines like:
Copy the
https
Forwarding URL (e.g.,https://<random-string>.ngrok.io
). This is your public URL.Update
APP_BASE_URL
in your.env
file with thishttps
ngrok URL.Restart your Node.js application (
node index.js
or usingnodemon
) after updating.env
so it picks up the newAPP_BASE_URL
.5. Error Handling and Logging
Our current code includes basic console logging and error handling. Let's refine it slightly.
infobipService.js
): ThesendSms
function already includes atry...catch
block that logs detailed errors fromaxios
, distinguishing between response errors, request errors, and setup errors. It re-throws a generic error to the caller./send-sms
inindex.js
): The endpoint wraps the service call intry...catch
. It logs the error server-side and returns a generic 500 error to the client to avoid leaking internal details. Basic 400 Bad Request errors are returned for invalid input./infobip-dlr
inindex.js
): This endpoint logs the entire incoming payload. It includes aconsole.warn
for unexpected formats and logs specific details for recognized formats. Crucially, it always responds with 200 OK to prevent Infobip retries, even if internal processing fails. Robust error handling here might involve:try...catch
.res.sendStatus(200)
back to Infobip.Further Improvements (Beyond this Guide):
winston
orpino
for structured JSON logging, making logs easier to parse and analyze, especially in production.axios-retry
orasync-retry
. Do not retry based on DLRs indicating final failure (likeUNDELIVERABLE
).6. Database Schema and Data Layer (Conceptual)
While this guide doesn't implement a database, here's how you would typically integrate one:
6.1 Conceptual Schema
You'd likely need a table to store information about outgoing messages:
6.2 Data Layer Integration
/send-sms
):infobipService.sendSms
, insert a new record intooutgoing_messages
with statusPENDING_INFOBIP
.infobipService.sendSms
, update the record using themessageId
from the Infobip response, setting theinfobip_message_id
field and potentially updating status toPENDING_ACCEPTED
(or similar based on the initial response)./infobip-dlr
):req.body.results
:messageId
from the report.outgoing_messages
usinginfobip_message_id
.infobip_status_group
,infobip_status_name
,infobip_status_description
, error fields,callback_received_at
, andlast_updated_at
based on the DLR content.messageId
is not found (log an error/warning).Tools: Use an ORM like Sequelize (for SQL databases) or Mongoose (for MongoDB) to manage database interactions more easily. Use migration tools like
sequelize-cli
orknex migrations
to manage schema changes.7. Security Features
Security is paramount, especially when handling API keys and potentially sensitive message data.
.env
to keepINFOBIP_API_KEY
out of the code and.gitignore
to prevent committing it. This is crucial./send-sms
): Basic validation is implemented to check for the presence and type ofto
andtext
. Enhance this based on expected phone number formats or message length constraints. Use libraries likejoi
orexpress-validator
for more robust validation schemas./infobip-dlr
):ngrok
with HTTPS and deploying to a server with HTTPS configured is essential to encrypt data in transit.X-Infobip-Signature
) along with the webhook. If so, you would:notifyUrl
as of common knowledge, but check their latest documentation./infobip-dlr
from those specific IPs. This can be difficult to maintain if IPs change./send-sms
endpoint from abuse by implementing rate limiting using middleware likeexpress-rate-limit
.helmet
middleware for Express to set various security-related HTTP headers (e.g., Content-Security-Policy, X-Content-Type-Options).npm install helmet
andapp.use(helmet());
.8. Handling Special Cases
447123456789
for the UK,14155552671
for the US). Ensure your input validation or normalization logic enforces this format before sending to the API.PENDING
status indicates the message is still in transit./infobip-dlr
) to be idempotent — meaning processing the same DLR multiple times has no adverse effects. Using theinfobip_message_id
as a unique key in your database helps achieve this (e.g., update status only if the new status is different or more final than the existing one).TIMESTAMPTZ
(Timestamp with Time Zone) in your database schemas (created_at
,last_updated_at
,callback_received_at
) to store timestamps unambiguously. Log timestamps usingtoISOString()
for clarity.9. Performance Optimizations (Conceptual)
For high-volume applications:
/infobip-dlr
request handler. Immediately send the200 OK
response, and then push the DLR payload onto a message queue (like RabbitMQ, Redis Streams, or AWS SQS) for background workers to process. This prevents holding up Infobip's request and avoids timeouts.WHERE
clauses (especiallyinfobip_message_id
).k6
,artillery
, orJMeter
to simulate traffic to your/send-sms
endpoint and (if possible) your/infobip-dlr
endpoint to identify bottlenecks under load.10. Monitoring, Observability, and Analytics (Conceptual)
app.get('/')
endpoint or create a dedicated/health
endpoint that checks basic connectivity (e.g., can reach the database). Monitoring services can ping this endpoint.