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
Authorizationheader) 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
collectionrole also feeds KPI/bar/line/pie chart blocks — see Binding charts to real data.
- 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 —
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.