Integration

    Liquid Templates

    Dynamic content rendering with Liquid templating for emails, notifications, and personalised content.

    Liquid Templates in SmartLinks

    Liquid is a templating language that allows you to dynamically insert data into text content. SmartLinks uses Liquid Templates in various APIs—such as email templates, notification messages, and dynamic content—to personalize communications with real-time data from your collections, products, proofs, and users.


    What are Liquid Templates?

    Liquid is an open-source template language created by Shopify. It uses a simple syntax with two main components:

    • Output tags {{ }} — Insert dynamic values
    • Logic tags {% %} — Control flow (if/else, loops, etc.)

    Basic Example

    Hello {{ contact.name }},
    
    Thank you for registering your {{ product.name }}!
    Your proof ID is: {{ proof.id }}
    
    {% if proof.claimed %}
    This item was claimed on {{ proof.claimedAt | date: "%B %d, %Y" }}.
    {% endif %}
    

    Core Data Objects

    SmartLinks provides several core objects that can be accessed in Liquid Templates. The available objects depend on the context (e.g., a proof-level template has access to proof, product, and collection).


    Collection

    A Collection represents a top-level business, brand, or organization. All products belong to a collection.

    FieldTypeDescription
    collection.idstringUnique identifier
    collection.titlestringDisplay title of the collection
    collection.descriptionstringDescription text
    collection.shortIdstringShort identifier for the collection
    collection.logoImage.urlstringURL to the collection's logo image
    collection.logoImage.thumbnails.x100string100px thumbnail
    collection.logoImage.thumbnails.x200string200px thumbnail
    collection.logoImage.thumbnails.x512string512px thumbnail
    collection.headerImage.urlstringURL to collection header/hero image
    collection.headerImage.thumbnails.*stringHeader image thumbnails (x100, x200, x512)
    collection.loaderImage.urlstringURL to collection loader image
    collection.primaryColorstringPrimary theme color (hex code)
    collection.secondaryColorstringSecondary theme color (hex code)
    collection.darkbooleanWhether dark mode is enabled
    collection.portalUrlstringURL for the collection's portal
    collection.redirectUrlstringCustom domain redirect URL
    collection.rolesobjectUser roles mapping (userId → role)
    collection.groupTagsarrayArray of group tag names
    collection.languagesarrayArray of supported language objects
    collection.defaultAuthKitIdstringDefault auth kit ID
    collection.allowAutoGenerateClaimsbooleanAllow claiming without proof ID

    Example Usage

    Welcome to {{ collection.title }}!
    
    {% if collection.portalUrl %}
    Visit our portal at {{ collection.portalUrl }}
    {% endif %}
    
    {% if collection.logoImage %}
    <img src="{{ collection.logoImage.url }}" alt="{{ collection.title }} logo" />
    <!-- Or use a thumbnail: -->
    <img src="{{ collection.logoImage.thumbnails.x200 }}" alt="{{ collection.title }} logo" />
    {% endif %}
    
    {% if collection.dark %}
    <!-- Dark mode is enabled -->
    {% endif %}
    

    Product

    A Product represents a type or definition of a physical or digital item. Products belong to a collection and can have many proofs (instances).

    FieldTypeDescription
    product.idstringUnique identifier
    product.namestringProduct name
    product.collectionIdstringID of the parent collection
    product.descriptionstringProduct description
    product.gtinstringGlobal Trade Item Number
    product.typestringProduct type from standard types
    product.heroImage.urlstringPrimary product image URL
    product.heroImage.thumbnails.x100string100px thumbnail
    product.heroImage.thumbnails.x200string200px thumbnail
    product.heroImage.thumbnails.x512string512px thumbnail
    product.tagsobjectTag map with boolean values
    product.dataobjectFlexible key-value data map
    product.adminobjectAdmin-only configuration
    product.admin.allowAutoGenerateClaimsbooleanAllow claiming without proof ID
    product.admin.lastSerialIdnumberLast generated serial ID

    Example Usage

    Your {{ product.name }}
    
    {{ product.description }}
    
    {% if product.gtin %}
    GTIN: {{ product.gtin }}
    {% endif %}
    
    {% if product.heroImage %}
    <img src="{{ product.heroImage.url }}" alt="{{ product.name }}" />
    <!-- Or use a thumbnail: -->
    <img src="{{ product.heroImage.thumbnails.x512 }}" alt="{{ product.name }}" />
    {% endif %}
    
    {% if product.tags.premium %}
    🌟 Premium Product
    {% endif %}
    
    {% if product.data.warranty_years %}
    Warranty: {{ product.data.warranty_years }} years
    {% endif %}
    

    Proof

    A Proof is a specific instance of a product—think of it as a unique digital certificate for a physical item. Proofs can be claimed by users and carry ownership information.

    FieldTypeDescription
    proof.idstringUnique identifier
    proof.collectionIdstringID of the parent collection
    proof.productIdstringID of the associated product
    proof.tokenIdstringUnique token identifier
    proof.userIdstringUser ID of the owner
    proof.claimablebooleanWhether the proof can be claimed
    proof.virtualbooleanWhether this is a virtual proof
    proof.valuesobjectArbitrary key-value pairs for proof data
    proof.createdAtdatetimeWhen the proof was created

    Note: Proof values object can contain any custom fields. Common examples:

    • proof.values.serialNumber - Serial number
    • proof.values.claimedAt - Claim timestamp
    • proof.values.status - Current status
    • proof.values.warrantyExpiry - Warranty expiration

    Example Usage

    Proof of Authenticity
    
    {% if proof.values.serialNumber %}
    Serial Number: {{ proof.values.serialNumber }}
    {% endif %}
    
    {% if proof.values.status %}
    Status: {{ proof.values.status }}
    {% endif %}
    
    {% if proof.claimable %}
    This item is available to claim.
    {% else %}
    This item has been claimed.
    {% endif %}
    
    {% if proof.virtual %}
    🌐 Digital Product
    {% endif %}
    
    {% if proof.values.claimedAt %}
    Claimed on: {{ proof.values.claimedAt | date: "%B %d, %Y at %H:%M" }}
    {% endif %}
    
    {% if proof.values.warrantyExpiry %}
    Warranty expires: {{ proof.values.warrantyExpiry | date: "%B %d, %Y" }}
    {% endif %}
    

    Contact

    A Contact represents a customer or user in the system. Contacts are associated with a collection and can own multiple proofs.

    FieldTypeDescription
    contact.contactIdstringUnique identifier
    contact.orgIdstringOrganization/collection ID
    contact.userIdstringLinked user ID (if authenticated)
    contact.emailstringPrimary email address
    contact.phonestringPrimary phone number
    contact.emailsarrayArray of all email addresses
    contact.phonesarrayArray of all phone numbers
    contact.firstNamestringFirst name
    contact.lastNamestringLast name
    contact.displayNamestringDisplay name
    contact.companystringCompany name
    contact.avatarUrlstringProfile picture URL
    contact.localestringPreferred language/locale (e.g., "en", "de")
    contact.timezonestringPreferred timezone
    contact.tagsarrayArray of tag strings for segmentation
    contact.sourcestringHow the contact was created
    contact.notesstringAdmin notes
    contact.externalIdsobjectExternal system IDs
    contact.customFieldsobjectCustom key-value data
    contact.createdAtdatetimeWhen the contact was created
    contact.updatedAtdatetimeWhen the contact was last updated

    Example Usage

    Hi {{ contact.firstName | default: contact.displayName | default: "there" }},
    
    {% if contact.locale == "de" %}
    Willkommen!
    {% elsif contact.locale == "fr" %}
    Bienvenue!
    {% else %}
    Welcome!
    {% endif %}
    
    {% if contact.phone %}
    We'll send updates to {{ contact.phone }}.
    {% endif %}
    
    {% if contact.company %}
    Company: {{ contact.company }}
    {% endif %}
    
    {% if contact.customFields.vip %}
    🌟 VIP Customer
    {% endif %}
    

    User (Account)

    A User represents an authenticated account in the system. This is typically the logged-in user performing an action.

    FieldTypeDescription
    user.uidstringUnique identifier
    user.emailstringEmail address
    user.displayNamestringDisplay name
    user.accountDataobjectAccount-specific data and settings

    Example Usage

    Logged in as: {{ user.displayName }} ({{ user.email }})
    
    {% if user.accountData.preferences.notifications %}
    Notifications are enabled.
    {% endif %}
    

    Attestation

    An Attestation is flexible data attached to a specific proof. It's used to store additional information like warranty registrations, tasting notes, service records, etc.

    FieldTypeDescription
    attestation.idstringUnique identifier
    attestation.publicobjectPublic attestation data (varies by type)
    attestation.privateobjectPrivate attestation data (varies by type)
    attestation.proofobjectAssociated proof reference/data
    attestation.createdAtdatetimeWhen the attestation was created
    attestation.updatedAtdatetimeWhen the attestation was last updated

    Note: The public and private objects contain custom fields based on your use case.

    Example Usage

    {% if attestation.public.type == "warranty_registration" %}
    Warranty Registration Details:
    - Registered: {{ attestation.createdAt | date: "%B %d, %Y" }}
    - Purchase Date: {{ attestation.public.purchaseDate }}
    - Store: {{ attestation.public.storeName }}
    {% endif %}
    
    {% if attestation.public.type == "tasting_note" %}
    🍷 Tasting Note:
    "{{ attestation.public.notes }}"
    Rating: {{ attestation.public.rating }}/5
    {% endif %}
    
    {% if attestation.private.internalNotes %}
    <!-- Private data only visible to admins -->
    Notes: {{ attestation.private.internalNotes }}
    {% endif %}
    

    Liquid Filters

    Liquid provides built-in filters to transform data. Common filters include:

    Text Filters

    FilterDescriptionExample
    upcaseConvert to uppercase{{ product.name | upcase }}
    downcaseConvert to lowercase{{ product.name | downcase }}
    capitalizeCapitalize first letter{{ contact.name | capitalize }}
    truncateLimit string length{{ product.description | truncate: 100 }}
    strip_htmlRemove HTML tags{{ content | strip_html }}
    escapeHTML escape special chars{{ user_input | escape }}
    defaultFallback value if empty{{ contact.name | default: "Customer" }}

    Date Filters

    FilterDescriptionExample
    dateFormat a date{{ proof.claimedAt | date: "%B %d, %Y" }}

    Common date formats:

    • %B %d, %Y → January 15, 2025
    • %Y-%m-%d → 2025-01-15
    • %d/%m/%Y → 15/01/2025
    • %H:%M → 14:30

    Array Filters

    FilterDescriptionExample
    joinJoin array elements{{ product.tags | join: ", " }}
    firstGet first element{{ product.images | first }}
    lastGet last element{{ product.images | last }}
    sizeGet array length{{ product.tags.size }}
    sortSort array{{ items | sort: "name" }}

    Number Filters

    FilterDescriptionExample
    plusAdd{{ count | plus: 1 }}
    minusSubtract{{ total | minus: discount }}
    timesMultiply{{ price | times: quantity }}
    divided_byDivide{{ total | divided_by: 2 }}
    roundRound number{{ average | round: 2 }}

    Control Flow

    Conditionals

    {% if proof.claimed %}
      This item is claimed.
    {% elsif proof.status == "pending" %}
      Claim pending verification.
    {% else %}
      Available to claim.
    {% endif %}
    
    {% unless contact.email %}
      No email on file.
    {% endunless %}
    

    Operators

    OperatorDescription
    ==Equals
    !=Not equals
    >Greater than
    <Less than
    >=Greater than or equal
    <=Less than or equal
    orLogical OR
    andLogical AND
    containsString/array contains
    {% if product.tags contains "premium" %}
      🌟 Premium Product
    {% endif %}
    
    {% if contact.email and proof.claimed %}
      Send confirmation to {{ contact.email }}
    {% endif %}
    

    Loops

    {% for tag in product.tags %}
      <span class="tag">{{ tag }}</span>
    {% endfor %}
    
    {% for image in product.images limit: 3 %}
      <img src="{{ image }}" alt="{{ product.name }} image {{ forloop.index }}" />
    {% endfor %}
    

    Loop variables:

    • forloop.index — Current iteration (1-indexed)
    • forloop.index0 — Current iteration (0-indexed)
    • forloop.first — Is this the first iteration?
    • forloop.last — Is this the last iteration?
    • forloop.length — Total number of iterations

    Common Use Cases

    Email Templates

    Subject: Your {{ product.name }} has been registered!
    
    Hi {{ contact.firstName | default: contact.displayName | default: "there" }},
    
    Great news! Your {{ product.name }}{% if proof.values.serialNumber %} (Serial: {{ proof.values.serialNumber }}){% endif %} 
    has been successfully registered to your account.
    
    {% if product.data.warranty_years %}
    Your warranty is valid for {{ product.data.warranty_years }} years 
    from the date of purchase.
    {% endif %}
    
    If you have any questions, please contact {{ collection.title }} support.
    
    Best regards,
    The {{ collection.title }} Team
    

    Notification Messages

    🎉 {{ contact.firstName }}, your {{ product.name }} is now verified!
    {% if proof.values.shortCode %}Proof ID: {{ proof.values.shortCode }}{% endif %}
    

    Dynamic Content Blocks

    {% if proof.values.tier == "gold" %}
      <div class="gold-benefits">
        As a Gold member, you get exclusive access to...
      </div>
    {% elsif proof.values.tier == "silver" %}
      <div class="silver-benefits">
        Your Silver membership includes...
      </div>
    {% endif %}
    

    Multilingual Content

    {% case contact.locale %}
      {% when "de" %}
        Vielen Dank für Ihre Registrierung!
      {% when "fr" %}
        Merci pour votre inscription!
      {% when "es" %}
        ¡Gracias por registrarte!
      {% else %}
        Thank you for registering!
    {% endcase %}
    

    Accessing Nested Data

    Use dot notation to access nested fields in data objects:

    {{ product.data.manufacturer }}
    {{ attestation.public.warranty.expiryDate }}
    {{ contact.customFields.vip_level }}
    {{ proof.values.serialNumber }}
    

    For dynamic keys, you may need to use bracket notation (if supported):

    {{ product.data["custom-field"] }}
    

    Best Practices

    1. Always use default filter for optional fields to avoid blank output:

      {{ contact.displayName | default: contact.firstName | default: "Valued Customer" }}
      
    2. Escape user-generated content when outputting as HTML:

      {{ attestation.public.userNotes | escape }}
      
    3. Check for existence before accessing nested data:

      {% if proof.values.warranty %}
        Warranty: {{ proof.values.warranty.type }}
      {% endif %}
      
    4. Use meaningful fallbacks for a better user experience:

      Hi {{ contact.firstName | default: contact.displayName | default: "there" }},
      
    5. Format dates appropriately for the user's locale:

      {{ proof.createdAt | date: "%d %B %Y" }}
      

    API Context

    Different APIs provide different objects in the Liquid context:

    API / FeatureAvailable Objects
    Email Templatescollection, product, proof, contact, attestation
    Push Notificationscollection, product, proof, contact
    SMS Messagescollection, product, proof, contact
    Wallet Passescollection, product, proof, contact
    Journey Actionscollection, product, proof, contact, event
    Broadcast Campaignscollection, contact, segment

    Check the specific API documentation for the exact objects available in each context.


    Further Resources