Dynamic Notification Templating Dynamic Notification Templating

Dynamic Notification Templating

Outline

Dynamic templating

In an effort to provide better customization and more dynamic copy templating, we are now offering new templating tools that will soon be available for notifications and 2-Way SMS subscription management.

Note: Dynamic templating is being rolled out in stages, and is currently only available for 2-Way SMS subscription management copies.

Dynamic templating allows for the creation of highly customizable and dynamic content by using placeholders and expressions within templates. Our new system leverages the Handlebars templating engine, enhanced with additional helpers and features to cater to more specific needs. 

When making copy edits, you may now see a section titled "View subscription context for conditional formatting":

Screenshot 2024-07-24 at 7.14.26 PM.png

This shows you the available variables and objects that you can reference in the copy you are editing. These variables can be one of several types: 

  • String, number, boolean
    • These are primitive types and can be inserted directly into your copy with ease.
  • Date 
    • This will be a date string formatted as ISO 8601. You could insert this directly into your copy, but we recommend formatting the date using one of the building functions.
  • Object 
    • Object variables will contain nested children properties. In the above context, we see one object on the top level, "targetSubscription", which has several properties attached to it. See more about accessing nested values below.
  • Array
    • Arrays are lists of items, and can contain 0 or more entries. In our above context, we have "SubscriptionLines" as an array property nested under the "targetSubscription". While many subscriptions may only have one product, it's possible to have multiple subscription lines. You'll now be able to reference as many subscription lines as you'd like in your copy. See more about accessing array values below.

How do I use it? 

For the below examples, let's assume we have the following context which fits the example schema above: 

{
  "targetSubscription": {
    "status": "active",
    "nextBillingDate": "2024-08-01",
    "totalPrice": "70.00",
    "BillingPolicy": {
      "interval": "monthly",
      "intervalCount": 1
    },
    "SubscriptionLines": [
      {
        "quantity": 2,
        "priceWithoutDiscount": "30.00",
        "ProductVariant": {
          "title": "Orange, 36 pack",
          "sku": "SKIO-ORANGE-36",
          "Product": {
            "title": "Skio Cola"
          }
        }
      },
      {
        "quantity": 1,
        "priceWithoutDiscount": "10.00",
        "ProductVariant": {
          "title": "Original, 12 pack",
          "sku": "SKIO-ORIGINAL-12",
          "Product": {
            "title": "Skio Cola"
          }
        }
      }
    ]
  },
  "supportEmail": "support@example.com",
  "magicLink": "https://example.com/magic-link"
}

Basic variables

Basic variables are the simplest form of placeholders in your templates. They allow you to insert dynamic content from the context directly into your template. In Handlebars, are variables are referenced using double curly braces ("{{" and "}}"). 

  If you have any questions, you can reach us at {{supportEmail}}

  Renders: 
  If you have any questions, you can reach us at support@example.com

For some variables, you will need to escape special characters using triply curly braces ("{{{" and "}}}"). The primary example of this is when rendering variables containing links, for example the "magicLink" property included in the context above. This is due to how Handlebars by default escapes some special characters. You can read more about HTML escaping on the Handlebar guide.

  If you have any questions, you can reach us at {{supportEmail}}, or you can 
  make changes online at {{{magicLink}}}.

  Renders: 
  If you have any questions, you can reach us at help@skio.com, or you can make 
  changes online at https://example.com/magic-link.

Referencing object and array values

Nested references 

Any nested value can be referenced using dot notation between the property names. This is a common syntax across many templating languages, so it may be familiar to you. 

Subscription status: {{targetSubscription.status}} and billing interval: 
{{targetSubscription.BillingPolicy.interval}}

Renders:
Subscription status: active and billing interval: monthly

Subscription status: active and billing interval: monthly

Indexing array values

You can directly access an item in an array using the index (i.e. nth item in the list).

First subscription product: 
{{targetSubscription.SubscriptionLines.0.ProductVariant.Product.title}}

Renders:
First subscription product: Skio Cola

Note: Counting starts at 0 and not 1! 

You can also loop through the list to reference every item. See more below.

Comments

While you may not need to, you can add comments to your templates as you see below. These can be great to explain why some copy or logic blocks were used in case yourself or someone else needs to reference them down the road. 

Here are some subscription details: 
- Subscription status: {{targetSubscription.status}}
{{! This comment will not show up in the output}}
{{!-- This comment may contain mustaches like }} --}}
- Billing interval: {{targetSubscription.BillingPolicy.interval}}

Renders:
Here are some subscription details:
- Subscription status: {{targetSubscription.status}}
- Billing interval: {{targetSubscription.BillingPolicy.interval}}

Built in blocks

Blocks provide a way to include conditional logic and loops within your templates. Handlebars has a set of built-in helpers which cover most use cases you may run into. 

Conditional statements 

Conditional statements allow you to conditionally render some part of your copy based on the evaluation of some statement or variable. In this example, assume we have a boolean (true or false) value in our "targetSubscription" called "isActive". We could include the following in our template: 

