Skip to Content
splashify CLICanned Messages

Canned Messages

The canned command (aliases: cm, cms, canned-messages) mirrors the Settings → Canned Messages page in the app. Use it to save commonly-sent WhatsApp replies — plain text, images, videos, documents, audio, or any advanced Meta Cloud API payload — and recall them from chat handlers or scripts later.

Backed by /api/v1/app/canned-messages[/:id]. Every call uses your stored oc_live_ token and is scoped to your account.

Quick start

# Save a text snippet splashify canned create \ --name "Welcome message" \ --shortcut "/welcome" \ --description "First-touch greeting" \ --type TEXT \ --text "Hi! Thanks for reaching out — how can we help?" # Save an image with a caption splashify canned create \ --name "Receipt template" \ --type IMAGE \ --url https://cdn.example.com/receipt.png \ --caption "Your receipt is attached" # See what you've got splashify canned

Message types

The backend stores any of these message_type values. The CLI provides convenience flags for the first five (the ones the app dialog exposes interactively); the rest are reachable via --payload '<json>'.

TypeConvenience flagsBuilt payload shape
TEXT--text{"text":{"body":"…"}}
IMAGE--url --caption{"image":{"link":"…","caption":"…"}}
VIDEO--url --caption{"video":{"link":"…","caption":"…"}}
AUDIO--url{"audio":{"link":"…"}}
DOCUMENT--url --filename --caption{"document":{"link":"…","filename":"…","caption":"…"}}
LOCATIONuse --payloadMeta location object
CONTACTuse --payloadMeta contacts array
INTERACTIVE_BUTTONuse --payloadMeta interactive (buttons)
INTERACTIVE_LISTuse --payloadMeta interactive (list)
INTERACTIVE_CTA_URLuse --payloadMeta interactive (cta_url)
ADDRESSuse --payloadMeta address-request body

Why these shapes? They match the app dialog’s buildPayload() exactly, so payloads written from the CLI render identically in the app’s preview pane and on WhatsApp itself.

Command reference

splashify canned — list

splashify canned # default: every canned message splashify canned list # alias splashify canned list --search receipt # name/shortcut/description match splashify canned list --type IMAGE # filter by message_type splashify canned list --search hi --type TEXT # combined
Backed byGET /api/v1/app/canned-messages

--search and --type are applied client-side (the backend returns the full list — same as the app does).

Response shape:

{ "success": true, "messages": [ { "id": "uuid", "name": "Welcome message", "shortcut": "/welcome", "description": "First-touch greeting", "message_type": "TEXT", "message_payload": {"text": {"body": "Hi! Thanks for reaching out…"}}, "is_active": true, "created_at": "...", "updated_at": "..." } ] }

splashify canned <id> — show one

splashify canned <message_id>

No dedicated backend endpoint — the CLI fetches the list and filters client-side, same pattern as splashify attribute <id> and splashify member <id>.

splashify canned create — save a new message

splashify canned create \ --name <string> \ --type <TEXT|IMAGE|VIDEO|AUDIO|DOCUMENT|> \ [--shortcut <string>] [--description <string>] \ [convenience flags OR --payload '<json>']
FlagRequired?Notes
--nameyesMax 200 chars. Duplicate names (case-insensitive) are refused with HTTP 409
--typeyesDefaults to TEXT if omitted
--shortcutnoThe short trigger you type in chat to recall this message
--descriptionnoFree-text note
--textTEXTBody text
--urlmedia typesPublic media URL (use splashify media upload to get one)
--captionoptionalCaption for IMAGE / VIDEO / DOCUMENT
--filenameoptionalFilename hint for DOCUMENT
--payloadadvancedFull message_payload JSON — wins over the convenience flags
Backed byPOST /api/v1/app/canned-messages

splashify canned update — change an existing message

splashify canned update <message_id> [--name …] [--shortcut …] \ [--description …] [--type …] \ [--text | --url --caption --filename …] \ [--payload '{…}']
Backed byPUT /api/v1/app/canned-messages/:id

Read-modify-write. The backend’s PUT replaces every column, so the CLI loads the current record first, overlays only the flags you passed, then sends the full body. Fields you don’t mention stay intact.

# Just rename it splashify canned update <id> --name "New welcome message" # Switch the body of a TEXT canned message splashify canned update <id> --text "Hi there 👋 — what can I do for you?" # Add a caption to an existing IMAGE canned message splashify canned update <id> --caption "Updated caption" # Change a TEXT canned message to a VIDEO (must also pass --url) splashify canned update <id> --type VIDEO --url https://cdn.example.com/intro.mp4 # Replace the whole payload (advanced types) splashify canned update <id> --type INTERACTIVE_LIST \ --payload '{"interactive":{"type":"list","body":{"text":"Choose one"}, …}}'

