Tracking Progress
Monitor broadcast delivery in real-time
Track your broadcast's delivery progress with instant updates on sent, delivered, and failed messages.
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}`);
Response
JSON{ "broadcastId": "brd_abc123", "status": "sending", "total": 5000, "pending": 2500, "sending": 100, "sent": 350, "delivered": 2000, "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 |
sent | number | Accepted by the provider, delivery not confirmed yet |
delivered | number | Delivery to the device confirmed |
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:
TypeScriptasync 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}!`);
Status Transitions
Codedraft → 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
Each recipient has one of these statuses:
| Status | Meaning |
|---|---|
pending, queued, sending | Not handed to the provider yet |
sent | The provider accepted the message. Delivery is not confirmed yet |
delivered | The channel confirmed delivery to the device. A WhatsApp read receipt also counts |
failed | Not delivered. A sent or delivered recipient moves here if the provider reports a failure later |
skipped | Not sent: the recipient opted out of the channel, or the broadcast was cancelled first |
Channels that never report delivery leave their recipients at sent. Completion counts sent recipients as done, so a completed broadcast can still have sent recipients that move to delivered or failed afterwards.
Get detailed status for each contact:
TypeScript// 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
JSON{ "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:
TypeScriptconst 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
TypeScriptasync 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:
TypeScript// 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
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
TypeScriptimport 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");