Track your broadcast’s delivery progress with instant updates on sent, delivered, and failed messages.
Get Progress
TypeScript
Python
Ruby
Go
PHP
cURL
const progress = await zavu . broadcasts . getProgress ( broadcastId );
console . log ( `Status: ${ progress . status } ` );
console . log ( `Progress: ${ progress . percentComplete } %` );
console . log ( `Delivered: ${ progress . delivered } / ${ progress . total } ` );
console . log ( `Failed: ${ progress . failed } ` );
Response
{
"broadcastId" : "brd_abc123" ,
"status" : "sending" ,
"total" : 5000 ,
"pending" : 2500 ,
"sending" : 100 ,
"delivered" : 2350 ,
"failed" : 50 ,
"skipped" : 0 ,
"percentComplete" : 48.0 ,
"estimatedCost" : 75.00 ,
"reservedAmount" : 75.00 ,
"actualCost" : 35.25 ,
"startedAt" : "2024-01-15T10:30:00.000Z" ,
"estimatedCompletionAt" : "2024-01-15T10:45:00.000Z"
}
Progress Fields
Field Type Description broadcastIdstring Broadcast identifier statusstring Current broadcast status totalnumber Total contacts in broadcast pendingnumber Not yet queued for sending sendingnumber Currently being sent deliverednumber Successfully delivered failednumber Failed to deliver skippednumber Skipped (broadcast cancelled) percentCompletenumber Percentage complete (0-100) estimatedCostnumber Estimated total cost (USD) reservedAmountnumber Amount reserved from balance (USD) actualCostnumber Actual cost so far (USD) startedAtstring When sending started estimatedCompletionAtstring Estimated completion time
Polling for Updates
Poll the progress endpoint at regular intervals:
async function waitForCompletion ( broadcastId : string ) {
const POLL_INTERVAL = 5000 ; // 5 seconds
while ( true ) {
const progress = await zavu . broadcasts . getProgress ( broadcastId );
console . log ( `[ ${ new Date (). toISOString () } ] ${ progress . percentComplete } % complete` );
console . log ( ` Delivered: ${ progress . delivered } ` );
console . log ( ` Failed: ${ progress . failed } ` );
console . log ( ` Pending: ${ progress . pending } ` );
if ( progress . status === "completed" || progress . status === "cancelled" ) {
return progress ;
}
await new Promise ( resolve => setTimeout ( resolve , POLL_INTERVAL ));
}
}
const finalProgress = await waitForCompletion ( broadcastId );
console . log ( `Broadcast ${ finalProgress . status } !` );
For large broadcasts, use longer polling intervals (10-30 seconds) to reduce API calls. The progress updates are efficient and don’t scan all contacts.
Status Transitions
draft → scheduled → sending → completed
↓ ↓
cancelled cancelled
Status percentCompleteDescription draft0% Still adding contacts scheduled0% Waiting for scheduled time sending0-99% Messages being delivered completed100% All messages processed cancelledvaries Stopped before completion
Get detailed status for each contact:
// Get failed contacts
const failed = await zavu . broadcasts . listContacts ( broadcastId , {
status: "failed" ,
limit: 100 ,
});
for ( const contact of failed . items ) {
console . log ( ` ${ contact . recipient } : ${ contact . errorMessage } ` );
}
{
"items" : [
{
"id" : "bc_abc123" ,
"recipient" : "+14155551234" ,
"status" : "failed" ,
"errorCode" : "30003" ,
"errorMessage" : "Unreachable destination" ,
"processedAt" : "2024-01-15T10:35:00.000Z"
}
],
"nextCursor" : null
}
Broadcast Analytics
After completion, get final statistics:
const broadcast = await zavu . broadcasts . get ( broadcastId );
console . log ( "=== Broadcast Report ===" );
console . log ( `Name: ${ broadcast . name } ` );
console . log ( `Status: ${ broadcast . status } ` );
console . log ( `Total: ${ broadcast . totalContacts } ` );
console . log ( `Delivered: ${ broadcast . deliveredCount } ` );
console . log ( `Failed: ${ broadcast . failedCount } ` );
console . log ( `Delivery Rate: ${ ( broadcast . deliveredCount / broadcast . totalContacts * 100 ). toFixed ( 1 ) } %` );
console . log ( `Duration: ${ calculateDuration ( broadcast . startedAt , broadcast . completedAt ) } ` );
console . log ( `Actual Cost: $ ${ broadcast . actualCost } ` );
Error Analysis
Common failure reasons:
Error Code Description Recommendation 30003Unreachable destination Invalid or disconnected number 30004Message blocked Content filtered by carrier 30005Unknown destination Number doesn’t exist 30006Landline destination Cannot SMS landlines 30007Carrier violation Message rejected by carrier rate_limitedToo many messages Temporary, will retry insufficient_balanceOut of credits Top up balance
async function exportFailedContacts ( broadcastId : string ) {
const failed : string [] = [];
let cursor : string | undefined ;
do {
const result = await zavu . broadcasts . listContacts ( broadcastId , {
status: "failed" ,
limit: 100 ,
cursor ,
});
for ( const contact of result . items ) {
failed . push ( ` ${ contact . recipient } , ${ contact . errorCode } , ${ contact . errorMessage } ` );
}
cursor = result . nextCursor ?? undefined ;
} while ( cursor );
// Write to CSV
const csv = [ "recipient,error_code,error_message" , ... failed ]. join ( " \n " );
fs . writeFileSync ( "failed_contacts.csv" , csv );
console . log ( `Exported ${ failed . length } failed contacts` );
}
Real-time Updates with Webhooks
For real-time updates without polling, configure webhooks:
// In your webhook handler
app . post ( "/webhooks/zavu" , ( req , res ) => {
const event = req . body ;
if ( event . type === "broadcast.completed" ) {
console . log ( `Broadcast ${ event . data . broadcastId } completed!` );
console . log ( `Delivered: ${ event . data . deliveredCount } ` );
console . log ( `Failed: ${ event . data . failedCount } ` );
}
res . status ( 200 ). send ( "OK" );
});
Broadcast Events
Event Description broadcast.startedBroadcast started sending broadcast.completedAll messages processed broadcast.cancelledBroadcast was cancelled
Best Practices
Use appropriate polling intervals
5 seconds for small broadcasts, 30 seconds for large ones
Handle partial failures
Some contacts may fail while others succeed - check both counts
Export and analyze failures
Review failed contacts to improve your contact list quality
Set up webhooks for production
Avoid continuous polling by using webhook notifications
Complete Monitoring 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 monitorBroadcast ( broadcastId : string ) {
console . log ( `Monitoring broadcast: ${ broadcastId } \n ` );
let lastDelivered = 0 ;
let lastFailed = 0 ;
while ( true ) {
const progress = await zavu . broadcasts . getProgress ( broadcastId );
// Calculate rate
const newDelivered = progress . delivered - lastDelivered ;
const newFailed = progress . failed - lastFailed ;
console . log ( `[ ${ progress . percentComplete . toFixed ( 1 ) } %] ` +
`Delivered: ${ progress . delivered } (+ ${ newDelivered } ) | ` +
`Failed: ${ progress . failed } (+ ${ newFailed } ) | ` +
`Pending: ${ progress . pending } ` );
lastDelivered = progress . delivered ;
lastFailed = progress . failed ;
if ( progress . status === "completed" ) {
console . log ( " \n === Broadcast Complete ===" );
console . log ( `Total: ${ progress . total } ` );
console . log ( `Delivered: ${ progress . delivered } ( ${ ( progress . delivered / progress . total * 100 ). toFixed ( 1 ) } %)` );
console . log ( `Failed: ${ progress . failed } ( ${ ( progress . failed / progress . total * 100 ). toFixed ( 1 ) } %)` );
break ;
}
if ( progress . status === "cancelled" ) {
console . log ( " \n === Broadcast Cancelled ===" );
console . log ( `Skipped: ${ progress . skipped } contacts` );
break ;
}
await new Promise ( r => setTimeout ( r , 5000 ));
}
}
monitorBroadcast ( "brd_abc123" );