Skip to content
Splashify Pro
Docs

Templates

The templates / template commands manage WhatsApp templates (mirroring /templates and /templates/create), and the rcs templates / rcs template commands manage RCS templates (mirroring /templates/rcs/create).

Both surfaces share the same lifecycle:

text
You write a template ────► Submit to Meta / JBM ────► Approval ────► Send to users
                            ↑ (CLI handles this)        ↑ (out-of-band)

The CLI handles the submit step and the read-side lifecycle (list, inspect, sync the status, delete). Approval is decided by Meta (WhatsApp) or JBM (RCS) and surfaces in the status field — you poll it via the sync / check-status commands.

Backed by /api/v1/app/templates/* and /api/v1/app/rcs/templates/*.

Quick start — WhatsApp

bash
# Simplest template: text-only marketing message
splashify templates create \
  --name "promo_welcome" \
  --language en \
  --category MARKETING \
  --text "Welcome to our store! Use code WELCOME10 for 10% off your first order."

# See it (status will be PENDING until Meta reviews it)
splashify templates                          # list
splashify template <template_id>             # show one

# Poll for the latest approval status
splashify templates sync                     # sync ALL from Meta
splashify templates sync <template_id>       # sync just one

Quick start — RCS

bash
# Simplest: basic text RCS template
splashify rcs templates create \
  --name "rcs_promo_welcome" \
  --type basic \
  --text "Welcome! Tap to shop our latest drop."

splashify rcs templates                                  # list
splashify rcs template <template_id>                     # show one
splashify rcs template <template_id> check-status        # poll approval state

WhatsApp template commands

splashify templates — list

bash
splashify templates                          # default
splashify templates list                     # alias

Response shape (per template):

json
{
  "template_id":    "1234567890123456",
  "waba_id":        "11122233344",
  "name":           "promo_welcome",
  "status":         "APPROVED",
  "category":       "MARKETING",
  "language":       "en",
  "quality_rating": "GREEN",
  "components":     [/* Meta components array */],
  "synced_at":      "2026-05-21T09:14:22Z",
  "created_at":     "2026-05-20T08:00:00Z"
}

status will be one of APPROVED, PENDING, REJECTED, IN_APPEAL, or DISABLED — same values Meta uses.

splashify template <template_id> — show one

bash
splashify template 1234567890123456

No dedicated backend GET — the CLI fetches the list and filters client-side. Same pattern as splashify member <id>, splashify canned <id>, etc.

splashify templates sync — pull from Meta

bash
splashify templates sync                     # sync ALL templates (alias: refresh)
splashify templates sync 1234567890123456    # sync just one template

This is the canonical way to refresh template status (APPROVED / REJECTED / etc.) after Meta reviews it. Run it after a create, or on a cron to keep your local mirror in sync.

splashify templates upload-media <file> — get a media handle

For templates that have a media header (IMAGE / VIDEO / DOCUMENT), Meta requires you to upload the sample file first and reference its media handle in the components array. The CLI exposes this as a two-step flow:

bash
# 1. Upload — get back a handle
HANDLE=$(splashify templates upload-media ./hero.jpg | jq -r '.handle')
echo "$HANDLE"
# "4::aW1hZ2UvanBlZw==:..." (opaque)

# 2. Plug it into example.header_handle in your components
cat <<EOF > ./tpl-with-media.json
{
  "name":     "spring_promo",
  "language": "en",
  "category": "MARKETING",
  "components": [
    {
      "type":   "HEADER",
      "format": "IMAGE",
      "example": { "header_handle": ["$HANDLE"] }
    },
    {
      "type": "BODY",
      "text": "Hi {{1}}, check out our spring drop ↓"
    }
  ]
}
EOF
splashify templates create --file ./tpl-with-media.json

The handle is persistent across templates for the lifetime Meta keeps it — you can reference one upload from multiple templates without re-uploading.

splashify templates create — submit a new template

Three ways to supply the body — pick whichever matches your shape.

bash
# (1) Convenience for a TEXT-only template
splashify templates create \
  --name "promo_welcome" --language en --category MARKETING \
  --text "Welcome to our store!"

# (2) Inline components JSON
splashify templates create \
  --name "promo_welcome" --language en --category MARKETING \
  --components '[
    {"type":"BODY","text":"Hi {{1}}, your order {{2}} has shipped."},
    {"type":"FOOTER","text":"Reply STOP to opt out"}
  ]'

# (3) Full payload from disk — name/language/category live inside the file
splashify templates create --file ./tpl-with-media.json

Components reference

A template is a list of Meta components. The full grammar (from the Meta docs):