{{#if targetSubscription.isActive}}
Your subscription is active!
{{else}}
Your subscription is inactive!
{{/if}}

Handlebars also offers an inverse to "#if", which could be useful if you only wanted to include the "else" portion of the above.

{{#unless targetSubscription.isActive}}
Your subscription is inactive!
{{/if}}

Loops 

Loops enable you to iterate over arrays and display content for each item. When inside the block, you can use "this" to reference the array being iterated over. 

Subscription line info: 

{{#each targetSubscription.SubscriptionLines}}
{{! Here, `this` is referring to `targetSubscription.SubscriptionLines` since
that is the item being iterated over }}
- Product: {{this.ProductVariant.Product.title}} - {{this.ProductVariant.title}} (x {{this.quantity}})

{{/each}}

Renders:
Subscription line info:
- Product: Skio Cola - Orange, 36 pack (x2)
- Product: Skio Cola - Original, 12 pack (x1)

Aliases

The "#with" helper allows you to change the evaluation context when within the provided body. In other words, it can help you to simplify your templates by aliasing the "parent" of the properties you want to render. 

{{! Remember our earlier example of nested references }}
Subscription status: {{targetSubscription.status}} and billing interval:
{{targetSubscription.BillingPolicy.interval}}

{{! Using the #with helper, we can rewrite this as the following }}
{{#with targetSubscription }}
Subscription status: {{status}} and billing interval: {{BillingPolicy.interval}}
{{/with}}

In this first example, the "#with" helper is passed the "targetSubscription" object as a parameter. When this happens, it will treat all expressions within the "#with" block as being children of "targetSubscription", so we can omit it from the contained expressions and make it easier to see what variables the expressions are referencing. 

We can also include aliases in our blocks. This lets us rename a variable for the body of the block, and can be added to "#with" and "#each" blocks.

{{! Recall our loop from before. We can add the following alias to the #each statement: }}
{{#each targetSubscription.SubscriptionLines as | line |}}
{{! Now, we can use both `this` and `line` to reference the current
index of `targetSubscription.SubscriptionLines` }}
- Product: {{line.ProductVariant.Product.title}} - {{line.ProductVariant.title}} (x {{line.quantity}})

{{/each}}

{{! This can be especially useful for clarity in #each blocks, since `this` is not
a very descriptive variable name }}

{{! We can do the same in #with blocks, and rewrite the above #with example with a new
alias for `targetSubscription` }}

{{#with targetSubscription as | sub | }}
Subscription status: {{sub.status}} and billing interval: {{sub.BillingPolicy.interval}}
{{/with}}

Built in functions 

Outside of the built in helpers from Handlebars, we've provided some additional built in functions that can be useful for elevating the personalization in your copy. 

Comparators 

Our "comparison" function allows you to perform a comparison on numbers and strings. It can be called as follows: 

{{(comparison valueA comparator valueB)}} 

Here is an example when used to show a customer how many subscription lines they have. We may want to conditionally pluralize the copy, which can be accomplished as so: 

{{#with targetSubscription.SubscriptionLines as | lines |}}
{{#if (comparison lines.length "gt" 1)}}
You are subscribed to {{lines.length}} items
{{else}}
You are subscribed to 1 item
{{/if}}
{{/with}}

The following comparators are available:

  • "gt": Greater than 
  • "lt": Less than 
  • "eq": Equal to 
  • "gte": Greater than or equal to
  • "lte": Less than or equal to

Integer arithmetic 

The "integerArithmetic" function allows you to perform basic scalar operations on a number variable. Below is an example where we want to show the customer the first product in their subscription, then show them the number of of additional items (or subscription lines) coming up in their next order: 

{{#with targetSubscription.SubscriptionLines as | lines |}}
Your next order contains {{lines.0.Product.title}} (x {{lines.0.quantity}})
{{#if (comparison lines.length "gt" 1)}}
and {{(integerArithmetic lines.length "subtract" 1)}} other(s)
{{/if}}
{{/with}}

Here, we used both the "comparison" and "integerArithmetic" functions to render our copy. We first check to see if there are more than 1 subscription lines, and if so we'll add "and n other(s)". 

Date formatting 

All of our date timestamps will be provided as ISO 8601 strings. You can use the "formatDate" function to format these strings in whatever way suits your needs best. 

{{#with targetSubscription}}
Your next order is coming on: {{(formatDate nextBillingDate "M/DD")}}
{{/with}}

This function takes two parameters. The first is the timestamp, in this case the property "nextBillingDate". The second is the format string, here "M/DD". For help with building your format strings, we use the moment.js library; you can learn more on the official documentation for moment.js format strings.

List item summation

Our last builtin function is "sumList", which allows you to sum all the items of an array given a key to one of the objects in that list. For example, if you wanted to render the total number of items that a customer is subscribed to, you could write the following: 

{{#with targetSubscription}}
You are subscribed to {{(sumListByItem SubscriptionLines "quantity")}} item(s) in total
{{/with}}