sms compliance
sms compliance
How to Build Two-Way SMS Messaging with Node.js, Express, and Infobip
Learn how to build two-way SMS messaging with Node.js, Express, and Infobip API. Complete tutorial covering webhooks, error handling, security, and production deployment.
How to Build Two-Way SMS Messaging with Node.js, Express, and Infobip
This comprehensive guide walks you through building a production-ready Node.js application using Express to handle two-way SMS messaging powered by the Infobip SMS API. You'll learn how to send and receive SMS messages, configure webhooks for inbound messages, implement error handling, secure your application, and deploy it to production.
We'll build a simple "echo bot" – an SMS automation application that automatically replies to any incoming text message with the same content. This serves as a solid foundation for more complex conversational SMS workflows, chatbots, and automated messaging systems.
Project Goals:
- Send outbound SMS messages using the Infobip Node.js SDK.
- Receive inbound SMS messages from Infobip via webhooks.
- Implement basic two-way SMS logic (echoing the message).
- Securely manage API credentials and environment variables.
- Set up essential error handling and logging.
- Prepare the application for production deployment.
Technologies Used:
- Node.js: A JavaScript runtime environment for server-side development (minimum version 14 required by Infobip SDK).
- Express: A minimal and flexible Node.js web application framework for building REST APIs.
- Infobip API & Node.js SDK (
@infobip-api/sdk): Official SDK for interacting with Infobip's SMS services (uses/sms/2/text/advancedendpoint). - dotenv: To manage environment variables securely.
System Architecture:
User's Phone <-----> Mobile Carrier <-----> Infobip Platform <-----> Your Node.js/Express App
| (SMS) | (Webhook POST & API Call)
+-------------------------------------------+
(SMS Delivery)
- A user sends an SMS to your provisioned Infobip phone number.
- Infobip receives the SMS (Mobile Originated - MO).
- Infobip sends an HTTP POST request (webhook) containing the message details to your configured endpoint in the Node.js/Express application.
- Your application processes the webhook payload (extracts sender number and message text).
- Your application uses the Infobip SDK to send a reply SMS (Mobile Terminated - MT) back to the original sender.
- Infobip delivers the reply SMS to the user's phone.
Prerequisites:
- An active Infobip account (Sign up here).
- A provisioned phone number within your Infobip account capable of sending and receiving SMS.
- Node.js (version 14 or higher) and npm (or yarn) installed on your development machine. (Download Node.js).
- Basic understanding of JavaScript, Node.js, REST APIs, and the command line.
- A tool for testing API endpoints (like
curlor Postman). - A way to expose your local development server to the internet (e.g., ngrok) for testing inbound webhooks.
Final Outcome:
By the end of this guide, you will have a running Node.js/Express application that can receive SMS messages sent to your Infobip number and automatically reply. You will also have a solid understanding of integrating Infobip for two-way SMS communication and best practices for building such applications.
1. Setting up the Project
Let's start by creating our project structure and installing the necessary dependencies.
-
Create Project Directory: Open your terminal or command prompt and create a new directory for your project. Navigate into it:
bashmkdir infobip-two-way-sms cd infobip-two-way-sms -
Initialize Node.js Project: Initialize the project using npm. This creates a
package.jsonfile to manage dependencies and project metadata. You can accept the defaults or customize them.bashnpm init -y- Why
-y? This flag automatically accepts the default settings fornpm init, speeding up the process. You can omit it to configure settings manually.
- Why
-
Install Dependencies: We need Express for our web server, the Infobip SDK to interact with their API, and
dotenvto manage environment variables.bashnpm install express @infobip-api/sdk dotenvexpress: The web framework.@infobip-api/sdk: The official Infobip Node.js SDK simplifies API interactions.dotenv: Loads environment variables from a.envfile intoprocess.env.
-
Create Project Structure: Let's organize our code logically.
bashmkdir src touch src/server.js src/infobipService.js src/webhookHandler.js touch .env touch .gitignoresrc/: Contains our main application source code.src/server.js: The main entry point for our Express application.src/infobipService.js: A dedicated module for Infobip API interactions (sending SMS).src/webhookHandler.js: A module to handle incoming Infobip webhooks..env: Stores sensitive configuration like API keys (DO NOT commit this file)..gitignore: Specifies intentionally untracked files that Git should ignore (likenode_modulesand.env).
-
Configure
.gitignore: Add the following lines to your.gitignorefile to prevent committing sensitive information and unnecessary files:text# Dependencies node_modules/ # Environment variables .env # Logs logs *.log npm-debug.log* yarn-debug.log* yarn-error.log* # Optional Editor directories .idea .vscode *.suo *.ntvs* *.njsproj *.sln *.sw? -
Set up Basic Express Server (
src/server.js): Opensrc/server.jsand add the following initial setup:javascript// src/server.js require('dotenv').config(); // Load environment variables from .env file const express = require('express'); const webhookHandler = require('./webhookHandler'); // We'll create this next const app = express(); const PORT = process.env.PORT || 3000; // Use port from env or default to 3000 // Middleware to parse JSON bodies. Crucial for reading webhook payloads. app.use(express.json()); // Define a simple root route for health checks or basic info app.get('/', (req, res) => { res.status(200).send('Infobip Two-Way SMS Server Running!'); }); // Define the route for Infobip inbound SMS webhooks // We use POST as Infobip sends data via POST requests app.post('/webhook/infobip/sms', webhookHandler.handleIncomingSms); // Start the server app.listen(PORT, () => { console.log(`Server listening on port ${PORT}`); });require('dotenv').config(): Must be called early to load variables before they are used.express.json(): This middleware is essential. Infobip sends webhook data as JSON in the request body. This middleware parses it and makes it available asreq.body./webhook/infobip/sms: This is the specific path where our application will listen for incoming SMS notifications from Infobip. You'll configure this URL in the Infobip portal later.
-
Add Start Script to
package.json: Open yourpackage.jsonfile and add astartscript within the"scripts"section. This provides a standard way to run your application.json// package.json (partial view) { // ... other configurations "scripts": { "start": "node src/server.js", // Add this line "test": "echo \"Error: no test specified\" && exit 1" }, // ... rest of the configurations }Now you can start your server (though it won't do much yet) by running
npm start.
2. Implementing Core Functionality (Sending SMS)
We'll create a dedicated service to encapsulate the logic for sending SMS messages using the Infobip SDK.
-
Configure Environment Variables (
.env): Open the.envfile and add your Infobip API Key and Base URL.dotenv# .env INFOBIP_API_KEY=YOUR_INFOBIP_API_KEY INFOBIP_BASE_URL=YOUR_INFOBIP_BASE_URL INFOBIP_SENDER_ID=YOUR_INFOBIP_NUMBER_OR_SENDER_ID # Your Infobip number or registered Alphanumeric Sender IDINFOBIP_API_KEY:- Purpose: Authenticates your requests to the Infobip API.
- How to obtain: Log in to your Infobip account. Navigate to the "API Keys" section (often found under account settings or developer tools). Generate a new API key if you don't have one. Copy the full key.
- Format: A long alphanumeric string (e.g.,
a1b2c3d4e5f67890a1b2c3d4e5f67890-a1b2c3d4-e5f6-7890-a1b2-c3d4e5f67890).
INFOBIP_BASE_URL:- Purpose: Specifies the regional API endpoint you should connect to.
- How to obtain: This is provided in your Infobip account dashboard, often on the homepage or API documentation landing page after you log in. It's specific to your account region.
- Format: A URL like
xxxxx.api.infobip.com. Do not includehttps://here; the SDK handles the protocol.
INFOBIP_SENDER_ID:- Purpose: The "from" address for your outbound SMS. This must be a number you've provisioned in Infobip or a pre-registered Alphanumeric Sender ID. Using your provisioned number is crucial for two-way messaging so users can reply.
- How to obtain: Find your purchased/provisioned numbers in the "Numbers" section of your Infobip portal.
- Format: A phone number in E.164 international format (e.g.,
447123456789– no spaces, country code included) or a registered alphanumeric string (e.g.,InfoSMS).
Security: Never commit your
.envfile to version control. Ensure.envhas restrictive file permissions (chmod 600 .env) and is never committed. -
Create Infobip Service (
src/infobipService.js): This module will initialize the Infobip client and provide a function to send messages.javascript// src/infobipService.js const { Infobip, AuthType } = require('@infobip-api/sdk'); // Ensure required environment variables are set if (!process.env.INFOBIP_BASE_URL || !process.env.INFOBIP_API_KEY) { console.error(""INFOBIP_BASE_URL and INFOBIP_API_KEY must be set in the .env file""); process.exit(1); // Exit if configuration is missing } // Initialize the Infobip client const infobipClient = new Infobip({ baseUrl: process.env.INFOBIP_BASE_URL, apiKey: process.env.INFOBIP_API_KEY, authType: AuthType.ApiKey, // Specify API Key authentication }); /** * Sends an SMS message using the Infobip API. * @param {string} recipientNumber - The destination phone number in E.164 format (e.g., 447123456789). * @param {string} messageText - The text content of the SMS. * @param {string} [senderId=process.env.INFOBIP_SENDER_ID] - The sender ID (your Infobip number or registered alphanumeric). Defaults to env var. * @returns {Promise<object>} - A promise that resolves with the Infobip API response or rejects with an error. */ const sendSms = async (recipientNumber, messageText, senderId = process.env.INFOBIP_SENDER_ID) => { if (!recipientNumber || !messageText) { throw new Error('Recipient number and message text are required.'); } if (!senderId) { console.warn('INFOBIP_SENDER_ID is not set. Using default sender.'); // Infobip might assign a shared short code if no sender is provided, // which might affect reply capabilities and deliverability. // For two-way, always use your provisioned number. } const payload = { messages: [ { destinations: [{ to: recipientNumber }], from: senderId, // Use the configured sender ID or number text: messageText, }, ], }; console.log(`Attempting to send SMS to ${recipientNumber} from ${senderId || 'default'}`); try { // Use the SDK's send method const response = await infobipClient.channels.sms.send(payload); console.log('Infobip SMS Send API Response:', JSON.stringify(response.data, null, 2)); return response.data; // Return the body of the response } catch (error) { console.error('Error sending SMS via Infobip:', error.response ? JSON.stringify(error.response.data, null, 2) : error.message); // Re-throw the error or handle it as needed throw error; } }; module.exports = { sendSms, };- Client Initialization: We create a single instance of the
Infobipclient using the credentials from.env.AuthType.ApiKeyis specified. sendSmsFunction: Takes the recipient, message text, and optionally a sender ID. It constructs the payload according to the Infobip API specification (specifically for the/sms/2/text/advancedendpoint structure, which the SDK uses).- Error Handling: Includes a
try...catchblock to handle potential API errors during the send request and logs relevant information. - Sender ID: Emphasizes the importance of
INFOBIP_SENDER_IDfor two-way communication. If not set, Infobip might use a default sender, which likely won't work for replies.
- Client Initialization: We create a single instance of the
3. Building the API Layer (Handling Inbound SMS)
Now, let's implement the logic to receive and process the incoming SMS messages sent by Infobip to our webhook endpoint.
-
Understand the Infobip Inbound SMS Payload: When Infobip receives an SMS directed to your number, it will make a POST request to your configured webhook URL (
/webhook/infobip/smsin our case). The request body contains JSON data similar to this structure (refer to Infobip SMS API documentation for the complete, up-to-date schema):json{ "results": [ { "messageId": "abc123xyz456", "from": "447123456789", // Sender's phone number "to": "447987654321", // Your Infobip number "text": "Hello from user!", // The message content "cleanText": "Hello from user!", "keyword": "HELLO", // Often the first word, capitalized "receivedAt": "2025-04-20T10:30:00.123Z", "smsCount": 1, "price": { "pricePerMessage": 0, "currency": "EUR" }, "callbackData": null // Data you might have sent with the outbound message } // Potentially more messages if received in quick succession ], "messageCount": 1, "pendingMessageCount": 0 }- Key Fields:
results[0].from(who sent the message) andresults[0].text(what they sent) are crucial for our echo bot.
- Key Fields:
-
Implement the Webhook Handler (
src/webhookHandler.js): This module takes the incoming request, extracts the necessary information, and uses theinfobipServiceto send a reply.javascript// src/webhookHandler.js const infobipService = require('./infobipService'); /** * Handles incoming SMS messages from Infobip webhooks. * Parses the message, logs it, and triggers a reply. * @param {import('express').Request} req - The Express request object. * @param {import('express').Response} res - The Express response object. */ const handleIncomingSms = async (req, res) => { console.log("Received Infobip Webhook:", JSON.stringify(req.body, null, 2)); // Basic validation: Check if the payload looks like an Infobip SMS notification if (!req.body || !req.body.results || !Array.isArray(req.body.results) || req.body.results.length === 0) { console.error("Invalid or empty payload received."); // Respond quickly to Infobip to acknowledge receipt, even if invalid return res.status(400).send({ message: "Invalid payload structure" }); } // Infobip recommends responding quickly to webhooks. // Acknowledge receipt immediately before processing. res.status(200).send({ message: "Webhook received successfully" }); // Process each message in the payload (usually just one) for (const message of req.body.results) { const sender = message.from; const receivedText = message.text; const messageId = message.messageId; // Useful for logging/tracking if (!sender || !receivedText) { console.warn(`Skipping message ${messageId}: Missing sender or text.`); continue; // Skip this message and proceed to the next if any } console.log(`Processing message ${messageId} from ${sender}: "${receivedText}"`); // --- Business Logic: Echo the message back --- const replyText = `You sent: "${receivedText}"`; try { // Use the Infobip service to send the reply // The 'sender' of the incoming message is the 'recipient' of our reply await infobipService.sendSms(sender, replyText); console.log(`Successfully sent reply to ${sender} for message ${messageId}`); } catch (error) { console.error(`Failed to send reply to ${sender} for message ${messageId}:`, error.message); // Implement retry logic or further error handling here if needed } // --- End Business Logic --- } }; module.exports = { handleIncomingSms, };- Logging: The first step is to log the entire incoming payload. This is invaluable for debugging.
- Validation: Basic checks ensure the payload structure is roughly what we expect.
- Quick Response: It's crucial to send a
200 OKresponse back to Infobip before doing potentially long-running processing (like calling the API to send the reply). Webhooks often have short timeouts. - Message Iteration: The code iterates through the
resultsarray, although typically it contains only one message per webhook request. - Data Extraction: It pulls out the
sender(from) andreceivedText(text). - Business Logic: This is where you'd implement your application's specific response logic. Here, we simply formulate an echo reply.
- Sending Reply: It calls
infobipService.sendSms, passing the original sender's number as the recipient for the reply. - Error Handling: Includes a
try...catcharound thesendSmscall to handle failures specific to sending the reply.
4. Integrating with Infobip (Webhook Configuration)
Your code is ready to send and receive, but you need to tell Infobip where to send the incoming SMS notifications.
-
Expose Your Local Server: Infobip's servers need to be able to reach your running application. During development, your machine is usually behind a firewall/NAT. Tools like
ngrokcreate a secure tunnel and provide a public URL.-
Install ngrok (Download ngrok).
-
Run your Node.js app:
npm start(it should log ""Server listening on port 3000""). -
In a new terminal window, start ngrok, telling it to forward to your application's port (e.g., 3000):
bashngrok http 3000 -
ngrok will display output similar to this:
Session Status online Account Your Name (Plan: Free) Version 3.x.x Region United States (us) Web Interface http://127.0.0.1:4040 Forwarding https://<unique-subdomain>.ngrok-free.app -> http://localhost:3000 Connections ttl opn rt1 rt5 p50 p90 0 0 0.00 0.00 0.00 0.00 -
Copy the
https://<unique-subdomain>.ngrok-free.appURL. This is your public webhook URL for now.
-
-
Configure Webhook in Infobip Portal:
- Log in to your Infobip account.
- Navigate to the ""Numbers"" section (or equivalent where you manage your purchased phone numbers).
- Find the specific phone number you are using for this application and go to its settings/configuration.
- Look for a section related to ""Forwarding,"" ""Webhooks,"" ""Messaging Settings,"" or ""Inbound Messages.""
- There should be an option to configure a URL for Incoming SMS Messages (or similar wording).
- Paste your ngrok URL, followed by the specific path you defined in
server.js:https://<unique-subdomain>.ngrok-free.app/webhook/infobip/sms - Important: Ensure the method is set to
POST. - Save the configuration changes in the Infobip portal.
(Dashboard navigation paths can change. Refer to the current Infobip documentation if you can't find the exact location.)
5. Error Handling, Logging, and Retries
Production applications need robust error handling and logging.
-
Consistent Error Handling:
- Our current code uses
try...catchblocks in key areas (infobipService.sendSms,webhookHandler.handleIncomingSms). - Always log errors with sufficient context (e.g., message ID, sender number, error details).
- Distinguish between errors that should stop processing (like invalid config) and errors that affect a single message (like failed API call to send reply).
- Consider creating custom error classes for better error categorization.
- Our current code uses
-
Logging:
console.logandconsole.errorare used for basic logging.- For production, use a dedicated logging library like Winston or Pino. They offer:
- Different log levels (debug, info, warn, error).
- Structured logging (JSON format is common for easier parsing).
- Multiple transports (log to console, files, external services).
- Example (Conceptual with Winston):
javascript
// Example logging setup (e.g., in a logger.js module) const winston = require('winston'); const logger = winston.createLogger({ level: process.env.LOG_LEVEL || 'info', format: winston.format.combine( winston.format.timestamp(), winston.format.json() ), transports: [ new winston.transports.Console(), // Add file transport for production // new winston.transports.File({ filename: 'error.log', level: 'error' }), // new winston.transports.File({ filename: 'combined.log' }), ], }); // Use logger.info(), logger.warn(), logger.error() instead of console.*
-
Retry Mechanisms:
- Network issues or temporary Infobip API glitches can cause requests to fail. Implement retries for transient errors.
- Use libraries like
async-retryor implement a simple loop with exponential backoff (wait longer between each retry). - Example (Conceptual
async-retryfor sending):javascript// Inside infobipService.sendSms, wrap the API call const retry = require('async-retry'); // ... try { await retry( async bail => { // bail is a function to call for non-retryable errors console.log(`Attempting to send SMS (retry logic)...`); const response = await infobipClient.channels.sms.send(payload); console.log('Infobip SMS Send API Response:', JSON.stringify(response.data, null, 2)); return response.data; }, { retries: 3, // Number of retries factor: 2, // Exponential backoff factor minTimeout: 1000, // Initial wait time (ms) onRetry: (error, attempt) => { console.warn(`Retrying SMS send (attempt ${attempt}) due to error: ${error.message}`); } } ); } catch (error) { console.error('Error sending SMS via Infobip after retries:', error.response ? JSON.stringify(error.response.data, null, 2) : error.message); throw error; } - Caution: Be careful not to retry errors that are clearly permanent (e.g., invalid API key, invalid recipient number format - Infobip error codes can help identify these).
6. Database Schema and Data Layer (Optional for Stateful Apps)
Our current echo bot is stateless – it doesn't remember past interactions. For most real-world applications (support chats, surveys, multi-step workflows), you'll need a database.
-
Why a Database?
- Store conversation history.
- Manage user state (e.g., which step of a survey they are on).
- Link SMS conversations to user accounts in your system.
- Store user preferences or data collected via SMS.
-
Example Schema (Conceptual - PostgreSQL/Prisma): You might have tables like:
User: Stores user information, potentially linked by phone number.Conversation: Groups related messages.Message: Stores individual inbound/outbound messages with details (sender, recipient, text, timestamp, Infobip message ID, status, direction - inbound/outbound).
-
Data Access:
- Use an Object-Relational Mapper (ORM) like Prisma or Sequelize to interact with your database in a structured way.
- Implement functions to:
- Find or create a user based on phone number.
- Create/retrieve conversation records.
- Save inbound and outbound messages, linking them to conversations/users.
- Query conversation history.
- Example (
webhookHandler.jsmodified conceptually):javascript// Inside handleIncomingSms, after extracting sender/text // const user = await db.findOrCreateUserByPhone(sender); // const conversation = await db.findOrCreateActiveConversation(user.id); // await db.saveMessage({ // conversationId: conversation.id, // direction: 'inbound', // infobipMessageId: messageId, // sender: sender, // recipient: message.to, // Your Infobip number // text: receivedText, // }); // --- Business Logic (now potentially stateful) --- // const currentState = conversation.state; // const replyText = determineReplyBasedOnState(currentState, receivedText); // conversation.state = determineNextState(...); // await db.updateConversationState(conversation.id, conversation.state); // --- End Business Logic --- // await infobipService.sendSms(sender, replyText); // await db.saveMessage({ ... direction: 'outbound', ... });
-
Migrations: Use the ORM's migration tools (e.g.,
prisma migrate dev) to manage database schema changes safely.
7. Adding Security Features
Security is paramount, especially when handling user data and API keys.
- Environment Variables: We're already using
.envto keep API keys out of the codebase. Ensure the.envfile has restrictive permissions (chmod 600 .env) and is never committed. - Input Validation/Sanitization:
- Webhook Payload: While we have basic structure validation, you could add more specific checks (e.g., ensure
fromlooks like a phone number, limittextlength). Libraries like Joi or Zod are excellent for schema validation. - Database Inputs: Sanitize any user-provided text (
receivedText) before storing it in the database or using it in replies to prevent potential Cross-Site Scripting (XSS) issues if this data is ever displayed in a web interface. Libraries likedompurify(if rendering as HTML) or simply escaping special characters might be needed depending on usage.
- Webhook Payload: While we have basic structure validation, you could add more specific checks (e.g., ensure
- Webhook Security:
- Problem: Anyone could potentially send a POST request to your
/webhook/infobip/smsendpoint, pretending to be Infobip. - Solutions:
- Signature Validation (Recommended): Check if Infobip offers webhook signature validation (often using HMAC-SHA signatures with a shared secret). This cryptographically verifies the request originated from Infobip. Consult Infobip's documentation for details on enabling and verifying signatures. If they offer it, implement the validation middleware.
- IP Filtering: Configure your firewall or infrastructure (e.g., AWS Security Group, Cloudflare) to allow requests to the webhook endpoint only from Infobip's published IP address ranges. This is less secure than signature validation but better than nothing.
- Secret in URL (Less Secure): Include a hard-to-guess secret token in the webhook URL path (
/webhook/infobip/sms/YOUR_SECRET_TOKEN). This provides minimal obscurity but is vulnerable if the URL leaks.
- Problem: Anyone could potentially send a POST request to your
- Rate Limiting:
- Protect your application from abuse (accidental or malicious) by limiting the number of requests from a single IP address or for a specific user.
- Use middleware like
express-rate-limit. - Example (
server.js):javascriptconst rateLimit = require('express-rate-limit'); const webhookLimiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 100, // Limit each IP to 100 requests per windowMs message: 'Too many requests from this IP, please try again after 15 minutes', standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers legacyHeaders: false, // Disable the `X-RateLimit-*` headers }); // Apply to the webhook route app.use('/webhook/infobip/sms', webhookLimiter); app.post('/webhook/infobip/sms', webhookHandler.handleIncomingSms);
- Helmet Middleware:
- Install and use Helmet to set security-related HTTP headers automatically.
- Installation:
npm install helmet - Usage in
server.js:javascriptconst helmet = require('helmet'); // Apply Helmet early in middleware chain app.use(helmet()); - Helmet sets headers like
Content-Security-Policy,Strict-Transport-Security, and removes theX-Powered-Byheader to reduce attack surface.
- Regular Security Audits: Run
npm auditregularly to check for known vulnerabilities in dependencies. Consider using tools like Snyk for continuous security monitoring.
8. Handling Special Cases
Real-world SMS involves nuances:
- Phone Number Formatting: Infobip expects and provides numbers in E.164 format (e.g.,
+14155552671,447123456789– note the plus sign is optional in API requests but the format is required). Ensure your application consistently handles this format when sending replies.- Recommended: Use
libphonenumber-jsto parse, validate, and format numbers. - Installation:
npm install libphonenumber-js - Example:
javascript
import parsePhoneNumber from 'libphonenumber-js'; const phoneNumber = parsePhoneNumber(sender); if (phoneNumber && phoneNumber.isValid()) { const e164Number = phoneNumber.format('E.164'); // '+14155552671' await infobipService.sendSms(e164Number, replyText); }
- Recommended: Use
- Character Limits & Concatenation: A standard SMS segment has 160 GSM-7 characters or 70 UCS-2 characters (for non-Latin alphabets or emojis). Longer messages are split into multiple segments (concatenated SMS).
- Be mindful of reply length to manage costs (Infobip charges per segment).
- Infobip handles the concatenation, but your application logic might need to consider length if splitting messages manually or truncating. The
smsCountfield in the inbound webhook indicates how many segments the incoming message used.
9. Implementing Performance Optimizations
For high-volume applications:
- Fast Webhook Responses: As mentioned, respond to Infobip webhooks immediately (
res.status(200).send()) before performing slow operations. - Background Processing: Offload time-consuming tasks (complex logic, multiple external API calls, database operations) to a background job queue (e.g., using libraries like BullMQ with Redis, or services like AWS SQS). The webhook handler simply adds a job to the queue, and a separate worker process handles it.
- Database Optimization: Use indexing on frequently queried columns (e.g., phone numbers, conversation IDs, timestamps). Optimize complex queries.
- Caching: Cache frequently accessed, rarely changing data (e.g., user profiles, configuration settings) using tools like Redis or Memcached to reduce database load.
- Load Testing: Use tools like
k6,Artillery, orApacheBenchto simulate high traffic volumes and identify bottlenecks in your webhook handler and API response times. - Profiling: Use Node.js built-in profiler or tools like Clinic.js to analyze CPU usage and memory allocation to pinpoint performance issues in your code.
Frequently Asked Questions (FAQ)
What is two-way SMS messaging?
Two-way SMS messaging allows both sending and receiving text messages programmatically. Unlike one-way SMS (broadcast only), two-way SMS enables interactive conversations where users can reply to messages and your application can respond automatically, creating chatbot-like experiences.
How do I get an Infobip API key?
Log in to your Infobip account and navigate to the API Keys section (usually under account settings or developer tools). Generate a new API key and copy it to your .env file as INFOBIP_API_KEY. Keep this key secure and never commit it to version control.
What phone number format does Infobip require?
Infobip expects phone numbers in E.164 international format: country code + national number without spaces or special characters (e.g., 447123456789 for a UK number). The optional + prefix is accepted but not required. Use libphonenumber-js to validate and format numbers correctly.
How do Infobip webhooks work?
When Infobip receives an SMS to your provisioned number, it sends an HTTP POST request to your configured webhook URL with a JSON payload containing the sender's number, message text, and metadata. Your Express application processes this webhook, extracts the data, and can send a reply using the Infobip SDK.
How can I secure my SMS webhook endpoint?
Implement multiple security layers: (1) Use webhook signature validation if Infobip provides it to verify request authenticity, (2) Apply rate limiting with express-rate-limit to prevent abuse, (3) Filter requests by Infobip's IP ranges, (4) Use Helmet middleware to set security headers, and (5) Validate all incoming payload data before processing.
What's the difference between MO and MT SMS?
MO (Mobile Originated) refers to messages sent from a user's phone to your application. MT (Mobile Terminated) refers to messages sent from your application to a user's phone. In two-way messaging, you receive MO messages via webhooks and send MT messages via the Infobip API.
How do I test webhooks locally?
Use ngrok or a similar tunneling tool to expose your local development server to the internet. Run ngrok http 3000 to get a public HTTPS URL, then configure this URL in your Infobip portal as your webhook endpoint. This allows Infobip to reach your local machine during development.
How much does SMS messaging with Infobip cost?
Infobip pricing varies by destination country and message volume. SMS messages are charged per segment (160 GSM-7 characters or 70 UCS-2 characters for Unicode). Check your Infobip account dashboard for specific pricing details and consider message length when designing replies to manage costs.