How to Implement Bulk Messaging to Multiple Recipients with OpenWA: A Complete Guide
OpenWA provides a dedicated bulk messaging service that processes message batches asynchronously through the BulkMessageService, allowing you to send personalized messages to multiple recipients with built-in rate limiting, progress tracking, and cancellation support.
The OpenWA library (available at rmyndharis/OpenWA) extends whatsapp-web-js with a robust NestJS-based architecture for handling high-volume WhatsApp messaging. Understanding how to implement bulk messaging to multiple recipients with OpenWA requires familiarity with its batch processing pipeline, which leverages TypeORM for persistence and an abstract engine interface for message dispatch.
Understanding the Bulk Messaging Architecture
OpenWA’s bulk messaging flow follows a stateless, asynchronous pattern designed for reliability at scale. The system accepts a batch of messages, persists metadata via the MessageBatch entity, and processes the queue in the background while exposing REST endpoints for monitoring and control.
The flow begins at the controller layer and moves through validation, persistence, and asynchronous execution:
-
Request Entry: The
MessageController.sendBulk()method receivesPOSTrequests at/sessions/:sessionId/messages/send-bulkwith aSendBulkMessageDtopayload containing the recipient list, message content, and processing options. -
Batch Initialization: The
BulkMessageService.createBatch()method validates the active session, generates or accepts a batch ID, and stores aMessageBatchrecord in the database. -
Asynchronous Processing: The service immediately invokes
processBatch(), which iterates through the message list and dispatches each via theIWhatsAppEngineinterface (implemented by adapters likewhatsapp-web-js). -
State Management: Progress counters update after each send, with status persisted every 10 messages (or at completion) to balance performance against durability.
Source references: src/modules/message/message.controller.ts#L82-L105, src/modules/message/bulk-message.service.ts#L37-L86.
Creating a Message Batch via the API
To initiate bulk messaging to multiple recipients with OpenWA, you must construct a request following the SendBulkMessageDto schema. This DTO validates message items, optional template variables, and processing behavior such as delays and error handling.
The endpoint requires:
- sessionId: The active WhatsApp session identifier in the URL path
- batchId: Optional custom identifier (auto-generated if omitted)
- messages: Array of message objects specifying
chatId,type(text, image, video, audio, document),content, and optionalvariables - options: Configuration object for
delayBetweenMessages(milliseconds),randomizeDelay(boolean), andstopOnError(boolean)
The controller returns HTTP 202 Accepted immediately, indicating the batch is queued for processing. The response payload includes the batchId, initial status (typically PENDING or PROCESSING), totalMessages count, and a statusUrl for polling.
Source reference: [src/modules/message/dto/bulk-message.dto.ts](https://github.com/rmyndharis/OpenWA/blob/main/src/modules/message/dto/bulk-message.dto.ts).
Processing Messages Asynchronously
Once persisted, the BulkMessageService executes processBatch() to handle the actual message dispatch. This method operates outside the HTTP request lifecycle, allowing the API to respond immediately while processing continues in the background.
Key implementation details from [src/modules/message/bulk-message.service.ts](https://github.com/rmyndharis/OpenWA/blob/main/src/modules/message/bulk-message.service.ts):
-
Cancellation Check: At each iteration, the service checks the
processingBatchesMap to detect if a cancellation request has been issued (lines 49-55). -
Template Variable Substitution: The service interpolates variables like
{{name}}into message content before dispatch (lines 62-68). -
Engine Abstraction: The service calls type-specific methods on the
IWhatsAppEngineinterface—such assendTextMessage(),sendImageMessage(), etc.—based on thetypefield in each message item (lines 50-86). -
Progress Tracking: After each successful or failed send, the service updates internal counters and appends results to the batch record. It persists the full state every 10 messages or upon completion to minimize database writes while maintaining recoverability (lines 99-104).
The MessageBatch entity ([src/modules/message/entities/message-batch.entity.ts](https://github.com/rmyndharis/OpenWA/blob/main/src/modules/message/entities/message-batch.entity.ts)) stores metadata including the message list, processing options, progress counters, per-message results, and final status.
Monitoring Batch Status and Cancellation
OpenWA exposes dedicated endpoints for supervising active batches and aborting operations when necessary.
Status Retrieval:
Send a GET request to /sessions/:sessionId/messages/batch/:batchId to retrieve current progress. The response includes:
status: Current state (PROCESSING,COMPLETED,CANCELLED,FAILED)progress: Object containingtotal,sent,failed, andcancelledcountsresults: Array of per-message outcomes with timestamps and error details if applicable
Cancellation:
To abort a running batch, send a POST to /sessions/:sessionId/messages/batch/:batchId/cancel. This flips the internal processingBatches flag and updates the database record to CANCELLED. Any in-flight messages complete, but pending items are skipped.
Source references: src/modules/message/message.controller.ts#L124-L163.
Practical Implementation Examples
Sending a Mixed Batch via cURL
The following example sends a text message with variables and an image message to different recipients, utilizing a 4-second randomized delay between sends:
curl -X POST "https://your-openwa-host/api/sessions/abc123/messages/send-bulk" \
-H "Content-Type: application/json" \
-d '{
"batchId": "promo_2024",
"messages": [
{
"chatId": "628123456789@c.us",
"type": "text",
"content": { "text": "Hello {{name}}! 🎉" },
"variables": { "name": "John" }
},
{
"chatId": "628987654321@c.us",
"type": "image",
"content": {
"image": { "url": "https://example.com/promo.jpg" },
"caption": "Check out our new product"
}
}
],
"options": {
"delayBetweenMessages": 4000,
"randomizeDelay": true,
"stopOnError": false
}
}'
Using the JavaScript SDK
For Node.js applications, the OpenWA SDK (located at sdk/javascript/src/index.ts) provides a typed interface:
import OpenWA from '@openwa/sdk';
const client = new OpenWA({ baseUrl: 'https://your-openwa-host/api' });
const batch = await client.sendBulk('abc123', {
batchId: 'campaign1',
messages: [
{
chatId: '628123456789@c.us',
type: 'text',
content: { text: 'Hey {{firstName}}! Welcome.' },
variables: { firstName: 'Alice' },
},
{
chatId: '628987654321@c.us',
type: 'document',
content: {
document: { url: 'https://example.com/menu.pdf' },
caption: 'Our latest menu'
}
}
],
options: {
delayBetweenMessages: 3000,
stopOnError: false
}
});
console.log('Batch initiated:', batch.batchId);
Polling for Completion Status
Monitor delivery progress by querying the status endpoint:
import fetch from 'node-fetch';
async function pollBatchStatus(sessionId, batchId, baseUrl) {
const response = await fetch(
`${baseUrl}/sessions/${sessionId}/messages/batch/${batchId}`
);
const data = await response.json();
console.log(`Status: ${data.status}`);
console.log(`Progress: ${data.progress.sent}/${data.progress.total}`);
console.log('Results:', data.results);
return data;
}
// Poll every 5 seconds until complete
const interval = setInterval(async () => {
const status = await pollBatchStatus('abc123', 'promo_2024', 'https://your-openwa-host/api');
if (['COMPLETED', 'CANCELLED', 'FAILED'].includes(status.status)) {
clearInterval(interval);
}
}, 5000);
Cancelling a Batch
If you need to abort a campaign in progress:
curl -X POST "https://your-openwa-host/api/sessions/abc123/messages/batch/promo_2024/cancel"
The response will show status: "CANCELLED" and the progress.cancelled count will reflect any messages skipped due to the abort request.
Summary
Implementing bulk messaging to multiple recipients with OpenWA involves orchestrating several architectural components:
-
Entry Point: Use the
POST /sessions/:sessionId/messages/send-bulkendpoint handled byMessageController.sendBulk()to submit batches with validatedSendBulkMessageDtopayloads. -
Processing Engine: The
BulkMessageService.createBatch()andprocessBatch()methods manage asynchronous dispatch through theIWhatsAppEngineinterface, supporting text, image, video, audio, and document types. -
State Persistence: The
MessageBatchentity tracks metadata, progress counters, and per-message results in the database, with optimized writes every 10 messages. -
Operational Control: Real-time monitoring via
GET .../batch/:batchIdand cancellation viaPOST .../batch/:batchId/cancelprovide production-grade observability and safety controls. -
Template Support: Variable substitution (e.g.,
{{name}}) occurs during processing, enabling personalized bulk messaging without multiple API calls.
Frequently Asked Questions
What message types does OpenWA bulk messaging support?
OpenWA supports text, image, video, audio, and document message types within a single batch. The BulkMessageService maps each message's type field to the corresponding method on the IWhatsAppEngine interface, such as sendTextMessage() or sendImageMessage(), as implemented in src/modules/message/bulk-message.service.ts#L50-L86.
How does OpenWA handle rate limiting in bulk messaging?
Rate limiting is managed through the options object in your SendBulkMessageDto. You can specify delayBetweenMessages (in milliseconds) and enable randomizeDelay to introduce jitter between sends. The processBatch() method enforces these delays by pausing execution between each dispatch, helping prevent WhatsApp rate limits or blocks without requiring external queue systems.
Can I cancel a bulk message batch after sending it?
Yes. OpenWA supports cancellation via the POST /sessions/:sessionId/messages/batch/:batchId/cancel endpoint. When invoked, the controller method flips a flag in the processingBatches Map checked by processBatch() during each iteration. The batch status updates to CANCELLED in the database, and any pending messages are skipped, though messages already in-flight (mid-send) may still complete.
How do template variables work in OpenWA bulk messages?
Template variables enable personalization by allowing placeholder substitution before dispatch. Include variables in your message content using double curly braces (e.g., "Hello {{firstName}}"), then provide a variables object mapping keys to values (e.g., { "firstName": "Alice" }). The BulkMessageService resolves these placeholders during the processBatch() execution cycle at src/modules/message/bulk-message.service.ts#L62-L68, ensuring each recipient receives customized content while maintaining a single batch request structure.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →