AMPscript in Marketing Cloud: fallbacks, templates and SAP
An AMPscript guide for Marketing Cloud: a first name that never prints blank, Content Builder template slots, email limits, and SAP with DKIM and DMARC.
250,000
On this page
- Personalization strings or AMPscript?
- How do I give a first name a fallback in AMPscript?
- Does the fallback work in the subject line and preheader?
- How do I test personalization before sending?
- How do Lookup, Concat, FormatDate and Replace work?
- How do I create an email template in Content Builder?
- What are Marketing Cloud's email limits?
- What does the Sender Authentication Package include?
- Which DNS records does Marketing Cloud need?
- Does Marketing Cloud's DKIM align with my From domain?
- How do I check authentication once it is live?
- Sources
AMPscript is the scripting language of Salesforce Marketing Cloud Engagement. Code between %%[ and ]%% runs when the email is sent, and %%= and =%% print a value inline (Salesforce). For a first name that never prints blank, read the field with AttributeValue(), test it with Empty() and print a fallback with v(), because a plain personalization string appears blank when the attribute is empty.
This guide covers Marketing Cloud Engagement (Email Studio and Content Builder), not Account Engagement, formerly Pardot: the AMPscript an email builder needs, Content Builder templates, the documented limits, and the Sender Authentication Package.
Personalization strings or AMPscript?
A personalization string is an attribute name between double percent signs, such as %%First_Name%%. Strings work in the subject line, preheader and body, are case-insensitive, and appear blank when the attribute is empty. Salesforce's advice is to define a default for every attribute you use this way.
Two names catch people out. %%firstname%% is a system string that returns the word before the first space in the Full Name profile attribute, and Full Name, Email and UserDefined are the only profile attributes an account starts with. Salesforce's list of strings says not to use any of them as a column name in a sendable data extension, so call the column First_Name, as Salesforce's own example does, not FirstName.
The rest of the syntax, from Adding AMPscript to Your Content and Variables:
| You write | What it is |
|---|---|
%%First_Name%% | A personalization string. Prints the attribute or column, or nothing when it is empty. |
%%firstname%% | A system string: the first word of the Full Name profile attribute. |
%%[ ... ]%% | An AMPscript block. Declares variables and runs several functions. |
%%= ... =%% | Inline AMPscript. One function, which may nest others, and prints its result. |
<script runat="server" language="ampscript"> | The tag form of a block, written the way server-side JavaScript is. |
@firstName | A variable. Names always begin with the at sign. |
How do I give a first name a fallback in AMPscript?
Salesforce's help page gives this pattern, with "Customer" as the fallback:
%%[IF Empty(First_Name) == "False" THEN SET @FN = First_Name ELSE SET @FN = "Customer" ENDIF]%%Hi %%=v(@FN)=%%It assumes First_Name exists in the data you send to. The developer reference prefers AttributeValue(), which searches the subscriber profile, the sendable data extension and journey data, and returns null when it finds nothing instead of failing the send. With the reference's Iif() greeting:
%%[
Var @firstName, @greeting
Set @firstName = Trim(AttributeValue("First_Name"))
Set @greeting = Iif(Empty(@firstName), "Hi there,",
Concat("Hi ", @firstName, ","))
]%%
<p>%%=v(@greeting)=%%</p>Line by line:
Vardeclares variables, which start as null and can only be declared in a block.Setassigns one.Trim()strips white space from both ends, becauseEmpty()is true only for an empty string or null, and a field holding one space would printHi ,.Iif()returns its second argument when the condition is true, and its third otherwise.Concat()joins strings, and spaces have to be written in.v()prints the variable.
When the name lives in another data extension, use Lookup(). It returns the first matching row, and the search column and value are case-sensitive, so match on a unique key. With sample names:
%%[
Var @owner
Set @owner = Lookup("Account_Owners", "Owner_First_Name", "Account_ID", AttributeValue("Account_ID"))
If Empty(@owner) Then
Set @owner = "your account team"
EndIf
]%%Without code, a profile attribute can carry a default value for every subscriber who has none. Salesforce's page does not say it covers a data extension column of the same name.
Does the fallback work in the subject line and preheader?
Yes, with a catch. AMPscript runs in a fixed order: preheader, HTML body, text body, then subject line. A variable set in the HTML body keeps its value in the text body and subject line, but the preheader has already run:
Preheader
Runs first, before the block has set anything
%%=v(@greeting)=%% here are Tuesday's notes
First_Name is Ada(nothing) here are Tuesday's notes
First_Name is empty(nothing) here are Tuesday's notes
HTML body
Runs second. The block sets @greeting here
%%[ ... Set @greeting = Iif(Empty(@firstName), "Hi there,", Concat("Hi ", @firstName, ",")) ]%% <p>%%=v(@greeting)=%%</p>First_Name is AdaHi Ada,
First_Name is emptyHi there,
Text body
Runs third, with the value the HTML body set
%%=v(@greeting)=%%
First_Name is AdaHi Ada,
First_Name is emptyHi there,
Subject line
Runs last, with the same value
%%=v(@greeting)=%% your notes from Tuesday
First_Name is AdaHi Ada, your notes from Tuesday
First_Name is emptyHi there, your notes from Tuesday
Orange: the part that runs before the variable existsDashed: the greeting prints nothing
So a subject line can reuse %%=v(@greeting)=%%, while a preheader that greets by name needs its own inline Iif().
In links, Salesforce asks you not to include personal data such as an email address. For a URL built from data, write the <a> tag in the email and put RedirectTo() in its href, which is how Salesforce says click tracking is kept:
<a href='%%=RedirectTo(@link)=%%'>%%=v(@type)=%%</a>Note
AttributeValue() and RedirectTo() as unavailable in Marketing Cloud Next.How do I test personalization before sending?
Preview and Test renders the email with one subscriber's data and sends tests only to the addresses you enter. A data extension can be previewed if it has an email address field or links to subscribers by ID or key. Three cautions from the same page: a test send counts against the sends you bought, attribute changes made during preview are permanent, and a test to an unsubscribed, bounced or held subscriber never arrives.
Test with a row for each case the code handles: a normal name, an empty field, a single space, and an account missing from the lookup table.
How do Lookup, Concat, FormatDate and Replace work?
Syntax copied from Salesforce's reference:
| Function | Syntax | What to know |
|---|---|---|
| Lookup | Lookup(dataObject, returnColumn, searchColumn1, searchValue1, [searchColumn2, searchValue2...]) | Returns the first match. Add column and value pairs to narrow it. |
| Concat | Concat(string1, string2, [string3], ...) | Takes as many strings as you pass. |
| FormatDate | FormatDate(dateString, dateFormat, timeFormat, localeCode) | Uses .NET format strings in Engagement: "d MMMM yyyy" gives 5 August 2023. |
| Replace | Replace(sourceString, searchSubstring, replacementSubstring) | Replaces every occurrence, not just the first. |
FormatDate matters for one more reason: date strings such as %%xtlongdate%% print in US format.
How do I create an email template in Content Builder?
Content Builder makes a template from pasted HTML or an existing template, and both open in the same editor. A region is editable when it is a content area: a div with data-type="slot". Salesforce's example (Add Content to a Paste HTML Template) allows one block of five types and sets the hint shown while the area is empty:
<div data-type="slot" data-key="Hero1" data-max-blocks="1" data-allowed-blocks="imageblock; textblock; freeformblock; abtestblock; dynamicblock" data-label="Place Hero Image Here"></div><body>
<!-- the Track Email Opens snippet -->
Open tracking
Inserted from Insert Code Snippet, immediately after the opening body tag.
<custom type="header" />
Account header
Where the account's header goes. Only paste HTML templates take this tag.
<table role="presentation" width="600"> <tr><td><img src="logo.png" alt="Logo"></td></tr> </table>
Fixed HTML
Outside any content area. Code View is locked in an email made from the template, so this stays as written.
<div data-type="slot" data-key="hero"
data-max-blocks="1" data-allowed-blocks="imageblock"
data-label="Hero image here"></div>Content area, restricted
One block, images only. The label is the hint shown while the area is empty.
<div data-type="slot" data-key="main"
data-label="Drop blocks or content here"></div>Content area, open
Any number of blocks of any type, the default.
<custom type="footer" />
Account footer
The standard footer carries the profile center link, the unsubscribe link and the postal address.
</body>
Orange: the attribute that makes a region editable
The rules for content areas, from the same two pages:
- Keys. Alphanumeric, under 17 characters, no spaces or capitals, though Salesforce's own
Hero1has one. Reserved words such ascontent,name,designandtagscan make a template unusable once saved. - Locking. A filled area can be locked so emails made from the template cannot change it.
- No cascade. Editing a template does not change emails already made from it.
Where Content Builder changes your HTML
HTML content blocks can add extra code wrappers, so Salesforce recommends code snippets for code that must stay exact. ContentBlockByKey() and its siblings also pull in the table the editor wraps around a block, which a code snippet avoids. In an email made from a template, Code View is locked.
What every template has to include
A paste HTML template carries what the account would otherwise add: <custom type="header" /> and <custom type="footer" /> where those belong, and the open-tracking snippet right after the opening <body> tag. Every email must include a profile center link, an unsubscribe link and your business address:
%%profile_center_url%%
%%unsub_center_url%%
%%Member_Busname%%
%%Member_Addr%%
%%Member_City%%
%%Member_State%%
%%Member_PostalCode%%
%%Member_Country%%The standard footer holds them; text-only emails need them added by hand. Setup, Email Optional Features can switch the checks off, but Salesforce says you stay responsible for the legally required content.
What are Marketing Cloud's email limits?
From Salesforce's Email Limits and Guidelines, as of October 2026:
| Limit | Value |
|---|---|
| Sending on shared IP addresses | 250,000 emails a month. Above that Salesforce requires dedicated IPs, though it does not throttle |
| One dedicated IP address | 2 million emails a day, a guideline for a warmed-up address |
| Warming up a new dedicated IP | 31 days |
| Attachment size | 5 MB |
| Message body | 100 KB or less, so webmail clients do not truncate it |
| Email archive retention | 21 days |
| Unsent triggered messages | Expire after 3 days in the queue |
The body figure is a tip, and it sits close to the size at which Gmail clips a message. The page sets no template width.
What does the Sender Authentication Package include?
The Sender Authentication Package (SAP) puts your domain on everything a message carries: account branding for tracked links, images and the view-as-webpage link; a private domain for the From address, authenticated with SPF, Sender ID and DKIM; CloudPages domains; a dedicated IP address; and Reply Mail Management. Senders of more than 250,000 emails a month must use it. Corporate and Pro editions include one SAP license, and Enterprise five.
A Private Domain on its own is the authentication part: DKIM, SPF and DMARC for the sending domain, without link branding or a dedicated IP, which Salesforce says suits any volume.
To set either up, as of October 2026 (Salesforce):
- In Setup, type
sslin Quick Find, open Domain SSL Certificates and click New Domain. - Enter the domain and an address for DNS and certificate notices.
- Choose email authentication and URL branding (SAP) or email authentication only (Private Domain).
- Choose the shared IP pool (under 100,000 emails a month), a new dedicated IP (over 250,000) or one you have, and whether to use Reply Mail Management.
- Choose how to manage DNS, then submit.
The status moves from Pending DNS validation to In progress, which can take up to five business days, to Active.
Which DNS records does Marketing Cloud need?
Either delegate the domain or a subdomain to Salesforce, which takes four NS records, ns1.exacttarget.com through ns4.exacttarget.com, or host DNS yourself and apply the zone file you download from the domain's action menu. Salesforce's sample zone for a sending domain of sample.example.com:
| Host | Type | What it does |
|---|---|---|
sample.example.com | MX | Lets Marketing Cloud's servers send |
bounce.sample.example.com | MX and TXT | Handles bounces, with its own SPF record |
reply.sample.example.com | MX and TXT | Reply Mail Management, with its own SPF record |
leave.sample.example.com | MX | Unsubscribes |
image, view, click, pages, cloud | CNAME | Images, view as web page, tracked clicks, landing pages, CloudPages |
mta.sample.example.com | A | The dedicated IP address |
example._domainkey.sample.example.com | TXT | The DKIM key, under the selector example |
sample.example.com | TXT | SPF, authorizing the bounce host |
_dmarc.sample.example.com | TXT | DMARC |
The sample selector is a placeholder; your zone file carries the real one. Hyperforce accounts get their entries later in setup instead.
Does Marketing Cloud's DKIM align with my From domain?
Yes, when the From address uses the SAP or private domain. Salesforce's Gmail compliance FAQ says SPF, DKIM and DMARC then typically pass, on self-hosted DNS once its TXT records are in place. Its troubleshooting article adds that sending from a domain not set up as SAP or a private domain fails authentication by default.
The sample zone shows why it aligns: the DKIM key sits under the sending domain, so the signing domain is yours, and the bounce host is its subdomain, so SPF aligns under DMARC's relaxed rule too (how alignment works).
On a delegated domain, Salesforce Support edits the DMARC record, including a rua address for reports, when you send them the record you want. Commercial sends get a List-Unsubscribe header automatically, which the bulk sender rules expect. Your main website domain can be the private domain, but then you host its records, and Support does not troubleshoot self-hosted DNS.
How do I check authentication once it is live?
Send tests to Gmail and Outlook.com, not only to your own company. Salesforce's troubleshooting article explains that corporate security gateways scan bodies and rewrite links, which breaks a DKIM signature even when the setup is right, so a failure seen only at corporate domains is usually not Marketing Cloud's. Then read the Authentication-Results line in a test's full header (Show original, in Gmail).
The free editor exports HTML with personalization strings, an inline Iif() fallback and %%unsub_center_url%% written in. Before the copy goes into Marketing Cloud, draft it in the free mail-merge preview, which shows each message against a sample of your list and flags every empty merge field, and run the email domain checker on your sending domain. Draftship also builds and runs outbound inside a client's own platform.
Sources
- developer.salesforce.comCode between %%[ and ]%% runs when the email is sent, and %%= and =%% print a value inline
- help.salesforce.comappears blank when the attribute is empty
- help.salesforce.comthe word before the first space in the Full Name profile attribute
- help.salesforce.comonly profile attributes an account starts with
- developer.salesforce.comThe rest of the syntax, from Adding AMPscript to Your Content and Variables
- developer.salesforce.comThe developer reference prefers AttributeValue(), which searches the subscriber profile, the sendable data extension and journey data, and returns null when it finds nothing instead of failing the send
- developer.salesforce.comWith the reference's Iif() greeting
- developer.salesforce.comstrips white space from both ends
- developer.salesforce.comTrim() strips white space from both ends, because Empty() is true only for an empty string or null, and a field holding one space would print Hi
- developer.salesforce.comspaces have to be written in
- developer.salesforce.comv() prints the variable
- developer.salesforce.comWhen the name lives in another data extension, use Lookup()
- help.salesforce.comWithout code, a profile attribute can carry a default value for every subscriber who has none
- developer.salesforce.compreheader, HTML body, text body, then subject line
- developer.salesforce.comFor a URL built from data, write the <a> tag in the email and put RedirectTo() in its href, which is how Salesforce says click tracking is kept
- help.salesforce.comrenders the email with one subscriber's data
- developer.salesforce.comFormatDate(dateString, dateFormat, timeFormat, localeCode). Uses .NET format strings in Engagement: "d MMMM yyyy" gives 5 August 2023.
- developer.salesforce.comReplace(sourceString, searchSubstring, replacementSubstring). Replaces every occurrence, not just the first.
- help.salesforce.comopen in the same editor
- help.salesforce.comAdd Content to a Paste HTML Template
- developer.salesforce.comContentBlockByKey() and its siblings also pull in the table the editor wraps around a block, which a code snippet avoids
- help.salesforce.comEvery email must include a profile center link, an unsubscribe link and your business address
- help.salesforce.comEmail Limits and Guidelines
- help.salesforce.comThe Sender Authentication Package (SAP) puts your domain on everything a message carries: account branding for tracked links, images and the view-as-webpage link; a private domain for the From address, authenticated with SPF, Sender ID and
- help.salesforce.comA Private Domain on its own is the authentication part: DKIM, SPF and DMARC for the sending domain, without link branding or a dedicated IP, which Salesforce says suits any volume
- help.salesforce.comTo set either up, as of October 2026
- help.salesforce.comSalesforce's sample zone for a sending domain of sample.example.com
- help.salesforce.comSalesforce's Gmail compliance FAQ says SPF, DKIM and DMARC then typically pass, on self-hosted DNS once its TXT records are in place
- help.salesforce.comIts troubleshooting article adds that sending from a domain not set up as SAP or a private domain fails authentication by default
- support.google.comThen read the Authentication-Results line in a test's full header (Show original, in Gmail)