Skip to main content
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}}
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.
You want to insert…Use thisRenders as
A text valueHi {{.FirstName}}!Hi Jane!
A value inside a sentenceYour 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.
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.
Template HTML (saved in SendX):
<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:
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:
<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 tagSource
{{.Name}}The recipient’s name field
{{.Email}}The recipient’s email field
{{.<anyKey>}}Any key you pass in customFields
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.

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 tagDescription
{{.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 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:
{{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):
<ul>
{{range .items}}
  <li>{{.name}} — {{.price}}</li>
{{end}}
</ul>

Helper functions

A few useful helpers are available inside tags (used as {{helper arg1 arg2}}):
HelperExampleDescription
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}}.
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.

Troubleshooting

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