Skip to main content
Once you’ve added contacts to your broadcast, trigger the send to start delivering messages.

Send Immediately

const broadcast = await zavu.broadcasts.send(broadcastId);

console.log(`Status: ${broadcast.status}`); // "sending"
console.log(`Started at: ${broadcast.startedAt}`);

Response

{
  "id": "brd_abc123",
  "name": "Weekly Newsletter",
  "status": "sending",
  "channel": "sms",
  "totalContacts": 1500,
  "pendingCount": 1500,
  "sendingCount": 0,
  "deliveredCount": 0,
  "failedCount": 0,
  "startedAt": "2024-01-15T10:30:00.000Z",
  "createdAt": "2024-01-15T10:00:00.000Z"
}

Schedule for Later

Schedule a broadcast to send at a specific time:
// Schedule for tomorrow at 9am UTC
const broadcast = await zavu.broadcasts.send(broadcastId, {
  scheduledAt: "2024-01-16T09:00:00.000Z",
});

console.log(`Status: ${broadcast.status}`); // "scheduled"
console.log(`Scheduled for: ${broadcast.scheduledAt}`);
Use scheduling to send campaigns at optimal times for your audience’s timezone. For example, schedule a morning message to arrive at 9am in the recipient’s local time.

Rescheduling a Broadcast

If you need to change the scheduled time, you can reschedule a broadcast that’s in scheduled status:
const broadcast = await zavu.broadcasts.reschedule(broadcastId, {
  scheduledAt: "2024-01-17T14:00:00.000Z",
});

console.log(`New scheduled time: ${broadcast.scheduledAt}`);
You can only reschedule broadcasts that are in scheduled status. The new time must be in the future.

Pre-Send Validation

Before sending, the API validates:
ValidationError if Failed
Status is draft or scheduledbroadcast_already_sending
At least 1 contact existsno_contacts
Sender is configured for channelsender_not_configured
Sufficient balance (SMS/Email)insufficient_balance
Template exists and approved (WhatsApp)template_not_approved

Balance Reservation

When you trigger a broadcast, Zavu reserves the estimated cost from your balance. This ensures funds are available for the entire campaign.
Available Balance: $100.00
Broadcast Cost:     $25.00 (estimated)
                    ───────
Reserved Balance:   $25.00 (blocked)
Usable Balance:     $75.00

How It Works

1

Cost Estimation

When you call /send, Zavu calculates the estimated cost based on contacts and channel rates
2

Balance Check

If your available balance (excluding other reservations) is less than the estimated cost, the request fails
3

Reservation Created

The estimated amount is reserved and cannot be used for other broadcasts or messages
4

Messages Sent

As messages are delivered, actual costs are deducted from the reservation
5

Reservation Released

When the broadcast completes or is cancelled, any unused reserved funds are released back to your available balance

Checking Your Balance

const billing = await zavu.billing.get();

console.log(`Total Balance: $${billing.balance}`);
console.log(`Reserved: $${billing.reservedBalance}`);
console.log(`Available: $${billing.availableBalance}`);

Response with Reservation

When a broadcast starts, the response includes reservation details:
{
  "id": "brd_abc123",
  "status": "sending",
  "totalContacts": 1500,
  "estimatedCost": 25.00,
  "reservedAmount": 25.00,
  "startedAt": "2024-01-15T10:30:00.000Z"
}

Insufficient Balance

If you don’t have enough available balance:
{
  "code": "insufficient_balance",
  "message": "Insufficient balance for this broadcast",
  "details": {
    "estimatedCost": 25.00,
    "availableBalance": 10.50,
    "reservedBalance": 15.00,
    "totalBalance": 25.50,
    "requiredAmount": 14.50
  }
}
Reserved funds are blocked until the broadcast completes or is cancelled. Plan your campaigns to avoid blocking funds needed for other messaging.

Reservation Release

ScenarioWhat Happens
Broadcast completesUnused reservation released immediately
Broadcast cancelledFull reservation released (minus already-sent messages)
Actual cost < estimatedDifference released when broadcast completes
Actual cost > estimatedAdditional amount charged from available balance
WhatsApp message costs are billed directly by Meta, not deducted from your Zavu balance. No reservation is created for WhatsApp-only broadcasts.

Cancelling a Broadcast

Cancel a broadcast that hasn’t completed:
const broadcast = await zavu.broadcasts.cancel(broadcastId);

console.log(`Status: ${broadcast.status}`); // "cancelled"

What Happens on Cancel

Original StatusAction
draftBroadcast deleted
scheduledReturns to draft, can be rescheduled
sendingStops new messages, pending contacts marked as skipped
Messages already queued or sent cannot be cancelled. Only pending contacts are skipped.

Delivery Rate Limits

Zavu automatically rate-limits delivery to prevent carrier throttling:
ChannelRateNotes
SMS10/secondShared across all projects
WhatsApp60/secondPer WhatsApp Business Account
Email14/secondVia AWS SES
For a 10,000 contact SMS broadcast:
  • Estimated time: ~17 minutes (10,000 / 10 per second)
Large broadcasts are processed in the background. Use the progress endpoint to monitor delivery.

Cost Estimation

Get estimated cost before sending:
const broadcast = await zavu.broadcasts.get(broadcastId);

console.log(`Estimated cost: $${broadcast.estimatedCost}`);
console.log(`Total contacts: ${broadcast.totalContacts}`);
ChannelCost Model
SMSPer-message (varies by country)
WhatsAppFree (24h window) or template pricing
Email$0.02 per message
Plus MAU (Monthly Active User) charge of $0.10 per unique recipient per month.

Complete Workflow Example

import Zavudev from '@zavudev/sdk';

const zavu = new Zavudev({
  apiKey: process.env['ZAVUDEV_API_KEY'], // This is the default and can be omitted
});

async function sendPromotion(contacts: Array<{phone: string, name: string}>) {
  // 1. Create broadcast
  const broadcast = await zavu.broadcasts.create({
    name: "Holiday Sale 2024",
    channel: "sms",
    text: "Hi {{name}}! Our holiday sale is live. Get 30% off with code HOLIDAY30. Shop now!",
  });

  console.log(`Created broadcast: ${broadcast.id}`);

  // 2. Add contacts in batches
  const BATCH_SIZE = 1000;
  for (let i = 0; i < contacts.length; i += BATCH_SIZE) {
    const batch = contacts.slice(i, i + BATCH_SIZE);

    const result = await zavu.broadcasts.addContacts(broadcast.id, {
      contacts: batch.map(c => ({
        recipient: c.phone,
        templateVariables: { name: c.name },
      })),
    });

    console.log(`Batch ${Math.floor(i / BATCH_SIZE) + 1}: Added ${result.added} contacts`);
  }

  // 3. Check estimated cost
  const updated = await zavu.broadcasts.get(broadcast.id);
  console.log(`Estimated cost: $${updated.estimatedCost}`);
  console.log(`Total contacts: ${updated.totalContacts}`);

  // 4. Send the broadcast
  await zavu.broadcasts.send(broadcast.id);
  console.log("Broadcast started!");

  // 5. Poll for progress
  let complete = false;
  while (!complete) {
    await new Promise(r => setTimeout(r, 5000)); // Wait 5 seconds

    const progress = await zavu.broadcasts.getProgress(broadcast.id);
    console.log(`Progress: ${progress.percentComplete}% (${progress.delivered} delivered, ${progress.failed} failed)`);

    if (progress.status === "completed" || progress.status === "cancelled") {
      complete = true;
    }
  }

  console.log("Broadcast complete!");
}

Next Steps

Tracking Progress

Monitor delivery progress in real-time