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

# Personalization & Merge Tags

> Use merge tags to insert dynamic values — names, custom fields, links, and images — into email templates and transactional emails.

SendX lets you insert dynamic, per-recipient values into your email content using **merge tags**. Use them to greet a contact by name, drop in an order amount, render a dynamic image, or build a personalized button link.

## Syntax

SendX renders email content with **Go's template engine**, so every merge tag is wrapped in double curly braces and **must start with a leading dot (`.`)**:

```
{{.VariableName}}
```

<Warning>
  **The leading dot is required.** `{{.FirstName}}` works; `{{FirstName}}` (no dot) does **not** — it will fail to render and your template will appear to "not take effect." This is the single most common personalization mistake.
</Warning>

| You want to insert…       | Use this                                        | Renders as                                          |
| ------------------------- | ----------------------------------------------- | --------------------------------------------------- |
| A text value              | `Hi {{.FirstName}}!`                            | `Hi Jane!`                                          |
| A value inside a sentence | `Your order {{.orderId}} is confirmed.`         | `Your order A12345 is confirmed.`                   |
| A dynamic image           | `<img src="{{.imageUrl}}" />`                   | `<img src="https://cdn.example.com/x.png" />`       |
| A dynamic button/link     | `<a href="{{.buttonUrl}}">{{.buttonLabel}}</a>` | `<a href="https://app.example.com/pay">Pay now</a>` |

Merge tags work anywhere in the template — in the subject line, in body text, and inside HTML attributes like `src` and `href`.

## Personalizing transactional emails (`POST /send/template`)

When you send a transactional email with a saved template, you supply the per-recipient values in the **`customFields`** object on each `to` entry. Every key you pass becomes a merge tag of the same name.

<Note>
  Keys map **verbatim**: the key `orderId` populates `{{.orderId}}`, `amount` populates `{{.amount}}`, and so on. You do **not** need to pre-define these as custom fields in your account — any ad-hoc key works for transactional sends.
</Note>

**Template HTML (saved in SendX):**

```html theme={null}
<h1>{{.accountName}} update</h1>
<p>Hi {{.firstName}}, we received your payment of {{.amount}}.</p>
<img src="{{.imageUrl}}" alt="receipt" />
<a href="{{.buttonUrl}}">{{.buttonLabel}}</a>
```

**Request:**

```bash theme={null}
curl -X POST "https://api.sendx.io/api/v1/rest/send/template" \
  -H "Content-Type: application/json" \
  -H "X-Team-ApiKey: YOUR_API_KEY" \
  -d '{
    "from": { "email": "support@example.com", "name": "Acme" },
    "to": [
      {
        "email": "jane@acme.com",
        "name": "Jane",
        "customFields": {
          "accountName": "Acme",
          "firstName": "Jane",
          "amount": "$42.00",
          "imageUrl": "https://cdn.example.com/receipt.png",
          "buttonUrl": "https://app.example.com/pay/A12345",
          "buttonLabel": "View receipt"
        }
      }
    ],
    "subject": "{{.accountName}} update",
    "template": "template_qmK2dMPNoQDobfxI4yMM7k"
  }'
```

**Rendered email:**

```html theme={null}
<h1>Acme update</h1>
<p>Hi Jane, we received your payment of $42.00.</p>
<img src="https://cdn.example.com/receipt.png" alt="receipt" />
<a href="https://app.example.com/pay/A12345">View receipt</a>
```

### Variables available in transactional templates

The transactional send path builds a minimal data context. The following are available in a `/send/template` render:

| Merge tag       | Source                             |
| --------------- | ---------------------------------- |
| `{{.Name}}`     | The recipient's `name` field       |
| `{{.Email}}`    | The recipient's `email` field      |
| `{{.<anyKey>}}` | Any key you pass in `customFields` |

<Warning>
  In transactional templates, `{{.FirstName}}`, `{{.LastName}}`, and `{{.Company}}` exist but default to **empty** unless you pass them yourself in `customFields`. The richer native tags below (e.g. `{{.ViewInBrowserLink}}`, `{{.GlobalUnsubscribeLink}}`) are **only** populated for campaign, drip, and workflow sends — not transactional sends. For transactional unsubscribe handling, use the `List-Unsubscribe` header.
</Warning>

## Personalizing campaigns & automations

For campaigns, drip sequences, and workflow emails, SendX pulls values from the contact's stored profile, so a richer set of native tags is available automatically:

