Creating a template
Open Templates in the sidebar, then + New template. Each template binds to a single channel — EMAIL, SMS, WHATSAPP, or VOICE — because every channel has different length, formatting, and approval rules.
Variables
Variables in double braces resolve from the contact record at send time:
Subject: Welcome to {{workspaceName}}, {{firstName}}
Hi {{firstName}},
Thanks for joining. Use code WELCOME15 at checkout for 15% off your first order.
— {{senderName}}{{firstName}}, {{lastName}}, {{email}}, {{phone}}— contact fields{{workspaceName}}, {{senderName}}— workspace metadata{{metadata.<key>}}— anything you've stored on the contact's metadata blob
Start from a layout — Email gallery
On the EMAIL channel, click ✦ Start from layout in the HTML editor toolbar to open a small gallery of pre-built modern templates. Five layouts ship today:
- Transactional notice — clean card with logo, headline, body, CTA.
- Receipt / order summary — itemised lines + total + CTA.
- Marketing announcement — color header band + two-column body.
- Payment reminder — invoice-style amount + due + payment-link CTA (Bahasa-friendly).
- Welcome / onboarding — friendly greeting + 3-step checklist + CTA.
Each layout exposes copy fields (headline, intro, body, CTA, footer) and brand colors (primary, text, background, surface) plus an optional logo URL. The preview iframe re-renders on every keystroke. Variables like {{first_name}} still resolve at send time — the layout is just the chrome.
AI assist (Template Generator)
Click ✨ AI build in the template editor to draft a starting point from a plain-language prompt (e.g. "3-day overdue invoice nudge in Bahasa Indonesia for WhatsApp").
Connect a provider in Settings → AI connectorto get real LLM output. Supported providers: Anthropic Claude, OpenAI GPT, OpenRouter (any of 100+ models behind one key), and local Ollama. Your key stays in the browser; the backend never sees it. Without a connector, the generator returns a deterministic stub good enough for demos.
Formatting values with pipe filters
When a partner (or your CSV import) gives you raw values — an ISO date, an integer amount, a URL — apply a filter inline so the customer sees something readable. Filters chain with| exactly like Liquid or Jinja, but the engine is tiny on purpose.
{{ amount | currency:'IDR':'id' }} → "Rp 1.500.000"
{{ due_date | date:'long':'id' }} → "2 Juni 2026"
{{ due_date | date:'short':'id' }} → "2 Jun 2026"
{{ count | number:'id' }} → "1.250"
{{ name | upper }} → "ANDINI"
{{ note | trim | default:'—' }} → fallback when emptydate:'long' | 'short' | 'iso' | 'datetime'— second arg is locale (default'id').currency:'IDR' | 'USD' | 'EUR' | …— second arg locale.number:'id' | 'en'[:decimals]upper,lower,trimdefault:'fallback'— used when the value is empty
Filters that don't understand their input (e.g., a non-date string given to date) pass the value through unchanged. So a malformed value never blanks an outgoing message.
Real example — payment reminder (Bahasa)
Contact metadata as it arrives from the system of record:
{
"loan_id": "LOAN-2026-0042",
"amount": 1500000,
"due_date": "2026-06-02",
"payment_link": "https://demo.bank.example/pay/abc"
}Hi {{first_name}} 👋
Ini adalah reminder bahwa pembayaran kamu sebesar
{{ amount | currency:'IDR':'id' }} akan jatuh tempo pada
{{ due_date | date:'long':'id' }}.
Silakan lakukan pembayaran disini:
{{ payment_link }}
Terima kasih.Hi Andini 👋 Ini adalah reminder bahwa pembayaran kamu sebesar Rp 1.500.000 akan jatuh tempo pada 2 Juni 2026. Silakan lakukan pembayaran disini: https://demo.bank.example/pay/abc Terima kasih.
Channel-specific rules
- WhatsApp: marketing templates must be pre-approved by Meta. Transactional ("session") messages within 24h of customer reply have no template restriction.
- SMS: 160 chars per segment in GSM-7 encoding. Longer messages bill multiple segments — preview shows the count before save.
- Email: HTML and plain-text variants both supported. Subject required.
- Voice: body becomes TTS script. Business plan only.