splashify canned toggle — activate / deactivate

splashify canned toggle <message_id>
Backed byPOST /api/v1/app/canned-messages/:id/toggle

Flips is_active. The endpoint always returns the new state — is_active: true means the message is now usable by chat handlers, false hides it.

splashify canned delete — remove

splashify canned delete <message_id> splashify canned rm <message_id> # alias splashify canned remove <message_id> # alias
Backed byDELETE /api/v1/app/canned-messages/:id

Permanent. There is no soft-delete and no recovery — back up first if you need to keep a copy.

Advanced — interactive payloads

For the message types the app dialog does not expose (interactive buttons, lists, CTA URLs, location, contact, address) use --payload:

splashify canned create \ --name "Quick reply buttons" \ --type INTERACTIVE_BUTTON \ --payload '{ "interactive": { "type": "button", "body": {"text": "How can we help?"}, "action": { "buttons": [ {"type": "reply", "reply": {"id": "billing", "title": "Billing"}}, {"type": "reply", "reply": {"id": "support", "title": "Support"}}, {"type": "reply", "reply": {"id": "sales", "title": "Sales"}} ] } } }' splashify canned create \ --name "Open store CTA" \ --type INTERACTIVE_CTA_URL \ --payload '{ "interactive": { "type": "cta_url", "body": {"text": "Open our online store"}, "action": { "name": "cta_url", "parameters": {"display_text": "Open store", "url": "https://shop.example.com"} } } }' splashify canned create \ --name "Office location" \ --type LOCATION \ --payload '{ "location": { "latitude": 12.9716, "longitude": 77.5946, "name": "HQ", "address": "Bangalore, India" } }'

The backend forwards message_payload to Meta verbatim — anything the WhatsApp Cloud API accepts  works.

Common workflows

Bulk-create from a JSON file

// canned.json [ {"name": "Hi", "type": "TEXT", "text": "Hi! How can we help?"}, {"name": "Hours", "type": "TEXT", "text": "We're open 9-6, Mon-Sat."}, {"name": "Brochure", "type": "DOCUMENT", "url": "https://…/brochure.pdf", "filename": "brochure.pdf"} ]
jq -c '.[]' canned.json | while read -r row; do splashify canned create \ --name "$(echo "$row" | jq -r '.name')" \ --type "$(echo "$row" | jq -r '.type')" \ $(echo "$row" | jq -r 'select(.text) | "--text \"\(.text)\""') \ $(echo "$row" | jq -r 'select(.url) | "--url \(.url)"') \ $(echo "$row" | jq -r 'select(.filename) | "--filename \(.filename)"') done

Deactivate every TEXT canned message you used last quarter

splashify canned list --type TEXT | \ jq -r '.messages[] | select(.updated_at < "2026-04-01") | .id' | \ xargs -I{} splashify canned toggle {}

Find the message backing a /welcome shortcut

splashify canned list --search /welcome | \ jq '.messages[] | select(.shortcut == "/welcome") | {id, name, message_type}'

Round-trip an existing payload — copy with a new name

SRC_ID="<existing message_id>" PAYLOAD=$(splashify canned $SRC_ID | jq -c '.message_payload') TYPE=$(splashify canned $SRC_ID | jq -r '.message_type') splashify canned create --name "Welcome — variant B" --type "$TYPE" --payload "$PAYLOAD"
NEW_URL=$(splashify media upload ./receipt-v2.png | jq -r '.file_url') splashify canned update <id> --url "$NEW_URL" --caption "Updated receipt"

Troubleshooting

“A canned message with this name already exists” — names are unique case-insensitively across your account. Use splashify canned list --search <name> to find the duplicate, then either rename or delete the old row.

“Name must be 200 characters or less” — trim the --name argument.

“Invalid message payload” — your --payload JSON didn’t parse on the backend. Validate locally with jq first: echo "$PAYLOAD" | jq .

Update didn’t change the type — make sure you passed --type and the matching media flag. Switching from TEXT → IMAGE without --url would fail validation; the CLI catches this before the round-trip and prints a usage error.

--text ignored on an IMAGE--text only builds the payload for --type TEXT. For IMAGE/VIDEO/DOCUMENT captions, use --caption instead.

See also

  • splashify media — upload files first to get the CDN URLs you pass to --url.
  • splashify templates — approved WhatsApp templates, which are different from canned messages (templates need Meta approval, canned messages don’t).
  • splashify message — the send-side commands that can include the payloads you stored here.