Once you’ve added contacts to your broadcast, trigger the send to start delivering messages.
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:
Validation Error if Failed Status is draft or scheduled broadcast_already_sendingAt least 1 contact exists no_contactsSender is configured for channel sender_not_configuredSufficient balance (SMS/Email) insufficient_balanceTemplate 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
Cost Estimation
When you call /send, Zavu calculates the estimated cost based on contacts and channel rates
Balance Check
If your available balance (excluding other reservations) is less than the estimated cost, the request fails
Reservation Created
The estimated amount is reserved and cannot be used for other broadcasts or messages
Messages Sent
As messages are delivered, actual costs are deducted from the reservation
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
Scenario What Happens Broadcast completes Unused reservation released immediately Broadcast cancelled Full reservation released (minus already-sent messages) Actual cost < estimated Difference released when broadcast completes Actual cost > estimated Additional 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 Status Action 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:
Channel Rate Notes SMS 10/second Shared across all projects WhatsApp 60/second Per WhatsApp Business Account Email 14/second Via 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 } ` );
Channel Cost Model SMS Per-message (varies by country) WhatsApp Free (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