WhatsApp Calling
The calling command mirrors the /calling page and call is its
per-id sibling. Together they cover everything the page does except
the browser-only WebRTC live-audio bridge (intentionally — it’s a real-time
audio stream that has no place in a CLI).
Backed by /api/v1/app/calling/*. Like broadcasts and email, every send
runs a subscription + wallet preflight before submission.
Quick start
splashify calling # overview card (page header)
splashify calling analytics # period analytics
splashify calling calls --limit 50 # recent calls
# Templates
splashify calling templates # list
splashify calling template create-call-button … # tap-to-call (URL/PHONE)
splashify calling template create-permission … # permission request
splashify calling send <template_id> --to +91… # send a template message
# Backend-initiated outbound call
splashify calling initiate --to +91… --phone-number-id <id>
# Recordings
splashify calling upload-recording <file> --call-id <id>Command reference
Overview, settings, analytics, history
splashify calling # page-header overview
splashify calling overview # alias
splashify calling settings # business hours + icon visibility
splashify calling settings update --hours … --icon-visible true
splashify calling analytics [--period 30d|7d|today]
splashify calling calls [--limit N] [--status sent|delivered|failed]
splashify calling call <call_id> # detail (alias: splashify call <id>)
splashify calling call <call_id> status # latest status from backend| Backed by | GET /app/calling/overview |
|---|---|
GET/PUT /app/calling/settings | |
GET /app/calling/analytics?period=… | |
GET /app/calling/calls?limit=&status= | |
GET /app/calling/calls/:call_id | |
GET /app/calling/calls/:call_id/status |
Templates — call buttons & permissions
WhatsApp Calling has two template variants:
| Type | Purpose | Backend |
|---|---|---|
| Call button | Adds a tap-to-call CTA on outbound messages | POST /app/calling/templates/call-button |
| Permission | Asks the recipient to grant calling permission first | POST /app/calling/templates/permission |
splashify calling templates # list all
splashify calling template <template_id> # show one
splashify calling template create-call-button \
--name "support_cb" --label "Call support" --phone-number +919876543210
splashify calling template create-permission \
--name "ask_perm" --body "Tap to allow us to call you"
splashify calling template set-default <template_id> # default for sends
splashify calling template delete <template_id>| Backed by | GET/DELETE /app/calling/templates[/:id] |
|---|---|
POST /app/calling/templates/call-button | |
POST /app/calling/templates/permission | |
POST /app/calling/templates/:id/set-default |
Send a calling template message
splashify calling send <template_id> --to +919876543210
splashify calling send <template_id> --to +919876543210 --vars '["John","ORD-1024"]'| Backed by | POST /app/calling/send |
|---|
Same preflight as broadcasts/email — refuses on subscription_expired
or insufficient_balance before round-trip. Calling has its own per-call
rate (separate from text + media).
Initiate an outbound call (backend dial)
splashify calling initiate --to +919876543210 --phone-number-id <waba_phone_id>| Backed by | POST /app/calling/initiate |
|---|
Triggers a backend-initiated call (the WABA dials the recipient). Same
preflight applies. Returns the new call_id you can poll with
splashify calling call <id> status.
No browser bridge. The page also has a WebRTC live-audio bridge for answering the call in your browser. The CLI cannot do that — initiate from the CLI and answer in the browser (or via your phone’s WABA app).
Permission lookup
splashify calling permission-status --to +919876543210 # has this user granted calling perm?
splashify calling permissions # all granted permissions| Backed by | GET /app/calling/permission-status?phone=… |
|---|---|
GET /app/calling/permissions |
Upload a recording
splashify calling upload-recording ./call.mp3 --call-id <call_id>| Backed by | POST /app/calling/calls/:call_id/recording (multipart) |
|---|
Uploads a recording to attach to a call row — useful when the audio
came from a third-party PBX and you want it logged in /calling/<id>.
Common workflows
Daily call volume report
splashify calling analytics --period 7d | \
jq '.analytics.daily_series[] | {date, total: .count, failed: .failed_count}'Re-send the default call-button template to every contact tagged “leads”
TPL=$(splashify calling templates | jq -r '.templates[] | select(.is_default) | .template_id')
splashify contacts --tag leads | jq -r '.contacts[].phone_number' | \
xargs -I{} splashify calling send "$TPL" --to {}Watch a call ring through
RESP=$(splashify calling initiate --to +919876543210 --phone-number-id <id>)
CID=$(echo "$RESP" | jq -r '.call_id')
while : ; do
STATUS=$(splashify calling call "$CID" status | jq -r '.status')
echo "$(date -u +%H:%M:%S) $STATUS"
[ "$STATUS" = "completed" ] || [ "$STATUS" = "failed" ] && break
sleep 2
donePause calling outside business hours
splashify calling settings update \
--hours '{"mon":["09:00","18:00"],"tue":["09:00","18:00"], … }' \
--icon-visible trueTroubleshooting
permission_required on send — the recipient hasn’t accepted the
permission template. Send a --type permission template first (or check
with splashify calling permission-status).
subscription_expired / insufficient_balance — same preflight
errors as broadcasts/email. See Subscription
and Wallet.
initiate 502s — Meta’s outbound dial gateway is occasionally flaky.
Retry after ~30s; persistent failures usually mean the recipient’s IG
account is mid-suspension. Check splashify calling call <id> status
for the latest reason.
Upload-recording rejects the file — Meta accepts MP3/WAV up to ~25MB. Larger files / other formats are refused with a 400 listing the limits.
No WebRTC bridge in the CLI — by design. Initiate + monitor from the CLI, answer in the browser (or your WABA app).
See also
splashify templates— non-calling templates (text, media, interactive).splashify expenses— per-call billing log.splashify message— the parallel surface for non-calling WhatsApp.