json
[
  {
    "type":   "HEADER",
    "format": "TEXT|IMAGE|VIDEO|DOCUMENT|LOCATION",
    "text":   "…",                     // when format=TEXT
    "example": {
      "header_handle": ["<handle>"]    // when format=IMAGE/VIDEO/DOCUMENT
    }
  },
  {
    "type": "BODY",
    "text": "Hi {{1}}, your order {{2}} has shipped",
    "example": { "body_text": [["Alice", "ORD-1024"]] }
  },
  {
    "type": "FOOTER",
    "text": "Reply STOP to opt out"
  },
  {
    "type": "BUTTONS",
    "buttons": [
      {"type": "QUICK_REPLY", "text": "Track order"},
      {"type": "URL", "text": "Visit site", "url": "https://example.com/"},
      {"type": "PHONE_NUMBER", "text": "Call us", "phone_number": "+919876543210"}
    ]
  }
]

Other supported component types: CAROUSEL (with nested cards[] → each card has its own HEADER/BODY/BUTTONS), LIMITED_TIME_OFFER (LTO), AUTHENTICATION body (with add_security_recommendation and code_expiration_minutes).

Variables. {{1}}, {{2}}, … are positional placeholders Meta fills in at send time. Each variable requires a sample value in example.body_text for Meta's review.

splashify templates delete <template_id> — remove

bash
splashify templates delete 1234567890123456
splashify templates delete 1234567890123456 --name "promo_welcome"   # safer

Optionally pass --name so Meta can target the deletion accurately by name — recommended for templates with multiple language variants under the same name.

RCS template commands

RCS has its own provider stack (JBM for India) and its own template shape. The rcs templates subtree namespaces these to keep them distinct from WhatsApp's.

Feature gate. RCS templates require the RCS feature to be enabled on your account. If not, the create endpoint returns HTTP 403 with RCS is not enabled for your account — contact your provider to enable it.

splashify rcs templates — list

bash
splashify rcs templates                      # default
splashify rcs templates list                 # alias
splashify rcs template <id>                  # show one

Response shape (per template):

json
{
  "template_id":      "uuid",
  "name":             "rcs_promo_welcome",
  "template_type":    "rich_card",       // basic | rich_card | carousel
  "status":           "approved",        // pending | ai_reviewing | approved | rejected
  "template_data":    "{\"type\":\"card\",\"card\":{…}}",
  "variables":        [ { "index": 1, "label": "Customer name", "sample": "Rahul" } ],
  "rejection_reason": "",
  "created_at":       "...",
  "updated_at":       "..."
}

template_data is stored as a JSON-encoded string — use jq -r '.template_data | fromjson' to expand it.

variables lists every {{n}} variable the template uses, with the name and sample saved for it (empty strings when none were set). It is an empty list for a template without variables. See RCS Templates.

splashify rcs template <id> check-status — poll approval

bash
splashify rcs template <template_id> check-status
splashify rcs template <template_id> status            # alias

Returns the latest status from the underlying RCS provider. Run it after a create to confirm pending → approved.

splashify rcs templates upload-media — upload rich-card media

bash
splashify rcs templates upload-media ./hero.jpg --height MEDIUM
splashify rcs templates upload-media ./tall.jpg --height TALL
splashify rcs templates upload-media ./short.jpg --height SHORT

Returns a URL you plug into a rich_card's file_url (and optionally thumbnail_url).

splashify rcs templates create — submit

Three ways to supply template_data:

bash
# (1) Convenience — basic text RCS message
splashify rcs templates create \
  --name "rcs_promo_welcome" \
  --type basic \
  --text "Welcome! Tap to shop our latest drop."

# (2) Inline JSON for a rich card or carousel
splashify rcs templates create \
  --name "rcs_card" \
  --type rich_card \
  --data '{
    "type": "card",
    "card": {
      "title":       "Spring drop",
      "description": "20% off all jackets — this week only",
      "url":         "<file_url from upload-media>",
      "suggestions": [
        {"type": "url",     "text": "Shop now",  "url": "https://example.com/spring", "postback": "shop_now"},
        {"type": "message", "text": "Notify me", "postback": "notify_me"}
      ]
    }
  }'

# (3) Full payload from disk
splashify rcs templates create --name "rcs_carousel" --type carousel --file ./carousel.json

template_data shapes

The shapes the app dialog builds (see buildTemplateData):

text
// type: basic
{
  "type": "text",
  "text": "Hi! Tap to shop ↓",
  "suggestions": [/* optional */]
}

// type: rich_card
{
  "type": "card",
  "card": {
    "title":         "Spring drop",
    "description":   "20% off…",
    "url":           "<media file_url>",
    "thumbnail_url": "<optional>",
    "suggestions":   [/* optional */]
  }
}

// type: carousel
{
  "type": "multiple_cards",
  "cards": [
    { "card": { /* same shape as rich_card.card */ } },
    { "card": { /* … */ } }
  ]
}

Suggestion shapes

