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:

  1. Request Entry: The MessageController.sendBulk() method receives POST requests at /sessions/:sessionId/messages/send-bulk with a SendBulkMessageDto payload containing the recipient list, message content, and processing options.

  2. Batch Initialization: The BulkMessageService.createBatch() method validates the active session, generates or accepts a batch ID, and stores a MessageBatch record in the database.

  3. Asynchronous Processing: The service immediately invokes processBatch(), which iterates through the message list and dispatches each via the IWhatsAppEngine interface (implemented by adapters like whatsapp-web-js).

  4. 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 optional variables
  • options: Configuration object for delayBetweenMessages (milliseconds), randomizeDelay (boolean), and stopOnError (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 processingBatches Map 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 IWhatsAppEngine interface—such as sendTextMessage(), sendImageMessage(), etc.—based on the type field 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 containing total, sent, failed, and cancelled counts
  • results: 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-bulk endpoint handled by MessageController.sendBulk() to submit batches with validated SendBulkMessageDto payloads.

  • Processing Engine: The BulkMessageService.createBatch() and processBatch() methods manage asynchronous dispatch through the IWhatsAppEngine interface, supporting text, image, video, audio, and document types.

  • State Persistence: The MessageBatch entity 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/:batchId and cancellation via POST .../batch/:batchId/cancel provide 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →