Broadcasts

Send an approved template to many contacts at once, with per-recipient variables and a throttle that protects your number’s quality rating. A broadcast is a campaign: create it, then track it to completion.

With an app key, broadcasts need the whatsapp:campaign scope. You can only broadcast an approved template — build and submit one first (see Creating & managing templates). With your org API key you get the full flow — drafts, scheduling, saved audiences, start and resume — on more kinds of numbers.


Create a broadcast

POST /campaigns queues the campaign and (by default) starts sending immediately.

curl -X POST https://api.telenow.ai/api/app-whatsapp/campaigns \
  -H "Authorization: Bearer vai_app_…" \
  -H "Content-Type: application/json" \
  -d '{
    "channelId": "b1e2c3d4-…",
    "name": "Spring sale",
    "template": "spring_sale",
    "language": "en_US",
    "throttle_per_min": 120,
    "recipients": [
      { "phone": "+919876543210", "variables": ["Asha"] },
      { "phone": "+919812345678", "variables": ["Ravi"] }
    ]
  }'
{ "success": true, "data": {
  "campaignId": "c4f0a1b2-…",
  "inserted": 2,
  "suppressed": 0,
  "status": "running"
} }
FieldTypeNotes
channelIdstringRequired. The Meta Cloud number to send from (native or your own Meta app)
namestringRequired. A label for your own reference
templatestringRequired. Name of an approved template on this channel
languagestringOptional. Template language (defaults to en_US)
throttle_per_minnumberOptional. Max sends per minute — defaults to 60, allowed 1–6,000. Protects your quality rating
header_componentobjectOptional. The runtime header component for a template with a media header — e.g. { "type": "header", "parameters": [ { "type": "image", "image": { "id": "<mediaId>" } } ] } with a mediaId from this channel
recipientsarrayRequired. { phone, variables? } per contact — see below. ≤ 5,000 per campaign. Phone numbers need the country code (+91…)
autostartboolOptional. Defaults to true. An app key has no start route, so a campaign created with false stays a draft here — start it with your org API key (POST …/whatsapp-campaigns/{id}/start) or from the dashboard

Per-recipient variables. Each recipient supplies its own variables array, filling the template’s {{1}}, {{2}}… in order — so every contact gets a personalised message from one approved template:

"recipients": [
  { "phone": "+919876543210", "variables": ["Asha", "#1234"] },
  { "phone": "+919812345678", "variables": ["Ravi", "#1235"] }
]

