> ## Documentation Index
> Fetch the complete documentation index at: https://docs.useboom.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Template variables

> How to bind a WhatsApp template's placeholders to live data in a journey.

A `SEND_MESSAGE` node sends an approved WhatsApp template. Templates have
positional placeholders (`{{1}}`, `{{2}}`, …). You fill each one by setting
**`templateBindings`** on the node — a map from a placeholder to the **path** of
the value it should carry:

```json theme={null}
{
  "templateBindings": {
    "1": "customer.name",
    "2": "person.bank"
  }
}
```

If a placeholder has no binding, or its path doesn't resolve, the message goes
out with that slot blank. So bind every placeholder, and use a path that
actually resolves.

## Where the values come from

A binding path starts with one of three roots — the data available when the
message is sent:

| Root           | What it is                                                                      | Examples                                                                                                                                                                               |
| -------------- | ------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `customer.*`   | Built-in fields on the contact                                                  | `customer.name`, `customer.lastName`, `customer.phoneNumber`, `customer.email`                                                                                                         |
| `person.*`     | **Custom person attributes** from your CDP (the ones you set via people upsert) | `person.bank`, `person.plan`, `person.city`                                                                                                                                            |
| `engagement.*` | Values produced during this journey run                                         | `engagement.extracted.<field>` (from the conversation), `engagement.workflowState.<field>` (event/segment data), `engagement.nodeOutputs.<nodeId>.*` (an earlier HTTP node's response) |

A custom attribute you set on a person (for example `bank`) is referenced as
**`person.bank`** — not `attributes.bank`.

<Warning>
  `attributes.<key>` is the syntax for **DECISION / CASE conditions**, a
  different part of a journey. It does **not** work in a message binding. In a
  `SEND_MESSAGE` template binding, a custom person attribute is always
  `person.<key>`.
</Warning>

## Discover the valid variables

Rather than guess, ask the API (or MCP) for the exact set a given journey can
bind — the same list the visual builder shows:

```http theme={null}
GET /api/v1/journeys/{journeyId}/message-variables
```

It returns the variables grouped by source, each with a ready-to-use `path`.
Copy a `path` straight into `templateBindings`.

## Validation

When you save or validate a journey, a binding whose path can't resolve (for
example `attributes.bank` or `customer.attributes.bank`) is reported as an
error and blocks publishing, so a broken binding never ships silently. Fix it by
switching to a path from the catalog above (custom attributes → `person.<key>`).
