{"componentChunkName":"component---src-templates-api-js","path":"/api/template-language/","result":{"data":{"allApiBlueprint":{"edges":[{"node":{"meta":{"title":"A/B Testing API","label":"New"},"fields":{"path":"/api/ab-testing/","file":"ab-testing.apib"}}},{"node":{"meta":{"title":"Data Privacy API","label":"New"},"fields":{"path":"/api/data-privacy/","file":"data-privacy.apib"}}},{"node":{"meta":{"title":"Events API","label":null},"fields":{"path":"/api/events/","file":"events.apib"}}},{"node":{"meta":{"title":"Ingest API","label":"Private Access"},"fields":{"path":"/api/events-ingest/","file":"events-ingest.apib"}}},{"node":{"meta":{"title":"Inbound Domains API","label":null},"fields":{"path":"/api/inbound-domains/","file":"inbound-domains.apib"}}},{"node":{"meta":{"title":"Account API","label":null},"fields":{"path":"/api/account/","file":"account.apib"}}},{"node":{"meta":{"title":"API Overview","label":null},"fields":{"path":"/api/","file":"index.apib"}}},{"node":{"meta":{"title":"Automatic Inline Seeding","label":"Deliverability"},"fields":{"path":"/api/inline-seeds/","file":"inline-seeds.apib"}}},{"node":{"meta":{"title":"Labs APIs","label":null},"fields":{"path":"/api/labs/","file":"labs.apib"}}},{"node":{"meta":{"title":"Message Events API","label":null},"fields":{"path":"/api/message-events/","file":"message-events.apib"}}},{"node":{"meta":{"title":"Recipient Validation API","label":"New"},"fields":{"path":"/api/recipient-validation/","file":"recipient-validation.apib"}}},{"node":{"meta":{"title":"IP Pools API","label":null},"fields":{"path":"/api/ip-pools/","file":"ip-pools.apib"}}},{"node":{"meta":{"title":"Seed List API","label":"Deliverability"},"fields":{"path":"/api/seed-list/","file":"seed-list.apib"}}},{"node":{"meta":{"title":"Bounce Domains API","label":null},"fields":{"path":"/api/bounce-domains/","file":"bounce-domains.apib"}}},{"node":{"meta":{"title":"Recipient Lists API","label":null},"fields":{"path":"/api/recipient-lists/","file":"recipient-lists.apib"}}},{"node":{"meta":{"title":"Sending IPs API","label":null},"fields":{"path":"/api/sending-ips/","file":"sending-ips.apib"}}},{"node":{"meta":{"title":"Relay Webhooks API","label":null},"fields":{"path":"/api/relay-webhooks/","file":"relay-webhooks.apib"}}},{"node":{"meta":{"title":"SMTP API","label":null},"fields":{"path":"/api/smtp/","file":"smtp.apib"}}},{"node":{"meta":{"title":"Snippets API","label":"New"},"fields":{"path":"/api/snippets/","file":"snippets.apib"}}},{"node":{"meta":{"title":"Subaccounts API","label":null},"fields":{"path":"/api/subaccounts/","file":"subaccounts.apib"}}},{"node":{"meta":{"title":"Sending Domains API","label":null},"fields":{"path":"/api/sending-domains/","file":"sending-domains.apib"}}},{"node":{"meta":{"title":"Suppression List API","label":null},"fields":{"path":"/api/suppression-list/","file":"suppression-list.apib"}}},{"node":{"meta":{"title":"Template Language","label":null},"fields":{"path":"/api/template-language/","file":"template-language.apib"}}},{"node":{"meta":{"title":"Tracking Domains API","label":null},"fields":{"path":"/api/tracking-domains/","file":"tracking-domains.apib"}}},{"node":{"meta":{"title":"Templates API","label":null},"fields":{"path":"/api/templates/","file":"templates.apib"}}},{"node":{"meta":{"title":"Transmissions API","label":null},"fields":{"path":"/api/transmissions/","file":"transmissions.apib"}}},{"node":{"meta":{"title":"Event Webhooks API","label":null},"fields":{"path":"/api/webhooks/","file":"webhooks.apib"}}},{"node":{"meta":{"title":"Usage API","label":null},"fields":{"path":"/api/usage/","file":"usage.apib"}}},{"node":{"meta":{"title":"Metrics API","label":null},"fields":{"path":"/api/metrics/","file":"metrics.apib"}}}]},"apiBlueprint":{"ast":{"element":"parseResult","content":[{"element":"category","meta":{"classes":{"element":"array","content":[{"element":"string","content":"api"}]},"title":{"element":"string","content":"Template Language"}},"attributes":{"meta":{"element":"array","content":[{"element":"member","meta":{"classes":{"element":"array","content":[{"element":"string","content":"user"}]}},"content":{"key":{"element":"string","content":"FORMAT"},"value":{"element":"string","content":"1A"}}},{"element":"member","meta":{"classes":{"element":"array","content":[{"element":"string","content":"user"}]}},"content":{"key":{"element":"string","content":"title"},"value":{"element":"string","content":"Template Language"}}},{"element":"member","meta":{"classes":{"element":"array","content":[{"element":"string","content":"user"}]}},"content":{"key":{"element":"string","content":"description"},"value":{"element":"string","content":"Documentation for the template language and substitution capabilities of SparkPost."}}},{"element":"member","meta":{"classes":{"element":"array","content":[{"element":"string","content":"user"}]}},"content":{"key":{"element":"string","content":"full"},"value":{"element":"string","content":"true"}}}]}},"content":[{"element":"copy","content":"The SparkPost API provides a powerful handlebars-style template language that you can use in the email subject, headers, text, HTML, and AMP HTML content.\n\n**Features include:**\n\n-   Sophisticated logic with full [conditionals](#header-if-else-statement) and [logical operators](#header-relational-and-logical-operators).\n\n-   [Looping](#header-each-statement) over arrays.\n\n-   Reference nested [object paths](#header-nested-object-paths).\n\n-   [Default values](#header-default-values) if substitution data is absent.\n\n-   Completely [dynamic content](#header-dynamic-content).\n\n-   Execution of built-in [macros](#header-macros).\n\n-   Automatic [HTML escaping](#header-html-escaping).\n\n-   Automatic encoding of UTF-8 variables in email headers.\n\n<Banner status=\"warning\">The template language can only be used through the API, not via SMTP</Banner>\n\n## Template Variables\n\nYou can pass in JSON data to a template through both the transmission and individual recipient. The data can be anything from a simple string or deeply nested arrays and objects. That data can then be used in [expressions](#header-expressions) or [statements](#header-statements) to generate the email.\n\n**Keys**<br />\nThe substitution keys can be contain any US-ASCII letters, digits, and underscores, not beginning with a digit, with the exception of the following keywords:\n\n|                                                                                                                                                                                                                             |\n| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `address`, `email`, `email_id`, `env_from`, `return_path`, `and`, `break`, `do`, `else`, `elseif`, `end`, `false`, `for`, `function`, `if`, `local`, `nil` ,`not`, `or`, `each`, `return`, `then`, `true`, `until`, `while` |\n\n**Values**<br />\nThe substitution values can be any valid UTF-8 string or JSON value.\n\n### Sources\n\nData can be passed in as `substitution_data` or `metadata`. The difference is that the `metadata` is sent through [webhook](/api/webhooks/) payloads and [message events](/api/message-events/), while `substitution_data` is only used for generating the template. If there is a conflict between `substitution_data` and `metadata`, the `substitution_data` value takes precedence.\n\nBoth `metadata` and `substitution_data` can be set inside the top-level transmission object **and** inside each recipient. The recipient-level data takes precedence over the transmission-level data.\n\nIn the following transmissions example, the hierarchy for the value of `city` is as follows: `San Francisco`, `Seattle`, `Baltimore`, and  `New York`.\n\nThe email would therefore render as: `Hello, New York`.\n\n```json\n{\n  \"metadata\": {\n    \"city\": \"San Francisco\"\n  },\n  \"substitution_data\": {\n    \"city\": \"Seattle\"\n  },\n  \"recipients\": [\n    {\n      \"address\": \"wilma@flintstone.com\",\n      \"metadata\": {\n        \"city\": \"Baltimore\"\n      },\n      \"substitution_data\": {\n        \"city\": \"New York\"\n      }\n    }\n  ],\n  \"content\": {\n    \"from\": \"fred@flintstone.com\",\n    \"subject\": \"Hello\",\n    \"html\": \"Hello, {{city}}!\"\n  }\n}\n```\n\n### Reserved Variables\n\nAs a convenience, SparkPost also adds additional variables based on the transmission details. They are as follows:\n\n-   `address.name` – Name from the recipient details.\n\n-   `email`, `email_id`, and `address.email` – Email address from recipient details.\n\n-   `env_from` – Return-Path address, sometimes called a \"bounce address\" or \"envelope sender address\"\n\n-   `return_path` - <span class=\"label label-info\">Enterprise</span> The `return_path` substitution variable is available to SparkPost Enterprise users.\n\n## Expressions\n\nExpressions allow you to insert variables into your content.\n\nAn expression is a `{{`, some content, followed by a `}}`. Any whitespace inside the double curly braces is ignored. All of the following are equivalent:\n\n<REPL>\n\n```html\n{{ value }}\n{{value}}\n{{  value   }}\n```\n\n```json\n{\n    \"value\": \"Hello 👋\"\n}\n```\n\n```results\nHello 👋\nHello 👋\nHello 👋\n```\n\n</REPL>\n\n### Missing Variables\n\nAn empty string is substituted for variables that do not exist or have a value of `null`.\n\n<REPL>\n\n```html\n* {{name}}\n* {{age}}\n* {{job}}\n* {{location}}\n```\n\n```json\n{\n    \"name\": \"Jane\",\n    \"age\": null,\n    \"job\": \"Software Engineer\" \n}\n```\n\n```results\n* Jane\n* \n* Software Engineer\n* \n```\n\n</REPL>\n\n### Default Values\n\nTo avoid rendering an empty string, you can set default values using the `or` operator. In the following example, if `name` does not exist, then the expression\nwill evaluate as `Customer`.\n\n<REPL>\n\n```html\nHello {{ name or 'Customer' }}\n```\n\n```json\n{\n    \"name\": null\n}\n```\n\n```results\nHello Customer\n```\n\n</REPL>\n\n### Nested Object Paths\n\nYou can reference variables that are nested inside arrays or objects using dot notation and square brackets. You can even use variables as part of the path like `part` is used in the example below.\n\n<REPL>\n\n```html\nStreet: {{address.street}}\nCity: {{address['city']}}\nDynamic: {{address[part]}}\n```\n\n```json\n{\n    \"address\": {\n        \"street\": \"Howard Street\",\n        \"city\": \"San Francisco\"\n    },\n    \"part\": \"street\"\n}\n```\n\n```results\nStreet: Howard Street\nCity: San Francisco\nDynamic: Howard Street\n```\n\n</REPL>\n\n### HTML Escaping\n\nBy default, variables in the transmissions `html` and `amp_html` content **are** HTML escaped. However, in the `text` content, variables are **not** HTML escaped. To render a value unescaped in the `html` and `amp_html` content, wrap it in triple curly braces, `{{{ value }}}`.\n\n<REPL>\n\n```html\nEscaped: {{custom_html}}\nUnescaped: {{{custom_html}}}\n```\n\n```json\n{\n    \"custom_html\": \"<b>Hello, World</b>\"\n}\n```\n\n```results\nEscaped: &lt;b&gt;Hello, World&lt;&#x2F;b&gt;\nUnescaped: <b>Hello, World</b>\n```\n\n</REPL>\n\n<Banner status=\"danger\">If your messages contain user-generated content, disabling HTML escaping (without handling escaping in your application) may expose recipients of your messages to various types of attacks such as <a href=\"https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)\">CSRF</a> or <a href=\"https://www.owasp.org/index.php/Cross-site_Scripting_(XSS)\">XSS</a>.</Banner>\n\n## Statements\n\nStatements allow you to implement logic into your templates. You can conditionally render content and loop through the data passed to the template. They have the same syntax as expressions but start with specific keywords such as `if`.\n\nUnlike expressions, statements don't return a value. Therefore, if they are on their own line, they will **not** produce a blank line in the resulting output. In addition, any whitespace after the statement will also not rendered.\n\nIn the following example, the template renders without blank lines:\n\n<REPL>\n\n```html\nStart of template\n{{ if state == \"MD\" }}\nMaryland\n{{ end }}\nEnd of template\n```\n\n```json\n{\n    \"state\": \"MD\"\n}\n```\n\n```results\nStart of template\nMaryland\nEnd of template\n```\n\n</REPL>\n\nAny content written on the same line as `end` will not break the line; however, content after that will:\n\n<REPL>\n\n```html\n{{ if city == \"Baltimore\" }}\nBaltimore\n{{ end }}, Maryland\n```\n\n```json\n{\n    \"city\": \"Baltimore\"\n}\n```\n\n```results\nBaltimore, Maryland\n```\n\n</REPL>\n\n<REPL>\n\n```html\n{{ if state == \"Baltimore\" }}\nBaltimore\n{{ end }}\nMaryland\n```\n\n```json\n{\n    \"state\": \"Baltimore\"\n}\n```\n\n```results\nBaltimore\nMaryland\n```\n\n</REPL>\n\n### `if/else` Statement\n\nThe `if/else` statement allows you to conditionally render some content if a specified condition is `true` or is not `null`. If the condition is `false` or `null`, another block of content may be rendered.\n\n```\n{{if signed_up}}\nWelcome\n{{end}}\n```\n\nYou may optionally include `then` at the end of an `if` statement.\n\n```\n{{if signed_up then}}\nWelcome\n{{end}}\n```\n\nYou can combine multiple `if/else` statements using `elseif` statements.\n\n```\n{{if signed_up}}\nWelcome\n{{elseif rejected_sign_up}}\nWe won't bug you\n{{else}}\nPlease sign up\n{{end}}\n```\n\n#### Relational and Logical Operators\n\nYou can use relational and logical operators for granular control of what content renders.\n\n```\n{{if not signed_up}}\nDon't forget to sign up!\n{{end}}\n\n{{if age > 30}}\ndo something\n{{else}}\ndo something else\n{{end}}\n\n{{if address.state == \"MD\"}}\ndo something\n{{end}}\n\n-- multi part conditionals\n{{if age > 30 and address.state == \"MD\"}}\ndo something\n{{end}}\n```\n\nThe relational and logical operators are as follows:\n\n**Relational Operators**\n\n| Expression | Description                     |\n| ---------- | ------------------------------- |\n| `x == y`   | x is equal to y                 |\n| `x != y`   | x is not equal to y             |\n| `x < y`    | x is less than y                |\n| `x > y`    | x is greater than y             |\n| `x <= y`   | x is less than or equal to y    |\n| `x >= y`   | x is greater than or equal to y |\n\n**Logical Operators**\n\n| Expression |\n| ---------- |\n| and        |\n| or         |\n| not        |\n\n**The Length Operator**\n\nThe length operator `#` gives the length of an array.\n\n<REPL>\n\n```html\nNumber of states: {{#states}}\n```\n\n```json\n{\n    \"states\": [\"MD\", \"CA\"]\n}\n```\n\n```results\nNumber of states: 2\n```\n\n</REPL>\n\n#### Arithmetic Operators\n\nRecognized arithmetic operators are as follows:\n\n| Expression |\n| ---------- |\n| `+`        |\n| `-`        |\n| `*`        |\n| `/`        |\n\n<REPL>\n\n```html\nYour discounted price is ${{price - 5}}.\n```\n\n```json\n{\n    \"price\": 15\n}\n```\n\n```results\nYour discounted price is $10.\n```\n\n</REPL>\n\n### `each` Statement\n\nYou can use an `each` statement to iterate over an array. If the array is empty or the value is `null`, nothing is rendered.\n\nInside the loop, you can access the current value via the `loop_var` variable. The current index can be accessed through the `loop_index` variable.\n\nThe following example iterates over array of strings and print out the value of each string:\n\n<REPL>\n\n```html\n{{ each children }}\nYou have a child named {{loop_var}}\n{{ end }}\n```\n\n```json\n{\n    \"children\": [\n        \"Rusty\",\n        \"Audrey\"\n    ]\n}\n```\n\n```results\nYou have a child named Rusty\nYou have a child named Audrey\n```\n\n</REPL>\n\nTo iterate over an array of objects, the syntax is the same, but access to the nested fields of the object is done using dot notation:\n\n<REPL>\n\n```html\nYour shopping cart has items in it:\n{{each shopping_cart}}\nItem: {{loop_var.name}}, Price: {{loop_var.price}}\n{{end}}\n```\n\n```json\n{   \n    \"shopping_cart\": [\n        {\n            \"name\": \"Jacket\",\n            \"price\": 39.99\n        },\n        {\n            \"name\": \"Gloves\",\n            \"price\": 5.00\n        }\n    ]\n}\n```\n\n```results\nYour shopping cart has items in it:\nItem: Jacket, Price: 39.99\nItem: Gloves, Price: 5\n```\n\n</REPL>\n\n#### Nested Loops\n\nWhen using nested loops, because you have multiple loop variables, you should access the values using `loop_vars.<name of the array>` (notice `loop_vars` is plural).\nThe following example uses `shopping_cart` and `a_nested_array`:\n\n<REPL>\n\n```html\n---\n{{each shopping_cart}}\nItem: {{loop_vars.shopping_cart.name}}\nPrice: {{loop_vars.shopping_cart.price}}\nThis item has the following nested values:\n{{each loop_vars.shopping_cart.a_nested_array}}\n  Nested value: {{loop_vars.a_nested_array.key}}\n{{end}}\n---\n{{end}}\n```\n\n```json\n{\n    \"shopping_cart\": [\n        {\n            \"name\": \"Jacket\",\n            \"price\": 39.99,\n            \"a_nested_array\": [\n                {\n                    \"key\": \"v2\"\n                },\n                {\n                    \"key\": \"v1\"\n                }\n            ]\n        },\n        {\n            \"name\": \"Gloves\",\n            \"price\": 5.00\n        }\n    ]\n}\n```\n\n```results\n---\nItem: Jacket\nPrice: 39.99\nThis item has the following nested values:\n  Nested value: v2\n  Nested value: v1\n---\nItem: Gloves\nPrice: 5\nThis item has the following nested values:\n---\n```\n\n</REPL>\n\n#### Array Indexing\n\n<Banner status=\"info\">Array indexes start at <code>1</code>. i.e. The first value in an array named <code>items</code> is <code>items[1]</code>.</Banner>\n\nIt is possible to access specific items within an array:\n\n```\nYou have children named {{ children[1] }} and {{ children[2] }}.\n```\n\nArray indexing and dot notation may also be used together:\n\n```\nThe first item in your shopping cart is {{ shopping_cart[1].name }}.\n```\n\n## Links\n\nLinks are handled slightly differently than regular content. All of the following applies to both links in the `html`, `amp_html` and `text` content.\n\n### Links in Variables\n\nThe template language identifies links by the fact that they start with a protocol – `https://` or `http://`. If the protocol is stored inside the variable, the link won't get recognized correctly. The means that it won't get tracked and expressions that are used in the link won't be escaped properly.\n\nTo use links that are entirely stored in variables processed correctly, render them as [dynamic content](#header-dynamic-content).\n\n### Links in Loops and Conditionals\n\nLinks are extracted from the template based on the fact they start with a protocol as explained in [Links in Variables](#header-links-in-variables).  When links are templated inside of loops or conditionals, there should be clear whitespace delimiters such as new lines or space between the link and any templating keywords.\n\n```no-highlight\n{{if host}}\nhttps://{{{host}}}/\n{{else}}\nhttps://www.sparkpost.com/\n{{end}}\n```\n\n### Personalized Links\n\nYou can use expressions inside of links to customize them. Instead of HTML escaping the value, expressions will URL encode the value.\n\n<Banner status=\"warning\">No spaces are allowed when an expression is used inside a query string.</Banner>\n\n<REPL>\n\n```html\nPersonalized link:\n<a href=\"https://company.com/dailydeals?user={{user}}&offercode={{offercode}}\">Go!</a>\n```\n\n```json\n{\n    \"user\": \"john\",\n    \"offercode\": \"Daily Deal!\"\n}\n```\n\n```results\nPersonalized link:\n<a href=\"https://company.com/dailydeals?user=john&offercode=Daily%20Deal%21\">Go!</a>\n```\n\n</REPL>\n\n### URL Encoding\n\nJust as with [HTML escaping](#header-html-escaping), you can disable URL encoding by using triple curly braces. This is useful when you have multiple pieces of the URL in one variable.\n\n<Banner status=\"warning\">Disabling URL encoding of variables can lead to broken links – be careful with this!</Banner>\n\n<REPL>\n\n```json\n{\n    \"link\" : \"www.company.com/groups\",\n    \"the_entire_suffix\" : \"groups/join?user=clark\"\n}\n```\n\n```html\n<a href=\"https://{{{link}}}\">click me</a>\n<a href=\"http://www.company.com/{{{the_entire_suffix}}}\">Go</a>\n```\n\n```results\n<a href=\"https://www.company.com/groups\">click me</a>\n<a href=\"http://www.company.com/groups/join?user=clark\">Go</a>\n```\n\n</REPL>\n\n<Banner status=\"danger\">If your messages contain user-generated content, disabling URL encoding (without handling encoding in your application) may expose recipients of your messages to various types of attacks such as <a href=\"https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)\">CSRF</a> or <a href=\"https://www.owasp.org/index.php/Cross-site_Scripting_(XSS)\">XSS</a>.</Banner>\n\n### Custom Link Attributes\n\nThe template language provides a few custom HTML attributes for attaching extra functionality to your links.\n\nWith the exception of `data-msys-linkname`, these custom attributes can also be used for links in the `text` part of a message using a double-square-bracket notation.\n\n```no-highlight\nhttp://www.example.com[[data-msys-clicktrack=\"0\"]]\n```\n\n#### Link Names\n\nLink names are used to identify links in click events in both webhook payloads and message events. You can name links through the `data-msys-linkname` custom attribute. If this attribute is not specified the link name will be reported as `Raw URL`. For example:\n\n```html\n<a href=\"http://www.example.com\" data-msys-linkname=\"banner\">Example</a>\n```\n\nLink names are limited to 63 bytes and will be automatically truncated if they exceed this limit. Note that some characters can count as multiple bytes when encoded, so links containing these characters may be truncated at fewer than 63 visible characters.\n\n#### Unsubscribe Links\n\nYou can configure a link in your content to generate a `link_unsubscribe` event  when clicked using the `data-msys-unsubscribe` custom attribute.  Click tracking is required for the generation of a `link_unsubscribe` event. For example:\n\n```html\n<a href=\"http://www.example.com/unsub_handler?id=1234\" data-msys-unsubscribe=\"1\">Unsubscribe</a>\n```\n\nMore information can be found [here](https://support.sparkpost.com/docs/user-guide/setting-up-unsubscribe-links/).\n\n#### Per-link Disabling of Click Tracking\n\nWhen click-tracking is enabled for a transmission, individual links can be skipped using the `data-msys-clicktrack` custom attribute. For example:\n\n```html\n<a href=\"http://www.example.com/\" data-msys-clicktrack=\"0\">Click</a>\n```\n\n#### Custom Link Sub-Paths\n\nIt is possible to add a custom sub-path to a tracked URL using the `data-msys-sublink` custom attribute. For example:\n\n```html\n<a href=\"http://www.example.com/\" data-msys-sublink=\"custom_path\">Click</a>\n```\n\nThe tracked link generated will look like this:\n\n```no-highlight\nhttp://<hostname>/f/custom_path/<encoded target url>\n```\n\n<Banner status=\"info\"><strong><a href=\"https://www.sparkpost.com/enterprise-email/\">SparkPost Enterprise only</a>:</strong> An example of how to use <strong>data-msys-sublink</strong> to support iOS Universal Links can be found <a href=\"https://support.sparkpostelite.com/customer/en/portal/articles/2231112-ios9-universal-links-support?b_id=8730#Creating%20Universal%20Links%20in%20Templates%20&%20Sub-Pathing\">here</a>.</Banner>\n\n## Macros\n\nMacros are built-in functions. Each macro consists of a name followed by parentheses, which may enclose any arguments that the macro accepts.\n\n### `render_dynamic_content()`\n\nThis macro allows you to render substitution variables, stored inside the `dynamic_html`, `dynamic_amp_html` and `dynamic_plain`. It will execute all expressions and will track all links.\n\nLearn more about [dynamic content](#header-dynamic-content).\n\n### `empty()`\n\nGiven an array as an argument, the `empty` macro returns true if the array is empty or false if the array is not empty. This is useful for determining whether to include a header in a dynamically generated HTML table and blocking iteration of the table if it is empty.\n\n<REPL>\n\n```html\n{{ if not empty(shopping_cart) }}\n<table>\n  <tr>\n    <th>Name</th>\n    <th>Price</th>\n  </tr>\n{{ each shopping_cart }}\n  <tr>\n    <td>{{loop_var.name}}</td>\n    <td>${{loop_var.price}}</td>\n  </tr>\n{{ end }}\n</table>\n{{ else }}\n<b>Buy something!</b>\n{{ end }}\n```\n\n```json\n{   \n    \"shopping_cart\": [\n        {\n            \"name\": \"Jacket\",\n            \"price\": 39.99\n        },\n        {\n            \"name\": \"Gloves\",\n            \"price\": 5.00\n        }\n    ]\n}\n```\n\n```results\n<table>\n  <tr>\n    <th>Name</th>\n    <th>Price</th>\n  </tr>\n  <tr>\n    <td>Jacket</td>\n    <td>$39.99</td>\n  </tr>\n  <tr>\n    <td>Gloves</td>\n    <td>$5</td>\n  </tr>\n</table>\n```\n\n</REPL>\n\n### `render_snippet()`\n\nSee [this section](#header-snippets).\n\n### Braces Macros\n\nIf you want opening or closing braces to appear in the content, you must escape them. The six macros for outputting braces are as follows:\n\n| Macro                    | Output |\n| ------------------------ | ------ |\n| `opening_single_curly()` | `{`    |\n| `closing_single_curly()` | `}`    |\n| `opening_double_curly()` | `{{`   |\n| `closing_double_curly()` | `}}`   |\n| `opening_triple_curly()` | `{{{`  |\n| `closing_triple_curly()` | `}}}`  |\n\n<REPL>\n\n```html\nHere is a curly: {{ opening_double_curly() }}\n```\n\n```results\nHere is a curly: {{\n```\n\n</REPL>\n\n### Escaping curly braces in amp-mustache templates\n\nAMP HTML email content may contain \"amp-mustache\" templates.  Most double and triple curly brace expressions\nwithin amp-mustache templates are automatically escaped by SparkPost such that the amp-mustache expressions\nare preserved upon delivery to email clients.\n\nThere are certain expressions that SparkPost will _not_ escape.  These include:\n\n-   Expressions that contain the keyword \"sparkpost\"\n\n-   Expressions that are already a call to one of the curly escape macros: opening_single_curly(), closing_single_curly(), opening_double_curly(), closing_double_curly(), opening_triple_curly(), and closing_triple_curly()\n\n-   Expressions that contain use of \"loop_var\"\n\n-   The \"else\" and \"end\" control statements\n\nIn the following example, {{name}}, {{price}}, {{#cart_items}}, {{/cart_items}}, and {{^cart_items}} will be automatically\nescaped such that they are preserved as amp-mustache template expressions upon delivery of the email.\nOn the other hand, {{sparkpost_name}} will be treated as a sparkpost templating expression since it contains the \"sparkpost\" keyword.\n\n```\n<amp-list src=\"https://ampbyexample.com/json/cart.json\" layout=\"fixed-height\" height=\"80\">\n    <template type=\"amp-mustache\">\n      <div id=\"cart\">\n        Cart items for {{sparkpost_name}}:<br>\n        {{#cart_items}}\n        <div class=\"cart-item\">\n            <span>{{name}}</span>\n            <span>{{price}}</span>\n          </div>\n        {{/cart_items}}\n        {{^cart_items}}\n          There are no featured products available. Please check back again later.\n        {{/cart_items}}\n      </div>\n    </template>\n  </amp-list>\n```\n\n## Dynamic Content\n\nDynamic content is content stored in a variable that contains expressions, statements, and links. Expressions inside of variables are **not** executed by default. Likewise, links are not tracked. To correctly render dynamic content, you must do the following:\n\n1.  Add the dynamic content inside the `dynamic_html` object at the _transmission-level_ substitution data.\n\n2.  Use the `render_dynamic_content()` macro to render the `dynamic_html` variables.\n\nHere is an example of dynamic content used correctly. Notice that the `{{username}}` is replaced with `foo` in the output.\n\n<REPL>\n\n```html\n<body>\n<p>Insert a chunk of html:</p>\n{{ render_dynamic_content(dynamic_html.my_html_chunk) }}\n</body>\n```\n\n```json\n{\n    \"dynamic_html\": {\n        \"my_html_chunk\" : \"<p><a href=\\\"http://www.example.com?q={{username}}\\\">Click here</a></p>\"\n    },\n    \"username\" : \"foo\"\n}\n```\n\n```results\n<body>\n<p>Insert a chunk of html:</p>\n<p><a href=\"http://www.example.com?q=foo\">Click here</a></p>\n</body>\n```\n\n</REPL>\n\nThe dynamic content will be correctly rendered _without_ HTML escaping,\nregardless of whether double or triple curly braces are used.\n\n**Plain text**\n\nTo insert dynamic content into the `text/plain` part of a message, place the dynamic content into the transmission-level substitution variable `dynamic_plain`.\n\nAs with `dynamic_html`, `dynamic_plain` variables must be wrapped in the `render_dynamic_content()` macro when used in the template.\n\n**AMP HTML**\n\nYou can also insert dynamic content into the `text/x-amp-html` part of a message using the transmission level `dynamic_amp_html`\nvariable:\n\n```json\n{\n  \"substitution_data\" : {\n    \"dynamic_amp_html\" : {\n      \"my_amp_html_chunk\": \"<amp-img width=\\\"30\\\" height=\\\"30\\\" src=\\\"https://www.example.com?u={{username}}\\\">\"\n    }\n  }\n}\n```\n\nAs with `dynamic_html` and `dynamic_plain`, you must use the `render_dynamic_content()` macro to include your dynamic AMPHTML in the template:\n\n```\nAttempting to insert a chunk of amp html:\n{{ render_dynamic_content(dynamic_amp_html.my_amp_html_chunk) }}\n```\n\n**Complex Example**\n\nFinally, as a more complex example, `render_dynamic_content()` can also be used inside an `each` loop. This example only renders the offers available as listed in the `offers` array.\n\n<REPL>\n\n```html\n<h3>Today's special offers</h3>\n<ul>\n{{each offers}}\n    <li>{{render_dynamic_content(dynamic_html[loop_var])}}</li>\n{{end}}\n</ul>\n```\n\n```json\n{\n    \"name\": \"John\",\n    \"offers\": [ \"offer1\", \"offer3\" ],\n    \"dynamic_html\": {\n        \"offer1\": \"<a href=\\\"http://t.com/offer/1?name={{name}}\\\">Premium-brand wirecutters</a>\",\n        \"offer2\": \"<a href=\\\"http://t.com/offer/2?name={{name}}\\\">Corks</a>\",\n        \"offer3\": \"<a href=\\\"http://t.com/offer/3?name={{name}}\\\">Super-effective bug spray</a>\"\n    }\n}\n```\n\n```results\n<h3>Today's special offers</h3>\n<ul>\n    <li><a href=\"http://t.com/offer/1?name=John\">Premium-brand wirecutters</a></li>\n    <li><a href=\"http://t.com/offer/3?name=John\">Super-effective bug spray</a></li>\n</ul>\n```\n\n</REPL>\n\nFor AMP HTML, the same example could be laid out using the `amp_html` part\n\n```json\n{\n    \"name\": \"John\",\n    \"offers\": [ \"offer1\", \"offer3\" ],\n    \"dynamic_html\": {\n        \"offer1\": \"<a href=\\\"http://t.com/offer/1?name={{name}}\\\">Premium-brand wirecutters</a>\",\n        \"offer2\": \"<a href=\\\"http://t.com/offer/2?name={{name}}\\\">Corks</a>\",\n        \"offer3\": \"<a href=\\\"http://t.com/offer/3?name={{name}}\\\">Super-effective bug spray</a>\"\n    },\n    \"dynamic_amp_html\": {\n      \"offer1\": \"<a href=\\\"http://t.com/offer/1?name={{name}}\\\">Premium-brand wirecutters</a>\",\n      \"offer2\": \"<a href=\\\"http://t.com/offer/2?name={{name}}\\\">Corks</a>\",\n      \"offer3\": \"<a href=\\\"http://t.com/offer/3?name={{name}}\\\">Super-effective bug spray</a>\"\n    },\n     \"content\": {\n        \"html\": \"<p>Today's special offers</p><ul>\\n{{each offers}}\\n<li>{{render_dynamic_content(dynamic_html[loop_var])}}</li>\\n{{end}}\\n</ul>\",\n        \"amp_html\": \"<!doctype html><html amp4email><head><meta charset=\\\"utf-8\\\"><script async src=\\\"https://cdn.ampproject.org/v0.js\\\"></script><style amp4email-boilerplate>body{visibility:hidden}</style></head><body><p>Today's special offers</p><ul>\\n{{each offers}}\\n<li>{{render_dynamic_content(dynamic_amp_html[loop_var])}}</li>\\n{{end}}\\n</ul></body></html>\",\n        \"from\": \"test@example.com\",\n        \"subject\": \"offers\"\n    }\n  }\n}\n```\n\n## Snippets\n\nSnippets are pieces of reusable content that are managed using the [Snippets endpoint](/api/snippets).\nOnce a snippet is created, it can be imported into any html, amp_html, or text email content using the `render_snippet` macro call.\n\nSnippets are similar to the `dynamic_html`, `dynamic_amp_html`, and `dynamic_plain` described in the previous section,\nwith the key difference being snippets are created ahead of time rather than\ndefined inline within a transmissions request.\n\nFor example, the /api/labs/snippets endpoint can be used to create a snippet with the ID `ourfooter`:\n\n```\n{\n  \"id\" : \"ourfooter\",\n  \"content\" : {\n    \"html\" : \"<footer><p>Our standard html footer content</p></footer>\",\n    \"text\" : \"Our standard plain text footer content\",\n    \"amp_html\" : \"<footer><p>Our standard amp html footer content</p></footer>\"\n  }\n}\n```\n\nThe snippet can then be imported into either plain text, html, or amp_html email content\nusing the `render_snippet` macro call. The macro call will automatically use the appropriate\ncontent.html, content.amp_html, or content.text snippet value based on the type of content that the snippet\nis being inserted into.\nFor example, if a transmission was injected with content.html of the form:\n\n```\n<html>\n<p>Our body content</p>\n{{ render_snippet( \"ourfooter\" ) }}\n</html>\n```\n\nThe resulting rendered HTML email content would look like:\n\n```\n<html>\n<p>Our body content</p>\n<footer><p>Our standard html footer content</p></footer>\n</html>\n```\n\nThe `render_snippet` macro takes a snippet ID as its only argument, which may come from `substitution_data`.\nThe following example illustrates how one of two plain text snippets can be utilized, based on the recipient's `substitution_data`.\n\nTwo snippets are created:\n\n```\n{\n  \"id\" : \"banner_snippet_A\",\n  \"content\" : {\n    \"text\" : \"Banner A\"\n  }\n}\n```\n\n```\n{\n  \"id\" : \"banner_snippet_B\",\n  \"content\" : {\n    \"text\" : \"Banner B\"\n  }\n}\n```\n\nTransmission with content.text:\n\n```\nThe following banner depends on the \"banner_id\" substitution_data value\n{{ render_snippet( banner_id ) }}\n```\n\nWhere Recipient 1 has substitution_data:\n\n```\n{\n  \"banner_id\" : \"banner_snippet_A\"\n}\n```\n\nAnd Recipient 2 has substitution_data:\n\n```\n{\n  \"banner_id\" : \"banner_snippet_B\"\n}\n```\n\nOther notes on snippet usage:\n\n-   Snippets themselves may contain substitution syntax. Though some restrictions apply: snippets cannot reference other snippets (`render_snippet`), nor can they utilize `render_dynamic_content`.\n\n-   Snippets may contain links (which will be click tracked if `click_tracking` is enabled).\n\n-   If a `render_snippet` call references a snippet which does not exist, the transmission will be discarded with a generation failure event.\n\n-   A template may utilize `render_snippet` at most 5 times. If this limit is exceeded, the transmission will be discarded with a generation failure event.\n\n-   Snippet UI can take up to 2 minutes to reflect the changes made.\n\n## Advanced\n\n### Substitutions in email_rfc822 Headers\n\nWhen it is desirable to have substitutions in RFC2047 encoded headers which are folded, be sure that each line of the header is separately RFC2047 encoded. Otherwise, the server will not be able to decode the header to look for the template language syntax.\n\n**Correct:**\n\n```\nSubject: =?gb2312?B?ztLE3M3Mz8Kyo8Gntviyu8nLye3M5c7SxNzNzM/CsqPBp7b4srvJy8ntzOU=?=\n   =?gb2312?B?ztLE3M3Mz8Kyo8Gntvg=?= \n```\n\n**Incorrect:**\n\n```\nSubject: =?gb2312?B?ztLE3M3Mz8Kyo8Gntviyu8nLye3M5c7SxNzNzM/CsqPBp7b4srvJy8ntzOU=\n   ztLE3M3Mz8Kyo8Gntvg=?=\n```\n\n### Encoding Rules\n\n-   If after substitution, a text/plain, text/html, or text/x-amp-html part contains 8-bit data, then that part will be quoted-printable encoded before being placed back into the MIME structure. The Content-Type will be updated appropriately.\n\n-   If after substitution, a header value contains 8-bit data, then the header\n    value will be RFC2047 base64 encoded before being written back to the headers structure."}]}]},"TableOfContents":[{"anchor":"#header-template-variables","title":"Template Variables","children":[{"anchor":"#header-sources","title":"Sources"},{"anchor":"#header-reserved-variables","title":"Reserved Variables"}]},{"anchor":"#header-expressions","title":"Expressions","children":[{"anchor":"#header-missing-variables","title":"Missing Variables"},{"anchor":"#header-default-values","title":"Default Values"},{"anchor":"#header-nested-object-paths","title":"Nested Object Paths"},{"anchor":"#header-html-escaping","title":"HTML Escaping"}]},{"anchor":"#header-statements","title":"Statements","children":[{"anchor":"#header-if-else-statement","title":"if/else Statement","children":[{"anchor":"#header-relational-and-logical-operators","title":"Relational and Logical Operators"},{"anchor":"#header-arithmetic-operators","title":"Arithmetic Operators"}]},{"anchor":"#header-each-statement","title":"each Statement","children":[{"anchor":"#header-nested-loops","title":"Nested Loops"},{"anchor":"#header-array-indexing","title":"Array Indexing"}]}]},{"anchor":"#header-links","title":"Links","children":[{"anchor":"#header-links-in-variables","title":"Links in Variables"},{"anchor":"#header-links-in-loops-and-conditionals","title":"Links in Loops and Conditionals"},{"anchor":"#header-personalized-links","title":"Personalized Links"},{"anchor":"#header-url-encoding","title":"URL Encoding"},{"anchor":"#header-custom-link-attributes","title":"Custom Link Attributes","children":[{"anchor":"#header-link-names","title":"Link Names"},{"anchor":"#header-unsubscribe-links","title":"Unsubscribe Links"},{"anchor":"#header-per-link-disabling-of-click-tracking","title":"Per-link Disabling of Click Tracking"},{"anchor":"#header-custom-link-sub-paths","title":"Custom Link Sub-Paths"}]}]},{"anchor":"#header-macros","title":"Macros","children":[{"anchor":"#header-render_dynamic_content()","title":"render_dynamic_content()"},{"anchor":"#header-empty()","title":"empty()"},{"anchor":"#header-render_snippet()","title":"render_snippet()"},{"anchor":"#header-braces-macros","title":"Braces Macros"},{"anchor":"#header-escaping-curly-braces-in-amp-mustache-templates","title":"Escaping curly braces in amp-mustache templates"}]},{"anchor":"#header-dynamic-content","title":"Dynamic Content"},{"anchor":"#header-snippets","title":"Snippets"},{"anchor":"#header-advanced","title":"Advanced","children":[{"anchor":"#header-substitutions-in-email_rfc822-headers","title":"Substitutions in email_rfc822 Headers"},{"anchor":"#header-encoding-rules","title":"Encoding Rules"}]}],"meta":{"title":"Template Language","description":"Documentation for the template language and substitution capabilities of SparkPost.","full":true},"fields":{"path":"/api/template-language/"}}},"pageContext":{"file":"template-language.apib"}},"staticQueryHashes":["1319884646","1428769721","3859448388"]}