Broadcasts
Sending the same template to many recipients is a campaign: one call, one
campaign_id, and counters you can poll while it runs.
- Price it with
review— a dry run that also validates the payload. - Send it with
send— returns acampaign_id. - Track it by polling that campaign.
Price it first
Section titled “Price it first”POSThttps://api.9bits.net/whatsapp/ng/v1/whatsapp/review
curl -X POST https://api.9bits.net/whatsapp/ng/v1/whatsapp/review \ -H "Authorization: Bearer $NINEBITS_WA_KEY" \ -H "Content-Type: application/json" \ -d '{ "message_type": "template", "template_name": "order_shipped", "language": "en", "params": ["Jane", "AB123"], "audience_type": "group", "group": "Lagos customers" }'{ "recipients": 240, "messages": 240, "cost_per": 85, "total_cost": 20400, "balance": 50653.49, "currency": "ngn", "category": "marketing", "can_send": true, "sender": "2349060008502", "template_name": "order_shipped"}can_send folds the balance check in — it is false when total_cost exceeds
what the account can spend.
Send it
Section titled “Send it”POSThttps://api.9bits.net/whatsapp/ng/v1/whatsapp/send
Same body, plus a name to identify the campaign in reporting.
{ "name": "July shipping notice", "message_type": "template", "template_name": "order_shipped", "language": "en", "params": ["Jane", "AB123"], "audience_type": "group", "group": "Lagos customers"}{ "campaign_id": "cb6a4171-3ea5-4447-a82c-54dfca42b344", "status": "running", "recipients": 240, "total_cost": 20400, "currency": "ngn", "scheduled": false}Choosing the audience
Section titled “Choosing the audience”audience_type decides how recipients are resolved.
Everyone opted in in a contact group. Opted-out contacts are skipped for you.
{ "audience_type": "group", "group": "Lagos customers", "params": ["Jane", "AB123"]}An explicit list, all receiving identical values.
{ "audience_type": "single", "recipients": ["2348020000000", "2348030000000"], "params": ["Jane", "AB123"]}An explicit list where each recipient gets their own template values.
{ "audience_type": "variable", "recipients": ["2348020000000", "2348030000000"], "per_recipient": { "2348020000000": ["Jane", "AB123"], "2348030000000": ["Chidi", "AB124"] }}per_recipient overrides params for the numbers it names. This is how you
send a personalised notice without one call per customer.
Schedule it for later
Section titled “Schedule it for later”{ "name": "Weekend sale", "message_type": "template", "template_name": "weekend_flash_sale", "language": "en", "params": ["Jane"], "audience_type": "group", "group": "Lagos customers", "scheduled": true, "schedule_at": "2026-08-15T09:00:00+01:00"}The campaign is created immediately with status scheduled and dispatched at
that time. schedule_at accepts RFC3339 or YYYY-MM-DD HH:mm, and is only read
when scheduled is true.
Track it
Section titled “Track it”GEThttps://api.9bits.net/whatsapp/ng/v1/whatsapp/campaigns/{‘{campaign_id}’}
curl https://api.9bits.net/whatsapp/ng/v1/whatsapp/campaigns/cb6a4171-3ea5-4447-a82c-54dfca42b344 \ -H "Authorization: Bearer $NINEBITS_WA_KEY"{ "campaign_id": "cb6a4171-3ea5-4447-a82c-54dfca42b344", "name": "July shipping notice", "status": "running", "total_recipients": 240, "sent_count": 240, "delivered_count": 231, "read_count": 118, "failed_count": 3, "cost": 20400, "currency": "ngn"}A broadcast is not all-or-nothing
Section titled “A broadcast is not all-or-nothing”Each recipient is priced, billed and queued individually. A recipient that
fails validation is skipped while the rest go out, so a partially successful
campaign is normal and failed_count is where it shows up.
total_cost at launch is what was actually debited for the recipients that were
accepted.