Get Progress
const progress = await zavu.broadcasts.progress(broadcastId);
console.log(`Status: ${progress.status}`);
console.log(`Progress: ${progress.percentComplete}%`);
console.log(`Delivered: ${progress.delivered}/${progress.total}`);
console.log(`Failed: ${progress.failed}`);
progress = zavu.broadcasts.progress(broadcast_id)
print(f"Status: {progress.status}")
print(f"Progress: {progress.percent_complete}%")
print(f"Delivered: {progress.delivered}/{progress.total}")
print(f"Failed: {progress.failed}")
progress = client.broadcasts.progress(broadcast_id)
puts "Status: #{progress.status}"
puts "Progress: #{progress.percent_complete}%"
puts "Delivered: #{progress.delivered}/#{progress.total}"
puts "Failed: #{progress.failed}"
progress, _ := client.Broadcasts.Progress(context.TODO(), broadcastID)
fmt.Printf("Status: %s\n", progress.Status)
fmt.Printf("Progress: %v%%\n", progress.PercentComplete)
fmt.Printf("Delivered: %d/%d\n", progress.Delivered, progress.Total)
fmt.Printf("Failed: %d\n", progress.Failed)
$progress = $client->broadcasts->progress($broadcastId);
echo "Status: {$progress->status}\n";
echo "Progress: {$progress->percentComplete}%\n";
echo "Delivered: {$progress->delivered}/{$progress->total}\n";
echo "Failed: {$progress->failed}\n";
curl https://api.zavu.dev/v1/broadcasts/{broadcastId}/progress \
-H "Authorization: Bearer $ZAVUDEV_API_KEY"
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 |
|---|---|---|
broadcastId | string | Broadcast identifier |
status | string | Current broadcast status |
total | number | Total contacts in broadcast |
pending | number | Not yet queued for sending |
sending | number | Currently being sent |
delivered | number | Successfully delivered |
failed | number | Failed to deliver |
skipped | number | Skipped (broadcast cancelled) |
percentComplete | number | Percentage complete (0-100) |
estimatedCost | number | Estimated total cost (USD) |
reservedAmount | number | Amount reserved from balance (USD) |
actualCost | number | Actual cost so far (USD) |
startedAt | string | When sending started |
estimatedCompletionAt | string | 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.progress(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 | percentComplete | Description |
|---|---|---|
draft | 0% | Still adding contacts |
scheduled | 0% | Waiting for scheduled time |
sending | 0-99% | Messages being delivered |
completed | 100% | All messages processed |
cancelled | varies | Stopped before completion |
Viewing Individual Contacts
Get detailed status for each contact:// Get failed contacts
const failed = await zavu.broadcasts.contacts.list(broadcastId, {
status: "failed",
limit: 100,
});
for (const contact of failed.items) {
console.log(`${contact.recipient}: ${contact.errorMessage}`);
}
Contact Status Response
{
"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.retrieve(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.status_changedAt)}`);
console.log(`Actual Cost: $${broadcast.actualCost}`);
Error Analysis
Common failure reasons:| Error Code | Description | Recommendation |
|---|---|---|
30003 | Unreachable destination | Invalid or disconnected number |
30004 | Message blocked | Content filtered by carrier |
30005 | Unknown destination | Number doesn’t exist |
30006 | Landline destination | Cannot SMS landlines |
30007 | Carrier violation | Message rejected by carrier |
rate_limited | Too many messages | Temporary, will retry |
insufficient_balance | Out of credits | Top up balance |
Export Failed Contacts
async function exportFailedContacts(broadcastId: string) {
const failed: string[] = [];
let cursor: string | undefined;
do {
const result = await zavu.broadcasts.contacts.list(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.status_changed") {
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.started | Broadcast started sending |
broadcast.status_changed | All messages processed |
broadcast.cancelled | Broadcast was cancelled |
Best Practices
1
Use appropriate polling intervals
5 seconds for small broadcasts, 30 seconds for large ones
2
Handle partial failures
Some contacts may fail while others succeed - check both counts
3
Export and analyze failures
Review failed contacts to improve your contact list quality
4
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.progress(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");
