# How to Implement Bulk Messaging to Multiple Recipients with OpenWA: A Complete Guide

> Learn to implement bulk messaging with OpenWA's BulkMessageService. Send personalized messages efficiently with rate limiting, progress tracking, and cancellation.

- Repository: [Yudhi Armyndharis/OpenWA](https://github.com/rmyndharis/OpenWA)
- Tags: how-to-guide
- Published: 2026-05-21

---

**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`](https://github.com/rmyndharis/OpenWA/blob/main/src/modules/message/message.controller.ts#L82-L105), [`src/modules/message/bulk-message.service.ts#L37-L86`](https://github.com/rmyndharis/OpenWA/blob/main/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)](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)](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)](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`](https://github.com/rmyndharis/OpenWA/blob/main/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:

```bash
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`](https://github.com/rmyndharis/OpenWA/blob/main/sdk/javascript/src/index.ts)) provides a typed interface:

```javascript
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:

```javascript
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:

```bash
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`](https://github.com/rmyndharis/OpenWA/blob/main/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`](https://github.com/rmyndharis/OpenWA/blob/main/src/modules/message/bulk-message.service.ts#L62-L68), ensuring each recipient receives customized content while maintaining a single batch request structure.