Dynamic Fields

    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.namestringDisplay name of the collection
    collection.descriptionstringDescription text
    collection.slugstringURL-friendly identifier
    collection.logoUrlstringURL to the collection's logo image
    collection.websiteUrlstringPrimary website URL
    collection.metadataobjectCustom key-value metadata
    collection.createdAtdatetimeWhen the collection was created
    collection.updatedAtdatetimeWhen the collection was last updated

    Example Usage

    Welcome to {{ collection.name }}!
    
    {% if collection.websiteUrl %}
    Visit us at {{ collection.websiteUrl }}
    {% endif %}
    
    <img src="{{ collection.logoUrl }}" alt="{{ collection.name }} logo" />
    

    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.descriptionstringProduct description
    product.skustringStock keeping unit
    product.slugstringURL-friendly identifier
    product.imageUrlstringPrimary product image URL
    product.imagesarrayArray of image URLs
    product.categorystringProduct category
    product.tagsarrayArray of tag strings
    product.metadataobjectCustom key-value metadata
    product.createdAtdatetimeWhen the product was created
    product.updatedAtdatetimeWhen the product was last updated

    Example Usage

    Your {{ product.name }} (SKU: {{ product.sku }})
    
    {{ product.description }}
    
    {% if product.tags.size > 0 %}
    Tags: {{ product.tags | join: ", " }}
    {% endif %}
    
    {% for image in product.images %}
    <img src="{{ image }}" alt="{{ product.name }}" />
    {% endfor %}
    

    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.serialNumberstringHuman-readable serial number
    proof.claimedbooleanWhether the proof has been claimed
    proof.claimedAtdatetimeWhen the proof was claimed
    proof.claimedBystringUser ID of the claimer
    proof.statusstringCurrent status (e.g., "active", "transferred")
    proof.nfcTagIdstringAssociated NFC tag ID (if applicable)
    proof.qrCodestringQR code identifier
    proof.shortCodestringShort code for easy lookup
    proof.metadataobjectCustom key-value metadata
    proof.createdAtdatetimeWhen the proof was created
    proof.updatedAtdatetimeWhen the proof was last updated

    Example Usage

    Proof of Authenticity
    
    Serial Number: {{ proof.serialNumber }}
    Status: {{ proof.status }}
    
    {% if proof.claimed %}
    Claimed on: {{ proof.claimedAt | date: "%B %d, %Y at %H:%M" }}
    {% else %}
    This item has not been claimed yet.
    {% endif %}
    
    {% if proof.metadata.warrantyExpiry %}
    Warranty expires: {{ proof.metadata.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.idstringUnique identifier
    contact.emailstringEmail address
    contact.namestringFull name
    contact.firstNamestringFirst name
    contact.lastNamestringLast name
    contact.phonestringPhone number
    contact.localestringPreferred language/locale (e.g., "en", "de")
    contact.timezonestringPreferred timezone
    contact.avatarUrlstringProfile picture URL
    contact.metadataobjectCustom key-value metadata
    contact.tagsarrayArray of tag strings for segmentation
    contact.createdAtdatetimeWhen the contact was created
    contact.updatedAtdatetimeWhen the contact was last updated
    contact.lastSeenAtdatetimeLast activity timestamp

    Example Usage

    Hi {{ contact.firstName | default: contact.name | 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 %}
    

    User (Account)

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

    FieldTypeDescription
    user.idstringUnique identifier
    user.emailstringEmail address
    user.namestringDisplay name
    user.adminbooleanWhether user has admin privileges
    user.avatarUrlstringProfile picture URL
    user.createdAtdatetimeAccount creation date

    Example Usage

    Logged in as: {{ user.name }} ({{ user.email }})
    
    {% if user.admin %}
    🔐 You have administrator access.
    {% 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.typestringAttestation type (app-defined)
    attestation.dataobjectThe attestation payload (varies by type)
    attestation.createdBystringUser ID who created it
    attestation.createdAtdatetimeWhen the attestation was created
    attestation.updatedAtdatetimeWhen the attestation was last updated

    Example Usage

    {% if attestation.type == "warranty_registration" %}
    Warranty Registration Details:
    - Registered: {{ attestation.createdAt | date: "%B %d, %Y" }}
    - Purchase Date: {{ attestation.data.purchaseDate }}
    - Store: {{ attestation.data.storeName }}
    {% endif %}
    
    {% if attestation.type == "tasting_note" %}
    🍷 Tasting Note by {{ attestation.data.author }}:
    "{{ attestation.data.notes }}"
    Rating: {{ attestation.data.rating }}/5
    {% 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: "there" }},
    
    Great news! Your {{ product.name }} (Serial: {{ proof.serialNumber }}) 
    has been successfully registered to your account.
    
    {% if product.metadata.warrantyYears %}
    Your warranty is valid for {{ product.metadata.warrantyYears }} years 
    from the date of purchase.
    {% endif %}
    
    If you have any questions, please contact {{ collection.name }} support.
    
    Best regards,
    The {{ collection.name }} Team
    

    Notification Messages

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

    Dynamic Content Blocks

    {% if proof.metadata.tier == "gold" %}
      <div class="gold-benefits">
        As a Gold member, you get exclusive access to...
      </div>
    {% elsif proof.metadata.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 metadata or data objects:

    {{ product.metadata.manufacturer }}
    {{ attestation.data.warranty.expiryDate }}
    {{ collection.metadata.social.twitter }}
    

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

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

    Best Practices

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

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

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

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

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

      {{ proof.claimedAt | 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