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.

Content Review

All broadcasts go through an automated AI content review before sending. This protects both you and your recipients from policy violations.

How Content Review Works

1

Submit for Sending

When you call /send, the broadcast enters pending_review status
2

AI Analysis

Content is analyzed for spam, phishing, prohibited content, and policy violations
3

Decision

Broadcast is either approved and proceeds, or rejected with feedback

Review Statuses

StatusDescription
pending_reviewContent being analyzed
approvedReview passed, broadcast proceeds
rejectedContent rejected, needs editing
escalatedSent to human review
rejected_finalRejected by human review (cannot be appealed)

Handling Rejections

If your broadcast is rejected, you’ll receive details about the issue:
{
  "id": "brd_abc123",
  "status": "rejected",
  "reviewResult": {
    "score": 0.35,
    "categories": ["spam_indicators", "missing_opt_out"],
    "reasoning": "Message contains promotional content without unsubscribe option",
    "flaggedContent": ["limited time offer", "act now"],
    "reviewedAt": "2024-01-15T10:31:00.000Z"
  },
  "reviewAttempts": 1
}

Editing and Retrying

After rejection, edit your broadcast content and retry the review:
// 1. Get the rejected broadcast
const broadcast = await zavu.broadcasts.get(broadcastId);

if (broadcast.status === "rejected") {
  console.log("Rejection reason:", broadcast.reviewResult?.reasoning);

  // 2. Edit the content to address issues
  await zavu.broadcasts.update(broadcastId, {
    text: "Thanks for being a customer! Check out our latest products. Reply STOP to unsubscribe.",
  });

  // 3. Retry the review
  const updated = await zavu.broadcasts.retryReview(broadcastId);
  console.log("New status:", updated.status); // "pending_review"
}
You have a maximum of 3 review attempts per broadcast. After 3 rejections, you must escalate to manual review or create a new broadcast.

Escalating to Manual Review

If you believe your content was incorrectly rejected, escalate to the Zavu team:
const broadcast = await zavu.broadcasts.escalate(broadcastId);

console.log("Status:", broadcast.status); // "escalated"
// Zavu team will review and update status to "approved" or "rejected_final"
Manual reviews are typically processed within 24 hours during business days. You’ll receive a webhook notification when the review is complete.

Content Guidelines

To avoid rejections, ensure your broadcasts:
RequirementExample
Include opt-out instructions”Reply STOP to unsubscribe”
Avoid spam trigger wordsAvoid “FREE”, “ACT NOW”, “LIMITED TIME” in all caps
Identify your businessInclude your company name
Don’t impersonateNever pretend to be banks, government, etc.
Avoid URL shortenersUse full URLs (see URL Verification)

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