convoup Docs
Docs / Guides / Bulk Sending

Bulk Sending

For sending templates to many customers, iterate over your customer list with per-customer error handling.

Basic Bulk Send

TypeScript
import { Convoup, ConvoupError } from 'convoup';

const client = new Convoup({
  apiKey: process.env.CONVOUP_API_KEY!,
});

const customers = [
  { phone: '+918851479441', invoiceId: 'INV-001', amount: 'Rs. 2,500', pdfUrl: 'https://api.example.com/invoices/INV-001.pdf' },
  { phone: '+919876543210', invoiceId: 'INV-002', amount: 'Rs. 4,800', pdfUrl: 'https://api.example.com/invoices/INV-002.pdf' },
  // ... 498 more customers
];

let sent = 0;
let failed = 0;

for (const customer of customers) {
  try {
    await client.sendInvoice({
      to: customer.phone,
      template: 'test_welcome_template',
      params: [customer.invoiceId, customer.amount],
      pdfUrl: customer.pdfUrl,
    });
    sent++;
  } catch (err) {
    failed++;
    if (err instanceof ConvoupError) {
      console.error(`Failed for ${customer.phone}: ${err.code} - ${err.message}`);
      if (err.code === 'TEMPLATE_NOT_FOUND') {
        console.error('Template not found. Aborting batch.');
        break;
      }
    } else {
      console.error(`Unexpected error for ${customer.phone}:`, err);
    }
  }
}

console.log(`Done. Sent: ${sent}, Failed: ${failed}`);

Bulk Send with Rate Limiting

Add delays between sends to avoid hitting rate limits:

TypeScript
function delay(ms: number): Promise<void> {
  return new Promise(resolve => setTimeout(resolve, ms));
}

for (const customer of customers) {
  try {
    await client.sendInvoice({
      to: customer.phone,
      template: 'test_welcome_template',
      params: [customer.invoiceId, customer.amount],
      pdfUrl: customer.pdfUrl,
    });
    sent++;
  } catch (err) {
    failed++;
    if (err instanceof ConvoupError && err.code === 'TEMPLATE_NOT_FOUND') break;
  }

  // 100ms delay (conservative; platform supports up to 100 msg/sec for campaigns)
  await delay(100);
}
Rate Limits

Campaign dispatch: Up to 100 messages/second per WABA (configurable 1–100 in campaign settings).

Live chat: Up to 10 messages/second per WABA.

Follow-up messages: Up to 5 messages/second per WABA.

HTTP API: 1,000 requests per 15-minute window. Auth endpoints: 20 req/15min.

Meta WABA tiers: Your daily send limit depends on your WABA tier (250 / 1K / 10K / 100K / Unlimited messages per day). The platform enforces Meta’s cap automatically.

The SDK examples below use conservative delays. Adjust based on your tier and 429 responses.

Bulk Send with Concurrency Control

For large batches, send multiple messages in parallel with a concurrency limit:

TypeScript
async function bulkSendWithConcurrency(
  customers: Customer[],
  concurrency: number,
): Promise<{ sent: number; failed: number }> {
  let sent = 0;
  let failed = 0;

  const chunks: Customer[][] = [];
  for (let i = 0; i < customers.length; i += concurrency) {
    chunks.push(customers.slice(i, i + concurrency));
  }

  for (const chunk of chunks) {
    const results = await Promise.allSettled(
      chunk.map(c =>
        client.sendInvoice({
          to: c.phone,
          template: 'test_welcome_template',
          params: [c.invoiceId, c.amount],
          pdfUrl: c.pdfUrl,
        })
      )
    );

    for (const result of results) {
      if (result.status === 'fulfilled') sent++;
      else failed++;
    }

    // 500ms between chunks to stay within rate limits
    await delay(500);
  }

  return { sent, failed };
}

const results = await bulkSendWithConcurrency(customers, 10);
console.log(`Sent: ${results.sent}, Failed: ${results.failed}`);

Concurrency Flow

graph TD A[Customer List] --> B[Split into chunks] B --> C[Process chunk with Promise.allSettled] C --> D{More chunks?} D -->|Yes| E[Delay between chunks] E --> C D -->|No| F[Return sent/failed counts]

Error Handling Strategy

Error CodeAction
TEMPLATE_NOT_FOUNDAbort the entire batch - template name is wrong
TEMPLATE_PARAM_MISMATCHLog and continue - params don't match for this customer
RECIPIENT_SUPPRESSEDLog and continue - number is blocked
RATE_LIMITEDDelay and retry
Network errorLog and continue - may be transient

Next Steps