| Merge tag                    | Description                                               |
| ---------------------------- | --------------------------------------------------------- |
| `{{.FirstName}}`             | Contact's first name                                      |
| `{{.LastName}}`              | Contact's last name                                       |
| `{{.Name}}`                  | First + last name                                         |
| `{{.Email}}`                 | Contact's email address                                   |
| `{{.Company}}`               | Contact's company                                         |
| `{{.CreatedDate}}`           | Date the contact was added (`YYYY-MM-DD`)                 |
| `{{.ViewInBrowserLink}}`     | "View in browser" URL                                     |
| `{{.GlobalUnsubscribeLink}}` | Unsubscribe URL                                           |
| `{{.Address}}`               | Your account's physical mailing address (CAN-SPAM footer) |

Custom fields are referenced by their **name**, e.g. a custom field named `City` becomes `{{.City}}`. See [Custom field naming rules](#custom-field-naming-rules) below.

## Default (fallback) values

If a value might be missing or blank, wrap it with the `fallback` helper so the email never shows an empty gap:

```
Hi {{fallback .FirstName "there"}}!
```

If `FirstName` is empty, this renders `Hi there!`. The SendX editor inserts this form automatically for every merge tag, using `"there"` as the default — so review the defaults if you want something other than "there" to appear for blank values.

## Conditional content & loops

Because SendX uses Go templates, you can conditionally show content or loop over lists.

**Conditional:**

```html theme={null}
{{if .couponCode}}
  <p>Use code <strong>{{.couponCode}}</strong> for 10% off!</p>
{{else}}
  <p>Thanks for being a customer.</p>
{{end}}
```

**Loop** (the values must be passed as a list):

```html theme={null}
<ul>
{{range .items}}
  <li>{{.name}} — {{.price}}</li>
{{end}}
</ul>
```

## Helper functions

A few useful helpers are available inside tags (used as `{{helper arg1 arg2}}`):

| Helper                                         | Example                              | Description                                     |
| ---------------------------------------------- | ------------------------------------ | ----------------------------------------------- |
| `fallback`                                     | `{{fallback .FirstName "there"}}`    | Returns the default if the value is empty/blank |
| `capitalize_words`                             | `{{capitalize_words .city}}`         | Title-cases the value                           |
| `first_character`                              | `{{first_character .FirstName}}`     | First character (e.g. for an avatar initial)    |
| `formatdate`                                   | `{{formatdate .date "02 Jan 2006"}}` | Formats a date                                  |
| `todaydate` / `tomorrowdate` / `yesterdaydate` | `{{todaydate "02 Jan 2006"}}`        | Relative date helpers                           |
| `days_until` / `days_since`                    | `{{days_until .expiry}}`             | Day-count helpers                               |

## Custom field naming rules

When you create a custom field in SendX, the **name** you pick becomes its merge tag. A few rules apply:

* Names are sanitized to letters, digits, and underscores — any other character (including spaces) becomes `_`. A field named `Order ID` becomes the tag `{{.Order_ID}}`.
* These names are **reserved** (they collide with native attributes) and can't be used for custom fields: `email`, `firstname`, `lastname`, `company`, `name`.
* For transactional `customFields` keys, use the same rule of thumb — stick to letters, digits, and underscores, and don't start a key with a digit, so it can be addressed as `{{.key}}`.

<Note>
  The opaque IDs you see in the REST API (e.g. `custom_field_MnuqBAG2NPLm7PZMWbjQxt`) are **not** what you use in templates. Templates always reference custom fields by their human-readable **name**.
</Note>

## Troubleshooting

<AccordionGroup>
  <Accordion title="My merge tags aren't being replaced — the raw {{...}} shows up, or my changes don't take effect">
    The most common cause is a **missing leading dot**. Use `{{.FirstName}}`, not `{{FirstName}}`. Also confirm:

    * The variable name matches exactly (it's case-sensitive — `{{.firstName}}` ≠ `{{.FirstName}}`).
    * For transactional sends, the key exists in the recipient's `customFields` object.
    * You're editing and sending the correct template ID.
  </Accordion>

  <Accordion title="A value shows as blank or as 'there'">
    The field had no value for that recipient. Pass the value in `customFields` (transactional) or set it on the contact (campaigns). Empty values wrapped in `{{fallback .X "there"}}` render the default — change the default text if "there" isn't what you want.
  </Accordion>

  <Accordion title="A native tag like {{.ViewInBrowserLink}} is empty in a transactional email">
    Those campaign-level system variables aren't injected for transactional `/send/template` sends. Only `{{.Name}}`, `{{.Email}}`, and your `customFields` keys are available there.
  </Accordion>
</AccordionGroup>
