## 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

1. Open any text field that accepts variables (composer, macro, canned response, campaign, follow-up,
   contract, booking reminder…).
2. Type `{{` — the variable list appears automatically.
3. Or click the **`{x}` Insert variable** button when it is available in the field header.
4. Search by name (for example "amount" or "due") and click to insert.
5. **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.
6. 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.com` on
  the contact record sees `a***@example.com` in 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 |

## See also

- [Account, agents and teams](/hc/ajuda/articles/administration-conta-agentes-equipes-en)