Overview
Dynamic variables let you write one message that arrives personalised for every person. Instead of
"Hello!", you write Hello {{ contact.first_name }}! and each contact receives their own name.
A variable is always written between double braces and resolved on the server, at send time β never in the browser. If the data does not exist, the variable becomes empty text and the rest of the message goes out normally.
Every variable uses the same vocabulary on every surface: what works in the reply composer works identically in an email, a campaign, a follow-up, a flow and a contract.
Prerequisites
- None. The basic variables (contact, account, inbox, agent) work from day one.
- A module's variables (charge, booking, contract, orderβ¦) only carry a value once that module is in use in the account. With no data they resolve to empty β they never break the message.
Step by step
- Open any text field that accepts variables (composer, macro, canned response, campaign, follow-up, contract, booking reminderβ¦).
- Type
{{β the variable list appears automatically. - Or click the
{x}Insert variable button when it is available in the field header. - Search by name (for example "amount" or "due") and click to insert.
- Check the value before sending. Inside a conversation, every variable in the list shows what it will produce for that contact β the real name, the real charge amount, the real booking date. What you see there is exactly what the customer will receive.
- Send a test before firing at your whole base.
Settings and options
The available groups
| Group | What it is for | Example |
|---|---|---|
| Contact | Who is on the other side | {{ contact.first_name }} |
| Conversation | The current conversation | {{ conversation.display_id }} |
| Account / Brand | Your company | {{ account.name }} Β· {{ brand.name }} |
| Agent | Who is answering | {{ agent.available_name }} |
| Date and time | The clock, in the account timezone | {{ now.date }} Β· {{ now.weekday }} |
| CRM deal | The deal linked to the contact | {{ crm.title }} Β· {{ crm.value_formatted }} |
| Organization | The contact's company | {{ organization.legal_name }} |
| Charge | The most recent charge | {{ payment.pay_url }} Β· {{ payment.due_date_formatted }} |
| Subscription | The recurring plan | {{ subscription.next_due_date_formatted }} |
| Order | The most recent order | {{ order.amount_formatted }} |
| Booking | The contact's appointment | {{ booking.date_formatted }} Β· {{ booking.manage_url }} |
| Contract | The open contract | {{ contract.sign_url }} |
| Task | The task linked to the contact | {{ task.due_at_formatted }} |
| Cart and checkout | Sales recovery | {{ commerce.pay_url }} |
| Product | The product being quoted | {{ product.price_formatted }} |
| Group | WhatsApp group / launch cohort | {{ group.invite_url }} |
| Team Β· SLA Β· Availability | Operations | {{ team.name }} Β· {{ wfm.online }} |
| Satisfaction Β· Engagement | Relationship | {{ csat.rating }} Β· {{ engagement.tier }} |
| Salesperson Β· Goal Β· Commission Β· Affiliate | Sales | {{ seller.name }} Β· {{ affiliate.referral_code }} |
| Ad Β· Lead | Paid acquisition | {{ lead.headline }} |
| Article | Help Center | {{ article.url }} |
| Account Brain | AI signals | {{ brain.risk_band }} |
The picker only shows the groups that work on that screen. A campaign, for instance, has no conversation, so conversation variables are not offered there.
Formatted values
Every money and date value exists in two forms:
- Raw β the value as stored:
{{ crm.value_amount }}β1500.0 - Formatted β ready for the customer to read:
{{ crm.value_formatted }}β$1,500.00
The same applies to dates: {{ payment.due_date }} β 2026-08-08 and
{{ payment.due_date_formatted }} β 08/08/2026, always in your account's currency, language and
timezone.
If a value has no formatted sibling, you can format it inline with a filter:
{{ payment.amount | money: 'USD' }} β $1,500.00
{{ booking.starts_at | datetime }} β 08/08/2026 02:30 PM
Custom fields
The custom fields you created also become variables, in the form
{{ contact.custom_attribute.key }}. This works for custom fields on contacts, conversations,
organizations, deals, products, tasks, groups, charges, bookings, follow-ups and contracts.
Use cases
- Overdue charge:
Hi {{ contact.first_name }}, your invoice of {{ payment.amount_formatted }} was due on {{ payment.due_date_formatted }}. Pay here: {{ payment.pay_url }} - Booking reminder:
Your appointment is on {{ booking.date_formatted }} at {{ booking.time_formatted }} with {{ booking.host }}. Need to reschedule? {{ booking.manage_url }} - Contract:
{{ contact.first_name }}, your contract "{{ contract.title }}" is ready: {{ contract.sign_url }} - Group invite:
Welcome! Join {{ group.name }}: {{ group.invite_url }}
Tips, limits and best practices
- Always send a test. It is the fastest way to see whether the variable brought the value you expected.
- Missing data becomes empty. Write the sentence so it still reads correctly without the value β avoid "Your order of has arrived".
- In campaigns, take extra care. If a variable used in the approved template does not resolve for a recipient, that recipient is skipped. Prefer variables you are certain exist.
- Tax ID: in messages the tax ID only ever appears masked (
{{ contact.masked_tax_id }}). The full document is exclusive to contracts, which the person signs themselves. - Do not invent variables. If it is not in the picker it does not exist, and it will render empty.
- Where the value does not show. Outside a conversation (macro, canned response, campaign, contract template) there is no contact yet, so the list shows only the variable name. That is the correct behaviour: the variable has no owner there yet.
- If your role hides a field, the value shows hidden too. An agent who sees
a***@example.comon the contact record seesa***@example.comin the variable list β it is the sent message that carries the real value.
Troubleshooting
| Symptom | Likely cause | What to do |
|---|---|---|
The message arrived with a literal {{ ... }} |
The variable was typed into a field that does not resolve variables | Use the picker β it only appears where variables work |
| The variable came out empty | The data does not exist for that contact | Check the record; rewrite the sentence to work without the value |
The value rendered as 1500.0 |
You used the raw form | Switch to {{ ...value_formatted }} |
| The date is off by one day | The account timezone is not what you expected | Adjust the timezone in Account settings |
| The campaign skipped recipients | A variable in the approved template had no value | Review the template and use safer variables |
| The "undefined variables" warning appears on a variable that works | The variable genuinely has no value for this contact | Look at the value next to it in the list: if it is blank, the data is not on record |