Documentation menu

Data sources & merge tags

Merge tags let you personalize every email with real data. Write {{field}} anywhere in a text block — Hi {{first_name}}, your {{plan}} renews soon — and each recipient sees their own values. A project can connect several data sources at once: each one gets a short alias, and fields from a non-audience source are written as {{alias.field}} (for example {{products.name}}).

Types of data source

Data sources are managed from the Data Sources section of your dashboard (contact lists live under Contacts). There are two types:

Hosted tables

A table stored in MailInApp. Define columns, add rows in the dashboard, and every column becomes a merge field. Best when your data lives in a spreadsheet today. Contact lists are hosted tables with a guaranteed email column.

API connections

Point MailInApp at your own HTTP endpoint that returns JSON. Rows are fetched server-side — from our servers, never from the recipient's inbox or your visitors' browsers.

Three ways to authenticate the connection, picked from the Authentication dropdown when you configure it:

  • Static headers — add request headers (for example an Authorization header) with a fixed value. The simplest option, and the only one that made sense before a token could expire.
  • OAuth2 Client Credentials — a token URL plus a client ID and secret. MailInApp exchanges them for an access token server-side, caches it, and refreshes it automatically before it expires — the common pattern for most API-key-and-secret enterprise integrations.
  • OAuth2 JWT Bearer — a token URL, issuer, subject, audience, and an RSA private key (PEM). MailInApp signs a fresh JWT assertion and exchanges it for an access token, with no interactive login and no refresh token to babysit — this is how Salesforce's server-to-server integrations authenticate (see the Salesforce integration guide), and it works the same way for a Google service account or any other IdP that supports the flow.

Whichever mode you pick, the resulting bearer token is injected as an Authorization header automatically — any additional headers you add still go out alongside it, merged in (a header literally named Authorization there is ignored, since the minted token always wins). All credential fields — header values, client secret, private key — follow the same rule:

  • stored server-side only,
  • never sent to the browser,
  • masked in every API response after you save them.

Editing a connection whose secret shows as masked and clicking Test these settings needs the real value re-entered first; Test saved connection instead runs the check against the credential exactly as it's stored, without ever sending it back to your browser.

Connecting sources: the Data panel

The studio's Data panel is where a project declares which sources it uses. + Add data source… connects one (up to 10 per project); each connection has three parts:

  • Alias — the short handle its merge tags use: lowercase letters, digits and underscores, starting with a letter (e.g. contacts, products, open_invoices). Renaming an alias automatically updates any repeat block bound to it.
  • Source — the hosted table, contact list or API connection behind it.
  • Role — how the email uses it:
    • Audience — the contact list the email sends to. At most one per project, and it must be a contact list. Its fields are the bare tags — {{first_name}}, {{email}} — resolved per recipient from their own row at send time. The audience also drives the "Preview as" selector and per-recipient response attribution.
    • Merge fields — fields readable under the alias: {{alias.field}}, resolved from the source's first row when the email renders. Use it for shared content — the featured product, this week's stats — rather than per-recipient data.
    • Repeat rows — rows that feed repeat blocks bound to the alias. Contributes no merge fields outside the repeat. The same collection role also feeds KPI/bar/line/pie chart blocks — see Binding charts to real data.

Every connected source lists its fields as clickable chips — click one to copy the exact merge tag, paste it into any text property. Display-condition editors group their field dropdown the same way: recipient (audience) fields plus one group per merge source.

Built-in tags

A handful of tags are provided by the platform itself rather than a data source — the studio's left-rail Variables panel lists them alongside any variables you define; click one to copy its tag.

  • {{recipient_email}} — the address the email is sent to.
  • {{today}} / {{now}} — the date (or date & time) the email is opened.
  • {{unsubscribe_url}} — a per-recipient one-click unsubscribe link. The Footer preset already includes it — see Sending to your contacts.

A per-recipient, single-use store discount code isn't a merge tag — drop the E-Commerce Discount block (Shopify/WooCommerce-connected audiences only) into the email instead, and it mints and displays its own code automatically. See Discount offer.

Built-in tags only resolve in real sends (manual, scheduled, or a "Backfill"-style test send) — the studio preview and canvas show them as empty or a placeholder, same as any field with no sample value.

Repeating content

The repeat block renders its children once per row of the source you bind it to — a product grid, a digest of articles, a list of open invoices. Pick the source by alias in the Inspector; inside the repeat, tags resolve against each repetition's own row.

Previewing with real data

The studio's preview-data selector renders the canvas with any row of the audience list, so you can check that {{first_name}} actually says Amina and not {{first_name}} before you send. Fields from other sources can be given preview values too.

Good to know

  • Fields that are missing for a recipient render as empty strings — design so that a blank value still reads naturally.
  • Hosted pages (live view, hosted forms) resolve merge tags per recipient at render time, so personalization survives even when a recipient leaves the inbox.
  • Projects built before multi-source support keep working unchanged: their single connected source appears in the Data panel automatically, and bare {{field}} tags always resolve against the audience.