Skip to main content
Shortcodes personalize your emails. Write {{ contact.firstName }} once, and every recipient sees their own name.
Renders as: Hi Ada, thanks for shopping with Acme Supply Co! This page is the full syntax reference. To insert a tag from the editor toolbar instead of writing it by hand, see Personalization.

Where each group works

Not every shortcode works in every email. Using one outside its context leaves it blank rather than raising an error.

Customer details

Works in every email — broadcasts and automations.
Renders as Hi Ada, — or Hi there, for a contact with no first name.
  • Always add | default:. Most contacts have only an email address, so without a fallback your email opens Hi ,.
  • Always write contact. in front. {{ firstName }} on its own silently fails in broadcast emails.
  • {{ contact.fullName }} shows the email address when both names are blank. For greetings, {{ contact.firstName | default: 'there' }} is safer.

Store

Works in every email.
  • In broadcasts these work in the email body but not in the subject line. In automations they work in both.
  • {{ retainful_shop_url_with_coupon_code }} requires a coupon step in the workflow. In a broadcast it drops the coupon silently and links to your store.

Abandoned cart

Abandoned-cart automations only. In any other email these render blank.
  • Keep the .size > 0 check. Without it, an email with no cart still shows the button, linking nowhere. See Hide empty values correctly.
  • The coupon version also requires a coupon step in the workflow.

Coupon

Automations with a coupon step only. These never work in broadcasts.
Renders as: Your code: SAVE10XYZAB. Valid for 7 days. Expires March 05, 2026
  • Each contact receives their own unique code.
  • Always apply | date: to {{ retainful_coupon_expiry_date }}. On its own it prints a raw timestamp.
  • The expiry clock starts when the coupon is created, so every email in a sequence shows the same date.
  • Keep the .size > 0 check. With no coupon step, {{ coupon.expiration_days }} prints 0 and the rest print blank, so the email reads “Valid for . (0 days)”.

Summary block

Lists the items your customer ordered or left in their cart. Drag the block in and the editor writes the loop for you — you only edit what is inside it.
Automation templates only. The Summary block is not available in broadcast campaigns.
Stripped of its layout tables and Outlook-only comments, a default Summary block is:
This produces one entry per item: an image, then Classic White T-Shirt × 2, then $59.98.
{{ item.price }} is already the line total — never multiply it. {{ item.price | times: item.quantity }} counts the quantity twice: a 2 × $29.99 line shows $119.96 instead of $59.98. For “unit × qty = total”, write {{ item.unitPrice }} × {{ item.quantity }} = {{ item.price }}.
  • Show or hide the name and price, and set their colour and size, in the block’s settings panel. You do not need to touch the shortcodes.
  • The Review block is the same thing with a button, but its loop variable is line, not item — write {{ line.name }} there.
  • If you edit the source: keep the htmlmin:ignore HTML comments shown above (they stop the loop tags being mangled), do not rename item, and do not change event.line_items. limit:9999 means no limit — lower it to cap how many items show.
  • In preview it shows placeholder textProduct Title, Quantity, $xx.xx, and a grey image. Real values appear on send.

Unsubscribe

Works in every email.
Unique per contact, and legally required in marketing email. If your email has no unsubscribe link, Retainful adds one automatically — but placing it yourself looks better. See Deliverability best practices.

Four rules worth knowing

Always add a fallback

default covers every “no value” case — blank, never set, or missing.

Hide empty values correctly

To hide a block when a shortcode has no value, test .size > 0: Correct:
Incorrect — the block always shows:
The plain form looks right but does not work. An empty value is still “something” as far as Liquid is concerned, so the block renders anyway, with a blank inside it. That is what produces an anchor tag with an empty href — a real button that goes nowhere. .size > 0 is safe on every shortcode in this document.

Typos are invisible

{{ contact.frstName }} renders as empty space. No error, no warning. Proofread, and always send a test email.
A test proves your spelling is right, not that the data will be there. A test send fills in sample values even for shortcodes that come back blank on a real send.

Do not invent tags

Only use what is in this document. A made-up {% tag %} — such as {% current_year %}breaks the whole email: every shortcode disappears and the raw tag is shown to your customer. For the current year, use {{ "now" | date: "%Y" }}.