Skip to content

Broadcasts

Sending the same template to many recipients is a campaign: one call, one campaign_id, and counters you can poll while it runs.

  1. Price it with review — a dry run that also validates the payload.
  2. Send it with send — returns a campaign_id.
  3. Track it by polling that campaign.

POSThttps://api.9bits.net/whatsapp/ng/v1/whatsapp/review

Terminal window
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.

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
}

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"]
}
{
"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.

GEThttps://api.9bits.net/whatsapp/ng/v1/whatsapp/campaigns/{‘{campaign_id}’}

Terminal window
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"
}

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.