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:
notifyUrlfor callbacks./infobip-dlr) to receive delivery reports from Infobip.notifyUrlprovided 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.jsonfile.-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
expressfor our web server,axiosto make HTTP requests to the Infobip API, anddotenvto 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.envfile 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
.gitignoreAdd
node_modulesand.envto your.gitignorefile. 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.envfiles based on a template or secure sharing mechanism.1.6 Set up Environment Variables (
.env)Open the
.envfile 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.jsfile.2.2 Implement
sendSmsFunctionAdd the following code to
infobipService.js. This code adapts the logic from the Infobip developer blog post, adding the crucialnotifyUrlparameter.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.notifyUrlandnotifyContentType? 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.jsfile:require('dotenv').config()first? This line must execute before any code that accessesprocess.envvariables (like ourinfobipService.js) to ensure those variables are loaded from the.envfile.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-smsendpoint to parse the incoming JSON request./send-smsendpoint? 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-dlrendpoint? 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 aresultsarray 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
.envfile.4.1 Obtain Infobip API Key and Base URL
Node DLR App Key).xxxxxx.api.infobip.com.4.2 Update
.envFileOpen your
.envfile and replace the placeholders with the actual values you obtained:4.3 Set Up ngrok for Local Development
Infobip needs to reach your
/infobip-dlrendpoint over the public internet.ngrokcreates 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
PORTin.env_ default 3000).ngrok will display output including lines like:
Copy the
httpsForwarding URL (e.g.,https://<random-string>.ngrok.io). This is your public URL.Update
APP_BASE_URLin your.envfile with thishttpsngrok URL.Restart your Node.js application (
node index.jsor usingnodemon) after updating.envso 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): ThesendSmsfunction already includes atry...catchblock that logs detailed errors fromaxios, distinguishing between response errors, request errors, and setup errors. It re-throws a generic error to the caller./send-smsinindex.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-dlrinindex.js): This endpoint logs the entire incoming payload. It includes aconsole.warnfor 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):
winstonorpinofor structured JSON logging, making logs easier to parse and analyze, especially in production.axios-retryorasync-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_messageswith statusPENDING_INFOBIP.infobipService.sendSms, update the record using themessageIdfrom the Infobip response, setting theinfobip_message_idfield and potentially updating status toPENDING_ACCEPTED(or similar based on the initial response)./infobip-dlr):req.body.results:messageIdfrom the report.outgoing_messagesusinginfobip_message_id.infobip_status_group,infobip_status_name,infobip_status_description, error fields,callback_received_at, andlast_updated_atbased on the DLR content.messageIdis 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-cliorknex migrationsto manage schema changes.7. Security Features
Security is paramount, especially when handling API keys and potentially sensitive message data.
.envto keepINFOBIP_API_KEYout of the code and.gitignoreto prevent committing it. This is crucial./send-sms): Basic validation is implemented to check for the presence and type oftoandtext. Enhance this based on expected phone number formats or message length constraints. Use libraries likejoiorexpress-validatorfor more robust validation schemas./infobip-dlr):ngrokwith 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:notifyUrlas of common knowledge, but check their latest documentation./infobip-dlrfrom those specific IPs. This can be difficult to maintain if IPs change./send-smsendpoint from abuse by implementing rate limiting using middleware likeexpress-rate-limit.helmetmiddleware for Express to set various security-related HTTP headers (e.g., Content-Security-Policy, X-Content-Type-Options).npm install helmetandapp.use(helmet());.8. Handling Special Cases
447123456789for the UK,14155552671for the US). Ensure your input validation or normalization logic enforces this format before sending to the API.PENDINGstatus 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_idas 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-dlrrequest handler. Immediately send the200 OKresponse, 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.WHEREclauses (especiallyinfobip_message_id).k6,artillery, orJMeterto simulate traffic to your/send-smsendpoint and (if possible) your/infobip-dlrendpoint to identify bottlenecks under load.10. Monitoring, Observability, and Analytics (Conceptual)
app.get('/')endpoint or create a dedicated/healthendpoint that checks basic connectivity (e.g., can reach the database). Monitoring services can ping this endpoint.