The response reports inserted (recipients queued) and suppressed (opted-out numbers — they're added to the campaign as suppressed and never messaged). A number listed twice is queued once; the duplicate is dropped and counted in neither field. status is running when autostart is true and at least one recipient was queued; otherwise it's draft.


Track a broadcast

Poll GET /campaigns/{id} for live progress. statusCounts breaks the roster down by delivery state:

curl https://api.telenow.ai/api/app-whatsapp/campaigns/c4f0a1b2-… \
  -H "Authorization: Bearer vai_app_…"
{ "success": true, "data": {
  "id": "c4f0a1b2-…",
  "name": "Spring sale",
  "status": "running",
  "totalRecipients": 2,
  "sent": 2,
  "failed": 0,
  "statusCounts": {
    "delivered": 1,
    "read": 1
  }
} }

Each recipient advances queued → sending → sent → delivered → read (or failed) as Meta reports status back; opted-out recipients sit at suppressed. statusCounts lists only the states that currently hold recipients. status on the campaign is draft, running, paused, completed, or cancelled. sent and failed are running totals. For per-message detail, subscribe to whatsapp.message.status — each broadcast send carries the same wamid you can correlate.

An app key sees only the campaigns it created — any other id returns 404. Sending is capped at about 400 messages a minute per campaign, whatever throttle_per_min says.


Pause & cancel

# Pause — stops sending; resume with your org API key (…/start) or from the dashboard
curl -X POST https://api.telenow.ai/api/app-whatsapp/campaigns/c4f0a1b2-…/pause \
  -H "Authorization: Bearer vai_app_…"
{ "success": true }
# Cancel — stops sending permanently; queued recipients become "suppressed"
curl -X POST https://api.telenow.ai/api/app-whatsapp/campaigns/c4f0a1b2-…/cancel \
  -H "Authorization: Bearer vai_app_…"
{ "success": true }

Pause halts a running campaign; cancel ends a draft, running or paused one for good. Already-sent messages are unaffected either way. Pausing a campaign that isn't running, or cancelling one that's already completed or cancelled, returns 409.


Opt-outs

A customer who replies with exactly one of stop, stopall, unsubscribe, optout, opt out or opt-out (the whole message, any case) is opted out automatically. Opted-out numbers are suppressed from every future broadcast — counted in suppressed rather than messaged — their still-queued messages in running campaigns are suppressed too, and template sends to them are refused. A sentence that merely contains "stop" doesn't opt anyone out.

STOP replies are recorded for Meta Cloud numbers. For partner numbers (GupShup, Plivo, AiSensy), add opt-outs yourself with your org API key:

MethodPathNotes
GET/api/orgs/{orgId}/whatsapp-campaigns/opt-outs{ optOuts: [ { phone, reason, created_at } ] } — newest 1,000; reason is stop_keyword or manual
POST/api/orgs/{orgId}/whatsapp-campaigns/opt-outs{ "phone": "+919876543210" } — idempotent; also suppresses that number's queued messages

There is no route to remove an opt-out.

Opt-in is mandatory before you broadcast, and a weak, un-opted list tanks your quality rating fast. Keep lists clean and lean on the throttle_per_min to ramp gently.


Limits

LimitApp keyOrg API key
Recipients5,000 per campaign20,000 per upload, uploads can repeat; 5,000 per saved-audience call
Active campaigns50 per appNo cap
ThrottleDefault 60/min, 1–6,000; at most ~400/min actually sentSame
TemplateMust be approved on the channelApproval is checked on Meta Cloud numbers
ChannelMeta Cloud numbers (native or your own Meta app)Meta Cloud and partner numbers; not WhatsApp Web

On the app key, exceeding 5,000 recipients or 50 active campaigns returns a 400. To message a larger audience, split it across campaigns — or use your org API key.


Endpoint reference

Base /api/app-whatsapp, scope whatsapp:campaign.

MethodPathPurpose
POST/campaignsCreate + (auto)start → { campaignId, inserted, suppressed, status }
GET/campaigns/{id}Status → { id, name, status, totalRecipients, sent, failed, statusCounts }
POST/campaigns/{id}/pausePause → { success: true }
POST/campaigns/{id}/cancelCancel → { success: true }

The dashboard's CSV/XLSX upload is parsed in the browser into the same rows these endpoints take, so the API takes JSON only.


With your org API key

With your org API key (X-API-Key: vai_live_…) a broadcast is built in steps — create a draft → add recipients → start — under /api/orgs/{orgId}/whatsapp-campaigns. Bodies use snake_case, and responses return the campaign as a database row (snake_case). Changes need an owner, admin or developer key, created by someone with one of those roles.

# 1) Create a draft (no recipients yet)
curl -X POST https://api.telenow.ai/api/orgs/{orgId}/whatsapp-campaigns \
  -H "X-API-Key: vai_live_…" \
  -H "Content-Type: application/json" \
  -d '{
    "channel_id": "b1e2c3d4-…",
    "name": "Spring sale",
    "template_name": "spring_sale",
    "template_language": "en_US",
    "throttle_per_min": 120
  }'

# 2) Add recipients (repeat as needed, up to 20,000 per call)
curl -X POST https://api.telenow.ai/api/orgs/{orgId}/whatsapp-campaigns/{id}/recipients \
  -H "X-API-Key: vai_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "recipients": [ { "phone": "+919876543210", "variables": ["Asha"] } ] }'

# 3) Start
curl -X POST https://api.telenow.ai/api/orgs/{orgId}/whatsapp-campaigns/{id}/start \
  -H "X-API-Key: vai_live_…"

Create (POST /) takes channel_id, name and template_name (required), plus optional:

FieldNotes
template_languageDefaults to en_US
throttle_per_minDefault 60, allowed 1–6,000
timezone, start_time_local, end_time_localA daily send window, e.g. "Asia/Kolkata", "09:00", "20:00" (24-hour HH:MM). Applies only when both times are set; a window that crosses midnight works
scheduled_atRFC 3339 time. The draft starts by itself once this time has passed and it has recipients
header_componentRuntime header for a media-header template — Meta Cloud numbers only

It returns { campaign } as a draft. On Meta Cloud numbers the template must be approved for that language (400 otherwise); WhatsApp Web numbers can't broadcast.

MethodPathWhat it does
GET/All the org's campaigns, newest first → { campaigns }
GET/{id}{ campaign, statusCounts }
POST/{id}/recipients{ recipients: [ { phone, variables? } ] } → { inserted, suppressed }. Up to 20,000 per call, and the body must stay under 2 MB. The campaign must be a draft, paused or running
POST/{id}/recipients-from-segment{ "segment_id" } — adds a saved audience (no per-recipient variables) → { inserted, suppressed, capped, cap }. At most 5,000 — the most recently updated contacts; capped: true means more matched
GET/{id}/recipients?status&limit&offsetRecipients with their delivery state → { recipients, statusCounts } (limit default 50, max 500)
POST/{id}/startStart a draft or resume a paused campaign → { campaign }. Needs at least one recipient; 409 from any other state
POST/{id}/pausePause a running campaign → { campaign }
POST/{id}/cancelCancel → { campaign }; queued recipients become suppressed
GET / POST/opt-outsSee Opt-outs

The org routes also start, pause and cancel campaigns created by an app key.