Five suggestion types are supported (mapped from the page's form to RML on submit):

text
// reply  → quick-reply chip
{"type": "message", "text": "Notify me", "postback": "notify_me"}

// open URL
{"type": "url", "text": "Shop now", "url": "https://example.com/", "postback": "shop_now"}

// dialer
{"type": "dial", "text": "Call us", "call_to": "+919876543210", "postback": "call_us"}

// calendar event
{
  "type": "calendar", "text": "Add to calendar", "postback": "cal",
  "title": "Brand launch", "description": "RSVP early",
  "start": "2026-06-01T10:00:00Z",
  "end":   "2026-06-01T12:00:00Z"
}

// location
{
  "type": "location", "text": "Find a store", "postback": "loc",
  "latitude": 12.9716, "longitude": 77.5946
}

splashify rcs templates delete

bash
splashify rcs templates delete <template_id>

Common workflows

Bulk-sync once a day via cron

bash
# crontab — every night at 03:30
30 3 * * * /usr/local/bin/splashify templates sync >/var/log/splashify-tpl-sync.log 2>&1

The endpoint is idempotent. Each run upserts the local mirror with Meta's authoritative status, so a rejected template visibly flips within ~24h.

Submit a template and wait for approval

bash
# Submit
RESP=$(splashify templates create --name "promo" --language en --category MARKETING --text "Hi!")
TID=$(echo "$RESP" | jq -r '.template_id // .id')

# Poll every 30s for up to 10 minutes
for _ in $(seq 1 20); do
  sleep 30
  splashify templates sync "$TID" > /dev/null
  STATUS=$(splashify template "$TID" | jq -r '.status')
  echo "$STATUS"
  [ "$STATUS" = "APPROVED" ] && break
  [ "$STATUS" = "REJECTED" ] && break
done

Find every REJECTED template + reason

bash
splashify templates | jq '.templates[] | select(.status == "REJECTED") |
  {template_id, name, language, quality_rating}'

Export a template's components as a re-usable JSON file

bash
splashify template <id> | jq '{name, language, category, components}' > export.json
# Submit it to another WABA (after you switch your CLI's context) with:
splashify templates create --file ./export.json
text
// ./carousel.json
{
  "name":     "spring_carousel",
  "language": "en",
  "category": "MARKETING",
  "components": [
    { "type": "BODY", "text": "Spring drop — pick your style ↓" },
    {
      "type": "CAROUSEL",
      "cards": [
        {
          "components": [
            { "type": "HEADER", "format": "IMAGE", "example": { "header_handle": ["<handle-1>"] } },
            { "type": "BODY",   "text": "Jackets — 20% off" },
            { "type": "BUTTONS","buttons": [{"type":"URL","text":"Shop","url":"https://example.com/jackets"}]}
          ]
        },
        {
          "components": [
            { "type": "HEADER", "format": "IMAGE", "example": { "header_handle": ["<handle-2>"] } },
            { "type": "BODY",   "text": "Tees — 15% off" },
            { "type": "BUTTONS","buttons": [{"type":"URL","text":"Shop","url":"https://example.com/tees"}]}
          ]
        }
      ]
    }
  ]
}
bash
splashify templates create --file ./carousel.json
text
// ./rcs-carousel.json
{
  "type": "multiple_cards",
  "cards": [
    {
      "card": {
        "title": "Jackets", "description": "20% off",
        "url":   "<file_url-1>",
        "suggestions": [
          {"type": "url", "text": "Shop", "url": "https://example.com/jackets", "postback": "shop_jackets"}
        ]
      }
    },
    {
      "card": {
        "title": "Tees", "description": "15% off",
        "url":   "<file_url-2>",
        "suggestions": [
          {"type": "url", "text": "Shop", "url": "https://example.com/tees", "postback": "shop_tees"}
        ]
      }
    }
  ]
}
bash
splashify rcs templates create --name "rcs_spring_carousel" --type carousel --file ./rcs-carousel.json

Troubleshooting

WhatsApp

"No WABA connected to your account yet" — finish the Meta Embedded Signup flow at app.splashifypro.com → WhatsApp → Connect Number, then retry. splashify waba confirms the connection.

templates create returns Meta's error_user_msg — Meta refused the template. The most common reasons: missing variables in the body ({{1}} etc. without example.body_text), category mismatch (promotional language in a UTILITY template), or restricted content. The CLI surfaces Meta's error_user_msg directly so the message is actionable.

Status stuck at PENDING — Meta reviews can take up to 24h (usually < 2 minutes). Run splashify templates sync <template_id> to force-refresh; if it's still PENDING after an hour, Meta is backed up — wait.

upload-media returns an opaque error — file too large (Meta caps templates at 5MB image / 16MB video / 100MB document), unsupported format (JPG/PNG/MP4/PDF/DOCX/XLSX), or the file path is wrong. Try a smaller / simpler file first.

RCS

"RCS is not enabled for your account" — RCS is a separately-billed feature. Check splashify subscription and contact your provider to add the RCS add-on.

Status ai_reviewing — the template is being checked against carrier policy first. It usually resolves to approved or rejected within seconds. rcs template <id> check-status re-polls.

rejected with rejection_reason — read the reason field for the specific guideline that was violated. Fix the template_data and re-create (RCS templates cannot be edited in place; create a new one).

Carousel only has 1 card — RCS requires ≥ 2 cards in a carousel. The page enforces this client-side; the backend may also reject it.

See also