Skip to Content
splashify CLIBroadcasts

Broadcasts

The broadcasts (plural) and broadcast (singular) commands mirror the /broadcasts and /broadcasts/<id> pages. From the terminal you can list every campaign, drill into one, see per-message delivery, cancel an in-flight broadcast, send a scheduled one immediately, restart a partially failed run, and rebroadcast a cohort.

Every write command runs the same subscription + wallet preflight that the page does before submitting; the backend still enforces both at send time. A broadcast that fails preflight prints why and exits non-zero without burning a wallet hold.

Quick start

splashify broadcasts # list every campaign splashify broadcast stats # overall stats card splashify broadcast <broadcast_id> # campaign detail splashify broadcast create \ --name "May Sale" \ --template may_offer \ --audience-type segment \ --audience-id <segment_id> \ --schedule 2026-06-01T10:00:00Z

Command reference

splashify broadcasts — list campaigns

splashify broadcasts # all
Backed byGET /api/v1/app/broadcasts

Response shape (per row):

{ "broadcast_id": "uuid", "name": "May Sale", "template_name": "may_offer", "audience_type": "segment", // segment | tag | all "audience_id": "...", "status": "scheduled", // draft | scheduled | running | completed | cancelled | failed "schedule_at": "2026-06-01T10:00:00Z", "started_at": null, "completed_at": null, "created_at": "...", "total_recipients": 1245, "sent_count": 0, "delivered_count":0, "read_count": 0, "failed_count": 0 }

splashify broadcast stats — global stats

splashify broadcast stats
Backed byGET /api/v1/app/broadcasts/stats

Returns counts across every broadcast (total / completed / running / failed) plus aggregate delivery metrics.

splashify broadcast <broadcast_id> — campaign detail

splashify broadcast 7a32bce6-…
Backed byGET /api/v1/app/broadcasts/:broadcast_id

Returns the full broadcast row plus the cohort counts and progress breakdown shown on the detail page.

splashify broadcast create — schedule a new campaign

splashify broadcast create \ --name "May Sale" \ --template may_offer \ --audience-type segment \ --audience-id <segment_id> \ --schedule 2026-06-01T10:00:00Z
Backed byPOST /api/v1/app/broadcasts
FlagRequiredNotes
--nameyesDisplay name
--templateyesApproved WhatsApp template name (must be APPROVED — see Templates)
--audience-typeyessegment | tag | all
--audience-idwhen type=segment|tagsegment_id (UUID) or tag name
--schedulenoISO 8601 UTC; omit to send immediately

Preflight checks before the round-trip:

  1. Subscription — the account must have an active plan OR unexpired trial. A subscription_expired refusal points to /settings/subscriptions.
  2. Balance — the wallet must cover the estimated cost (recipients × per-message rate for the template’s category). If short, the CLI surfaces the shortfall and the recharge link.

If either fails the CLI exits with the friendly error and does not create the broadcast. Once submitted, the backend’s broadcast worker enforces both again at send time (defence in depth).

Lifecycle actions

Per-id actions live after the broadcast id — splashify broadcast <id> <action>.

splashify broadcast <broadcast_id> cancel # stop a scheduled/running broadcast splashify broadcast <broadcast_id> send-now # send a `scheduled` broadcast immediately splashify broadcast <broadcast_id> restart # retry the failed cohort on a `failed`/partial broadcast splashify broadcast <broadcast_id> rebroadcast \ --cohort failed|sent|delivered|read|all # re-target a specific cohort on a fresh row
Backed byVerbEndpoint
cancelPOST/app/broadcasts/:id/cancel
send-nowPOST/app/broadcasts/:id/send-now
restartPOST/app/broadcasts/:id/restart
rebroadcastPOST/app/broadcasts/:id/rebroadcast

All four run the same preflight as create. cancel is irreversible — once a broadcast moves to cancelled, you re-broadcast or create a new one.

Per-message inspection

splashify broadcast <broadcast_id> messages [--page N] [--limit N] [--status sent|delivered|read|failed] splashify broadcast <broadcast_id> progress # live SSE stream of progress events splashify broadcast <broadcast_id> progress --once # print the first event and exit splashify broadcast <broadcast_id> progress --max 10 # stop after N events splashify broadcast <broadcast_id> cohorts # cohort counts (failed / sent / delivered / read) splashify broadcast <broadcast_id> --recompute # refresh the local rollup (Refresh button)
Backed byGET /app/broadcasts/:id/messages?page=&limit=&status=
GET /app/broadcasts/:id/progress (Server-Sent Events)
GET /app/broadcasts/:id/cohort-counts
GET /app/broadcasts/:id?recompute=true

--recompute is the page’s “Refresh” button — it re-aggregates the per-message rows into the broadcast row’s counters. Use it if the totals look stale. progress streams Server-Sent Events the same way the /broadcasts/<id>/progress page does; the loop ends when the broadcast reaches a terminal state (completed, cancelled, failed) or when --max / --once is satisfied.

CSV export

splashify broadcast <broadcast_id> export > campaign.csv # Filter by message status before exporting splashify broadcast <broadcast_id> export --status FAILED > failed.csv splashify broadcast <broadcast_id> export --csv # force the CSV content-type
Backed byGET /app/broadcasts/:id/export

Streams the same CSV the page’s Export CSV button produces — one row per recipient with phone, status, sent_at, delivered_at, failed_reason, etc. The CLI writes the raw response to stdout so you can redirect or pipe.

Live progress (SSE)

splashify broadcast <broadcast_id> progress # follow until terminal status splashify broadcast <broadcast_id> progress --once # snapshot (first event, then exit) splashify broadcast <broadcast_id> progress --max 10 # stop after N events
Backed byGET /app/broadcasts/:id/progress (Server-Sent Events)

Mirrors the /broadcasts/<id>/progress page. The CLI connects to the SSE stream, prints one JSON object per event (running counts + status), and exits cleanly when the broadcast reaches a terminal state (COMPLETED, CANCELLED, or FAILED). Aliases: watch, stream.

Each event has the shape:

{ "broadcast_id": "uuid", "status": "SENDING", "pct": 42.3, "counts": { "pending": 100, "sent": 850, "delivered": 820, "read": 612, "failed": 13 } }

Common workflows

List broadcasts that need attention

splashify broadcasts | jq '.broadcasts[] | select(.status == "failed" or .failed_count > 0) | {broadcast_id, name, status, failed_count, total_recipients}'

Watch a broadcast finish

# Recommended: follow the SSE stream and exit on terminal status splashify broadcast 7a32bce6-… progress

Need a polling loop instead (e.g. logging snapshots every 30s)?

BID=7a32bce6-… while : ; do STATUS=$(splashify broadcast "$BID" | jq -r '.status // .broadcast.status') PROG=$(splashify broadcast "$BID" progress --once | jq -c '.counts // .') echo "$(date -u +%H:%M:%S) $STATUS $PROG" [ "$STATUS" = "completed" ] || [ "$STATUS" = "cancelled" ] || [ "$STATUS" = "failed" ] && break sleep 30 done

Send to every “VIP” contact

SEGMENT_ID=$(splashify segments --search "VIP" | jq -r '.segments[0].id') splashify broadcast create \ --name "VIP exclusive — May" \ --template vip_may_offer \ --audience-type segment \ --audience-id "$SEGMENT_ID"

Cancel every scheduled broadcast (e.g. emergency stop)

splashify broadcasts | \ jq -r '.broadcasts[] | select(.status == "scheduled") | .broadcast_id' | \ xargs -I{} splashify broadcast {} cancel

Rebroadcast to recipients who failed

splashify broadcast <broadcast_id> rebroadcast --cohort failed # Backend re-targets only the recipients whose message status is `failed`, # at the same template, on a fresh broadcast row.

Troubleshooting

subscription_expired on create — the account has no active paid plan and the trial is over. Open /settings/subscriptions or run splashify subscription for status + upgrade URL.

insufficient_balance on create — wallet won’t cover the estimated cost. The error includes the shortfall amount and a recharge link. splashify wallet shows the current balance; recharge from the app.

Template not found / not approved--template takes the template name, not the ID. The template must be APPROVED on Meta — run splashify templates sync to refresh status, then splashify template <id> to confirm.

audience_id ignored — required only when --audience-type is segment or tag. For all, omit it.

Counts look wrong on the detail view — run splashify broadcast recompute-stats <id>. The page’s per-row counts come from a rollup that can lag during heavy bursts.

Cancelled but still receiving deliveries — cancellation stops scheduling of new messages. Messages already in Meta’s outbound queue get delivered; webhook updates land normally. Wait ~1 minute after cancel to see the final counts settle.

See also