Advanced Email Templates

Bird

25 Mar 2016

Email

1 min read

Advanced Email Templates

Key Takeaways

    • SparkPost templates support simple and advanced email composition, including HTML, text, conditional logic, loops, and per-recipient dynamic data through substitution variables.

    • Arrays can be passed into substitution data, allowing you to loop through items (e.g., lists of files, recent activity, custom table rows) to generate personalized sections for each recipient.

    • Triple-curly braces {{{ }}} allow you to insert raw, unescaped HTML, which is essential when your substitution data already contains markup.

    • Attachments can be sent easily when using the transmission endpoint (content.attachments), which abstracts MIME boundaries and encoding for you.

    • Inline images work similarly to attachments but render in the body using cid: references.

    • Templates themselves don’t natively support attachments, but you can still include them by crafting a full RFC 5322 email with MIME parts and storing that as an email_rfc822 template.

    • When building raw RFC 5322 templates, substitutions still work — but only in the headers, and the first HTML and text MIME parts.

    • Using your programming language’s mail client (e.g., JavaMail) offers another path: generate full MIME emails programmatically and send via the SparkPost transmissions API.

    • For maintainability, keep markup and data separate whenever possible—especially when building templates that include loops or multiple MIME sections.

    • Advanced template features (conditionals, expressions, array iteration) allow developers to build sophisticated, personalized emails without rewriting entire templates per use case.

Q&A Highlights

  • Can SparkPost templates handle loops and arrays?

    Yes. Templates can iterate over arrays using for each constructs. This enables dynamic tables, lists, or repeating HTML blocks for each recipient.

  • What are triple curly braces used for?

    {{{ variable }}} inserts raw HTML without escaping. It’s required when your substitution data already includes HTML markup.

  • Can I send attachments with templates?

    Not directly through template fields — but you can by storing an email_rfc822 template that includes MIME attachment parts.

  • Does substitution still work in RFC 5322 templates?

    Yes, but only within the headers and the first HTML + text MIME parts.

  • When should I use the transmission endpoint instead of templates?

    When sending dynamic attachments, inline images, or when you need SparkPost to abstract MIME handling automatically.

  • Is it better to embed view logic or keep it separate?

    Best practice is to keep your view markup and data separate. Use templates for presentation and pass clean, structured substitution data.

  • Can I generate a full MIME email using a programming library?

    Yes. Libraries like JavaMail allow you to construct RFC 5322 messages programmatically and send them through SparkPost’s transmission API.

  • Are advanced template features widely used?

    Surprisingly few developers use them, but they unlock powerful personalization: loops, conditionals, inline logic, and custom MIME structures.

  • Why would I ever need RFC 5322 templates?

    Only when sending complex multi-part emails (attachments, custom MIME types) that SparkPost’s standard template abstraction doesn’t support.

This post is directed at the developer who wants to get the most out of SparkPost’s email template capabilities. It is assumed you are comfortable with reading JSON content and following basic programming flow. As terms that maybe new to you are introduced like RFC 5322, the text is linked to its source reference.

With that out of the way, let’s jump right in.

SparkPost’s template and transmission capabilities make sending emails straight forward. Those capabilities provide an abstraction for text and HTML content which means most times there is no need to directly encode the raw email format which is defined in RFC 5322 formerly known as (RFC 822). But sometimes you may want to create more complex messages that have other Multipurpose Internet Mail Extensions (MIME) parts that are not directly exposed via the SparkPost’s RESTful interface.

This post is directed at the developer who wants to get the most out of SparkPost’s email template capabilities. It is assumed you are comfortable with reading JSON content and following basic programming flow. As terms that maybe new to you are introduced like RFC 5322, the text is linked to its source reference.

With that out of the way, let’s jump right in.

SparkPost’s template and transmission capabilities make sending emails straight forward. Those capabilities provide an abstraction for text and HTML content which means most times there is no need to directly encode the raw email format which is defined in RFC 5322 formerly known as (RFC 822). But sometimes you may want to create more complex messages that have other Multipurpose Internet Mail Extensions (MIME) parts that are not directly exposed via the SparkPost’s RESTful interface.

This post is directed at the developer who wants to get the most out of SparkPost’s email template capabilities. It is assumed you are comfortable with reading JSON content and following basic programming flow. As terms that maybe new to you are introduced like RFC 5322, the text is linked to its source reference.

With that out of the way, let’s jump right in.

SparkPost’s template and transmission capabilities make sending emails straight forward. Those capabilities provide an abstraction for text and HTML content which means most times there is no need to directly encode the raw email format which is defined in RFC 5322 formerly known as (RFC 822). But sometimes you may want to create more complex messages that have other Multipurpose Internet Mail Extensions (MIME) parts that are not directly exposed via the SparkPost’s RESTful interface.

Simplified Email Composition

First, let’s review a sunny day scenario for sending an email. Use the transmission endpoint to provide the text and HTML content. Behind the scenes, SparkPost takes care of composing a valid RFC 5322 email. SparkPost will insert substitution variables from substitution_data into the text and HTML content. This is a powerful way to generate custom content for each recipient in a common template.

Here is an example transmission with HTML and text content with substitution_data.

{
  "options": {
    "open_tracking": true,
    "click_tracking": true
  },
  "campaign_id": "christmas_campaign",
  "return_path": "bounces-christmas-campaign@domain.com",
  "metadata": {
    "user_type": "students"
  },
  "substitution_data": {
    "sender": "Big Store Team"
  },
  "recipients": [
    {
      "return_path": "123@bounces.domain.com",
      "address": {
        "email": "wilma@domain.com",
        "name": "Wilma Flintstone"
      },
      "tags": [
        "greeting",
        "prehistoric",
        "fred",
        "flintstone"
      ],
      "metadata": {
        "place": "Bedrock"
      },
      "substitution_data": {
        "customer_type": "Platinum"
      }
    }
  ],
  "content": {
    "from": {
      "name": "Fred Flintstone",
      "email": "fred@domain.com"
    },
    "subject": "Big Christmas savings!",
    "reply_to": "Christmas Sales <sales@domain.com>",
    "headers": {
      "X-Customer-Campaign-ID": "christmas_campaign"
    },
    "text": "Hi {{address.name}} \nSave big this Christmas in your area {{place}}! \nClick http://www.mysite.com and get huge discount\n Hurry, this offer is only to {{user_type}}\n {{sender}}",
    "html": "<p>Hi {{address.name}} <br>Save big this Christmas in your area {{place}}! <br>Click <a href=\"http://www.mysite.com\">here</a> and get huge discount</p><p>Hurry, this offer is only to {{user_type}}</p><p>{{sender}}</p>"
  }
}

First, let’s review a sunny day scenario for sending an email. Use the transmission endpoint to provide the text and HTML content. Behind the scenes, SparkPost takes care of composing a valid RFC 5322 email. SparkPost will insert substitution variables from substitution_data into the text and HTML content. This is a powerful way to generate custom content for each recipient in a common template.

Here is an example transmission with HTML and text content with substitution_data.

{
  "options": {
    "open_tracking": true,
    "click_tracking": true
  },
  "campaign_id": "christmas_campaign",
  "return_path": "bounces-christmas-campaign@domain.com",
  "metadata": {
    "user_type": "students"
  },
  "substitution_data": {
    "sender": "Big Store Team"
  },
  "recipients": [
    {
      "return_path": "123@bounces.domain.com",
      "address": {
        "email": "wilma@domain.com",
        "name": "Wilma Flintstone"
      },
      "tags": [
        "greeting",
        "prehistoric",
        "fred",
        "flintstone"
      ],
      "metadata": {
        "place": "Bedrock"
      },
      "substitution_data": {
        "customer_type": "Platinum"
      }
    }
  ],
  "content": {
    "from": {
      "name": "Fred Flintstone",
      "email": "fred@domain.com"
    },
    "subject": "Big Christmas savings!",
    "reply_to": "Christmas Sales <sales@domain.com>",
    "headers": {
      "X-Customer-Campaign-ID": "christmas_campaign"
    },
    "text": "Hi {{address.name}} \nSave big this Christmas in your area {{place}}! \nClick http://www.mysite.com and get huge discount\n Hurry, this offer is only to {{user_type}}\n {{sender}}",
    "html": "<p>Hi {{address.name}} <br>Save big this Christmas in your area {{place}}! <br>Click <a href=\"http://www.mysite.com\">here</a> and get huge discount</p><p>Hurry, this offer is only to {{user_type}}</p><p>{{sender}}</p>"
  }
}

First, let’s review a sunny day scenario for sending an email. Use the transmission endpoint to provide the text and HTML content. Behind the scenes, SparkPost takes care of composing a valid RFC 5322 email. SparkPost will insert substitution variables from substitution_data into the text and HTML content. This is a powerful way to generate custom content for each recipient in a common template.

Here is an example transmission with HTML and text content with substitution_data.

{
  "options": {
    "open_tracking": true,
    "click_tracking": true
  },
  "campaign_id": "christmas_campaign",
  "return_path": "bounces-christmas-campaign@domain.com",
  "metadata": {
    "user_type": "students"
  },
  "substitution_data": {
    "sender": "Big Store Team"
  },
  "recipients": [
    {
      "return_path": "123@bounces.domain.com",
      "address": {
        "email": "wilma@domain.com",
        "name": "Wilma Flintstone"
      },
      "tags": [
        "greeting",
        "prehistoric",
        "fred",
        "flintstone"
      ],
      "metadata": {
        "place": "Bedrock"
      },
      "substitution_data": {
        "customer_type": "Platinum"
      }
    }
  ],
  "content": {
    "from": {
      "name": "Fred Flintstone",
      "email": "fred@domain.com"
    },
    "subject": "Big Christmas savings!",
    "reply_to": "Christmas Sales <sales@domain.com>",
    "headers": {
      "X-Customer-Campaign-ID": "christmas_campaign"
    },
    "text": "Hi {{address.name}} \nSave big this Christmas in your area {{place}}! \nClick http://www.mysite.com and get huge discount\n Hurry, this offer is only to {{user_type}}\n {{sender}}",
    "html": "<p>Hi {{address.name}} <br>Save big this Christmas in your area {{place}}! <br>Click <a href=\"http://www.mysite.com\">here</a> and get huge discount</p><p>Hurry, this offer is only to {{user_type}}</p><p>{{sender}}</p>"
  }
}

Substitute Arrays of Data 

Many folks realize that SparkPost’s transmission and template endpoints can do simple content substitution in email headers and email bodies. But many overlook the ability to provide conditional content or arrays of data that can be substituted as well. You can also provide unique content per recipient. In this example we send an array of unique links to each recipient.

This is accomplished by providing a JSON array of data that will be populated into the email body. Once the data is provided SparkPost will use logic in the template to populate it.

In this example SparkPost will look for substitution data called “files_html” and do a “for each” on each element in the array. It will create a row with the value of “file” in the “files_html” element. Note the triple curly around “loop_var.file“. This is because each element of the array contains HTML and we need to tell the server to use it as is and not escape it. The text part will be a simple text label and the URL to the file.

<table>
  {{each files_html}}
    <tr>
      <td>{{{loop_var.file}}}</td>
    </tr>
  {{ end }}
</table>

Here is the completed working example:

{
  "recipients": [
    {
      "address": {
        "email": "recipient1@domain.com"
      },
      "substitution_data": {
        "files_html": [
          {
            "file": "<a href=\"http://domain.com/file1a.txt\">File 1a Description</a>"
          },
          {
            "file": "<a href=\"http://domain.com/file2a.txt\">File 2a Description</a>"
          }
        ],
        "files_plain": [
          {
            "file": "File 1a -- http://domain.com/file1a.txt"
          },
          {
            "file": "File 2a -- http://domain.com/file2a.txt"
          }
        ]
      }
    },
    {
      "address": {
        "email": "recipient2@domain.com"
      },
      "substitution_data": {
        "files_html": [
          {
            "file": "<a href=\"http://domain.com/file1b.txt\">File 1b Description</a>"
          },
          {
            "file": "<a href=\"http://domain.com/file2b.txt\">File 2b Description</a>"
          }
        ],
        "files_plain": [
          {
            "file": "File 1b -- http://domain.com/file1b.txt"
          },
          {
            "file": "File 2b -- http://domain.com/file2b.txt"
          }
        ]
      }
    }
  ],
  "return_path": "chris@test.domain.com",
  "content": {
    "from": {
      "name": "chris@test.domain.com",
      "email": "chris@test.domain.com"
    },
    "subject": "Sending with SparkPost is Fun",
    "html": "<b>Your Files:</b><br>\n<table>\n  {{each files_html}}\n    <tr><td>{{{loop_var.file}}}</td></tr>\n  {{ end }}\n</table>\n",
    "text": "Your Files:\n{{each files_plain}} {{loop_var.file}}\n{{ end }}\n"
  }
}

Pro Tip: In your code it is advisable to keep the view markup separate from the data but the goal here was to keep the example as simple and easy to follow as possible so we created two arrays. One array is for HTML part and the other is for the Text part. In production use it would be common to have one set of data and write the logic in the template code.

Many folks realize that SparkPost’s transmission and template endpoints can do simple content substitution in email headers and email bodies. But many overlook the ability to provide conditional content or arrays of data that can be substituted as well. You can also provide unique content per recipient. In this example we send an array of unique links to each recipient.

This is accomplished by providing a JSON array of data that will be populated into the email body. Once the data is provided SparkPost will use logic in the template to populate it.

In this example SparkPost will look for substitution data called “files_html” and do a “for each” on each element in the array. It will create a row with the value of “file” in the “files_html” element. Note the triple curly around “loop_var.file“. This is because each element of the array contains HTML and we need to tell the server to use it as is and not escape it. The text part will be a simple text label and the URL to the file.

<table>
  {{each files_html}}
    <tr>
      <td>{{{loop_var.file}}}</td>
    </tr>
  {{ end }}
</table>

Here is the completed working example:

{
  "recipients": [
    {
      "address": {
        "email": "recipient1@domain.com"
      },
      "substitution_data": {
        "files_html": [
          {
            "file": "<a href=\"http://domain.com/file1a.txt\">File 1a Description</a>"
          },
          {
            "file": "<a href=\"http://domain.com/file2a.txt\">File 2a Description</a>"
          }
        ],
        "files_plain": [
          {
            "file": "File 1a -- http://domain.com/file1a.txt"
          },
          {
            "file": "File 2a -- http://domain.com/file2a.txt"
          }
        ]
      }
    },
    {
      "address": {
        "email": "recipient2@domain.com"
      },
      "substitution_data": {
        "files_html": [
          {
            "file": "<a href=\"http://domain.com/file1b.txt\">File 1b Description</a>"
          },
          {
            "file": "<a href=\"http://domain.com/file2b.txt\">File 2b Description</a>"
          }
        ],
        "files_plain": [
          {
            "file": "File 1b -- http://domain.com/file1b.txt"
          },
          {
            "file": "File 2b -- http://domain.com/file2b.txt"
          }
        ]
      }
    }
  ],
  "return_path": "chris@test.domain.com",
  "content": {
    "from": {
      "name": "chris@test.domain.com",
      "email": "chris@test.domain.com"
    },
    "subject": "Sending with SparkPost is Fun",
    "html": "<b>Your Files:</b><br>\n<table>\n  {{each files_html}}\n    <tr><td>{{{loop_var.file}}}</td></tr>\n  {{ end }}\n</table>\n",
    "text": "Your Files:\n{{each files_plain}} {{loop_var.file}}\n{{ end }}\n"
  }
}

Pro Tip: In your code it is advisable to keep the view markup separate from the data but the goal here was to keep the example as simple and easy to follow as possible so we created two arrays. One array is for HTML part and the other is for the Text part. In production use it would be common to have one set of data and write the logic in the template code.

Many folks realize that SparkPost’s transmission and template endpoints can do simple content substitution in email headers and email bodies. But many overlook the ability to provide conditional content or arrays of data that can be substituted as well. You can also provide unique content per recipient. In this example we send an array of unique links to each recipient.

This is accomplished by providing a JSON array of data that will be populated into the email body. Once the data is provided SparkPost will use logic in the template to populate it.

In this example SparkPost will look for substitution data called “files_html” and do a “for each” on each element in the array. It will create a row with the value of “file” in the “files_html” element. Note the triple curly around “loop_var.file“. This is because each element of the array contains HTML and we need to tell the server to use it as is and not escape it. The text part will be a simple text label and the URL to the file.

<table>
  {{each files_html}}
    <tr>
      <td>{{{loop_var.file}}}</td>
    </tr>
  {{ end }}
</table>

Here is the completed working example:

{
  "recipients": [
    {
      "address": {
        "email": "recipient1@domain.com"
      },
      "substitution_data": {
        "files_html": [
          {
            "file": "<a href=\"http://domain.com/file1a.txt\">File 1a Description</a>"
          },
          {
            "file": "<a href=\"http://domain.com/file2a.txt\">File 2a Description</a>"
          }
        ],
        "files_plain": [
          {
            "file": "File 1a -- http://domain.com/file1a.txt"
          },
          {
            "file": "File 2a -- http://domain.com/file2a.txt"
          }
        ]
      }
    },
    {
      "address": {
        "email": "recipient2@domain.com"
      },
      "substitution_data": {
        "files_html": [
          {
            "file": "<a href=\"http://domain.com/file1b.txt\">File 1b Description</a>"
          },
          {
            "file": "<a href=\"http://domain.com/file2b.txt\">File 2b Description</a>"
          }
        ],
        "files_plain": [
          {
            "file": "File 1b -- http://domain.com/file1b.txt"
          },
          {
            "file": "File 2b -- http://domain.com/file2b.txt"
          }
        ]
      }
    }
  ],
  "return_path": "chris@test.domain.com",
  "content": {
    "from": {
      "name": "chris@test.domain.com",
      "email": "chris@test.domain.com"
    },
    "subject": "Sending with SparkPost is Fun",
    "html": "<b>Your Files:</b><br>\n<table>\n  {{each files_html}}\n    <tr><td>{{{loop_var.file}}}</td></tr>\n  {{ end }}\n</table>\n",
    "text": "Your Files:\n{{each files_plain}} {{loop_var.file}}\n{{ end }}\n"
  }
}

Pro Tip: In your code it is advisable to keep the view markup separate from the data but the goal here was to keep the example as simple and easy to follow as possible so we created two arrays. One array is for HTML part and the other is for the Text part. In production use it would be common to have one set of data and write the logic in the template code.

Attachments in Transmission Capabilities

The transmission endpoint also provides an abstraction for sending attachments. Below you will see attachments are specified in the content.attachments array where each object in the array describes an individual attachment item. Just as before SparkPost will take care of encoding text, HTML, substitutions and iterating through the attachment array to encode a properly formed email message.

Best practices dictate that sending attachments is best avoided unless explicitly required as part of your service.

Below are the required fields for an attachment:

  • type: The MIME type of the attachment

  • name: The filename of the attachment

  • data: Base64-encoded file data

This is what an attachment looks like inside transmission content stanza:

"content": {
  "attachments": [
    {
      "type": "audio/mp3",
      "name": "voicemail.mp3",
      "data": "TVAzIERhdGEK"
    }
  ]
}

You can also send “inline images” in a transmission. These are very similar to attachments and are specified in the content.inline_images array where each of the inline_image objects are similar to the attachment object shown above.

The transmission endpoint also provides an abstraction for sending attachments. Below you will see attachments are specified in the content.attachments array where each object in the array describes an individual attachment item. Just as before SparkPost will take care of encoding text, HTML, substitutions and iterating through the attachment array to encode a properly formed email message.

Best practices dictate that sending attachments is best avoided unless explicitly required as part of your service.

Below are the required fields for an attachment:

  • type: The MIME type of the attachment

  • name: The filename of the attachment

  • data: Base64-encoded file data

This is what an attachment looks like inside transmission content stanza:

"content": {
  "attachments": [
    {
      "type": "audio/mp3",
      "name": "voicemail.mp3",
      "data": "TVAzIERhdGEK"
    }
  ]
}

You can also send “inline images” in a transmission. These are very similar to attachments and are specified in the content.inline_images array where each of the inline_image objects are similar to the attachment object shown above.

The transmission endpoint also provides an abstraction for sending attachments. Below you will see attachments are specified in the content.attachments array where each object in the array describes an individual attachment item. Just as before SparkPost will take care of encoding text, HTML, substitutions and iterating through the attachment array to encode a properly formed email message.

Best practices dictate that sending attachments is best avoided unless explicitly required as part of your service.

Below are the required fields for an attachment:

  • type: The MIME type of the attachment

  • name: The filename of the attachment

  • data: Base64-encoded file data

This is what an attachment looks like inside transmission content stanza:

"content": {
  "attachments": [
    {
      "type": "audio/mp3",
      "name": "voicemail.mp3",
      "data": "TVAzIERhdGEK"
    }
  ]
}

You can also send “inline images” in a transmission. These are very similar to attachments and are specified in the content.inline_images array where each of the inline_image objects are similar to the attachment object shown above.

Attachments in Templates

Now that we have the proper background for sending attachments with the transmission endpoint let’s take a look at how to do this with templates. At the time of this writing, there is no attachments abstraction like you find for inline transmissions. One might conclude that templates can’t be created with attachments. You would be partially correct but there is a work around, although you will no longer be isolated from the RFC 5322 format.

You can accomplish attachments in templates by encoding RFC 5322 content yourself which includes the attachments. The good news is you won’t lose the ability to still use Substitution Data in your email headers, HTML and text parts. Be aware that this type of template limits the substitutions to the headers and the first HTML and first text part.

Here is an example of how it is done.

Now that we have the proper background for sending attachments with the transmission endpoint let’s take a look at how to do this with templates. At the time of this writing, there is no attachments abstraction like you find for inline transmissions. One might conclude that templates can’t be created with attachments. You would be partially correct but there is a work around, although you will no longer be isolated from the RFC 5322 format.

You can accomplish attachments in templates by encoding RFC 5322 content yourself which includes the attachments. The good news is you won’t lose the ability to still use Substitution Data in your email headers, HTML and text parts. Be aware that this type of template limits the substitutions to the headers and the first HTML and first text part.

Here is an example of how it is done.

Now that we have the proper background for sending attachments with the transmission endpoint let’s take a look at how to do this with templates. At the time of this writing, there is no attachments abstraction like you find for inline transmissions. One might conclude that templates can’t be created with attachments. You would be partially correct but there is a work around, although you will no longer be isolated from the RFC 5322 format.

You can accomplish attachments in templates by encoding RFC 5322 content yourself which includes the attachments. The good news is you won’t lose the ability to still use Substitution Data in your email headers, HTML and text parts. Be aware that this type of template limits the substitutions to the headers and the first HTML and first text part.

Here is an example of how it is done.

RFC822 Email

Create your RFC 5322 email with the substitution data you want. I created this one in my mail client and sent it to myself. Once I received it I copied the source and replace the fields I want to dynamically substitute.

MIME-Version: 1.0
Reply-To: {{replyto}}
Subject: {{subject}}
From: {{from}}
To: {{address.email}}
Content-Type: multipart/mixed; boundary="001a113c48b0b89d92052d3051da"
--001a113c48b0b89d92052d3051da
Content-Type: multipart/alternative; boundary="001a113c48b0b89d89052d3051d8"
--001a113c48b0b89d89052d3051d8
Content-Type: text/plain; charset=UTF-8
Email with a *text attachment*.
{{body2}}
--001a113c48b0b89d89052d3051d8
Content-Type: text/html; charset=UTF-8
<div dir="ltr">
  <div>Email with a <i>text attachment</i>.</div>
  {{body1}}
<

The last MIME part in this message you will see Content-Disposition: attachment; filename=myfile.txt”. That is where the name of the file is defined. Your attachment content will most certainly be much more complex but this example is trying to keep it simple.

Create your RFC 5322 email with the substitution data you want. I created this one in my mail client and sent it to myself. Once I received it I copied the source and replace the fields I want to dynamically substitute.

MIME-Version: 1.0
Reply-To: {{replyto}}
Subject: {{subject}}
From: {{from}}
To: {{address.email}}
Content-Type: multipart/mixed; boundary="001a113c48b0b89d92052d3051da"
--001a113c48b0b89d92052d3051da
Content-Type: multipart/alternative; boundary="001a113c48b0b89d89052d3051d8"
--001a113c48b0b89d89052d3051d8
Content-Type: text/plain; charset=UTF-8
Email with a *text attachment*.
{{body2}}
--001a113c48b0b89d89052d3051d8
Content-Type: text/html; charset=UTF-8
<div dir="ltr">
  <div>Email with a <i>text attachment</i>.</div>
  {{body1}}
<

The last MIME part in this message you will see Content-Disposition: attachment; filename=myfile.txt”. That is where the name of the file is defined. Your attachment content will most certainly be much more complex but this example is trying to keep it simple.

Create your RFC 5322 email with the substitution data you want. I created this one in my mail client and sent it to myself. Once I received it I copied the source and replace the fields I want to dynamically substitute.

MIME-Version: 1.0
Reply-To: {{replyto}}
Subject: {{subject}}
From: {{from}}
To: {{address.email}}
Content-Type: multipart/mixed; boundary="001a113c48b0b89d92052d3051da"
--001a113c48b0b89d92052d3051da
Content-Type: multipart/alternative; boundary="001a113c48b0b89d89052d3051d8"
--001a113c48b0b89d89052d3051d8
Content-Type: text/plain; charset=UTF-8
Email with a *text attachment*.
{{body2}}
--001a113c48b0b89d89052d3051d8
Content-Type: text/html; charset=UTF-8
<div dir="ltr">
  <div>Email with a <i>text attachment</i>.</div>
  {{body1}}
<

The last MIME part in this message you will see Content-Disposition: attachment; filename=myfile.txt”. That is where the name of the file is defined. Your attachment content will most certainly be much more complex but this example is trying to keep it simple.

Stored Template

Once you have a valid RFC 5322 email store it using the email_rfc822 form of the template endpoint instead of using text and HTML fields. Here is an example of what the content looks like for that message:

{
  "content": {
    "email_rfc822": "MIME-Version: 1.0\nReply-To: {{replyto}}\nSubject: {{subject}}\nFrom: {{from}}\nTo: {{address.email}}\nContent-Type: multipart/mixed; boundary=001a113c48b0b89d92052d3051da\n\n--001a113c48b0b89d92052d3051da\nContent-Type: multipart/alternative; boundary=001a113c48b0b89d89052d3051d8\n\n--001a113c48b0b89d89052d3051d8\nContent-Type: text/plain; charset=UTF-8\n\nEmail with a *text attachment*.\n\n{{body2}}\n\n--001a113c48b0b89d89052d3051d8\nContent-Type: text/html; charset=UTF-8\n\n<div dir=\"ltr\"><div>Email with a <i>text attachment</i>.</div>\n\n{{body1}}\n</div>\n\n--001a113c48b0b89d89052d3051d8--\n--001a113c48b0b89d92052d3051da\nContent-Type: text/plain; charset=US-ASCII; name=\"myfile.txt\"\nContent-Disposition: attachment; filename=\"myfile.txt\"\nContent-Transfer-Encoding: base64\nX-Attachment-Id: f_ild455ce0\n\nVGhpcyBpcyBteSBzaW1wbGUgdGV4dCBmaWxlLgo=\n--001a113c48b0b89d92052d3051da--"
  },
  "name": "_TMP_TEMPLATE_TEST"
}

When the request completes, SparkPost will respond with a unique identifier for your new template. For example xxxxxxx.

Once you have a valid RFC 5322 email store it using the email_rfc822 form of the template endpoint instead of using text and HTML fields. Here is an example of what the content looks like for that message:

{
  "content": {
    "email_rfc822": "MIME-Version: 1.0\nReply-To: {{replyto}}\nSubject: {{subject}}\nFrom: {{from}}\nTo: {{address.email}}\nContent-Type: multipart/mixed; boundary=001a113c48b0b89d92052d3051da\n\n--001a113c48b0b89d92052d3051da\nContent-Type: multipart/alternative; boundary=001a113c48b0b89d89052d3051d8\n\n--001a113c48b0b89d89052d3051d8\nContent-Type: text/plain; charset=UTF-8\n\nEmail with a *text attachment*.\n\n{{body2}}\n\n--001a113c48b0b89d89052d3051d8\nContent-Type: text/html; charset=UTF-8\n\n<div dir=\"ltr\"><div>Email with a <i>text attachment</i>.</div>\n\n{{body1}}\n</div>\n\n--001a113c48b0b89d89052d3051d8--\n--001a113c48b0b89d92052d3051da\nContent-Type: text/plain; charset=US-ASCII; name=\"myfile.txt\"\nContent-Disposition: attachment; filename=\"myfile.txt\"\nContent-Transfer-Encoding: base64\nX-Attachment-Id: f_ild455ce0\n\nVGhpcyBpcyBteSBzaW1wbGUgdGV4dCBmaWxlLgo=\n--001a113c48b0b89d92052d3051da--"
  },
  "name": "_TMP_TEMPLATE_TEST"
}

When the request completes, SparkPost will respond with a unique identifier for your new template. For example xxxxxxx.

Once you have a valid RFC 5322 email store it using the email_rfc822 form of the template endpoint instead of using text and HTML fields. Here is an example of what the content looks like for that message:

{
  "content": {
    "email_rfc822": "MIME-Version: 1.0\nReply-To: {{replyto}}\nSubject: {{subject}}\nFrom: {{from}}\nTo: {{address.email}}\nContent-Type: multipart/mixed; boundary=001a113c48b0b89d92052d3051da\n\n--001a113c48b0b89d92052d3051da\nContent-Type: multipart/alternative; boundary=001a113c48b0b89d89052d3051d8\n\n--001a113c48b0b89d89052d3051d8\nContent-Type: text/plain; charset=UTF-8\n\nEmail with a *text attachment*.\n\n{{body2}}\n\n--001a113c48b0b89d89052d3051d8\nContent-Type: text/html; charset=UTF-8\n\n<div dir=\"ltr\"><div>Email with a <i>text attachment</i>.</div>\n\n{{body1}}\n</div>\n\n--001a113c48b0b89d89052d3051d8--\n--001a113c48b0b89d92052d3051da\nContent-Type: text/plain; charset=US-ASCII; name=\"myfile.txt\"\nContent-Disposition: attachment; filename=\"myfile.txt\"\nContent-Transfer-Encoding: base64\nX-Attachment-Id: f_ild455ce0\n\nVGhpcyBpcyBteSBzaW1wbGUgdGV4dCBmaWxlLgo=\n--001a113c48b0b89d92052d3051da--"
  },
  "name": "_TMP_TEMPLATE_TEST"
}

When the request completes, SparkPost will respond with a unique identifier for your new template. For example xxxxxxx.

Sending the Template

The good news is that creating the RFC 5322 content was the hard part. From here on out sending that template with SparkPost is exactly the same as sending any other template.

Here is how we send that template and populate the substitution data:

{
  "campaign_id": "MyCampaign",
  "return_path": "myReturnPath@yourdomain.com",
  "substitution_data": {
    "replyto": "myReplyToh@yourdomain.com",
    "from": "MyFrom@yourdomain.com",
    "subject": "my subject",
    "body1": "Extra content for the HTML part",
    "body2": "Extra content for the text part"
  },
  "recipients": [
    {
      "substitution_data": {},
      "address": {
        "email": "test1@domain.com",
        "name": "test1"
      }
    }
  ],
  "content": {
    "template_id": "xxxxxxx",
    "use_draft_template": true
  }
}

The good news is that creating the RFC 5322 content was the hard part. From here on out sending that template with SparkPost is exactly the same as sending any other template.

Here is how we send that template and populate the substitution data:

{
  "campaign_id": "MyCampaign",
  "return_path": "myReturnPath@yourdomain.com",
  "substitution_data": {
    "replyto": "myReplyToh@yourdomain.com",
    "from": "MyFrom@yourdomain.com",
    "subject": "my subject",
    "body1": "Extra content for the HTML part",
    "body2": "Extra content for the text part"
  },
  "recipients": [
    {
      "substitution_data": {},
      "address": {
        "email": "test1@domain.com",
        "name": "test1"
      }
    }
  ],
  "content": {
    "template_id": "xxxxxxx",
    "use_draft_template": true
  }
}

The good news is that creating the RFC 5322 content was the hard part. From here on out sending that template with SparkPost is exactly the same as sending any other template.

Here is how we send that template and populate the substitution data:

{
  "campaign_id": "MyCampaign",
  "return_path": "myReturnPath@yourdomain.com",
  "substitution_data": {
    "replyto": "myReplyToh@yourdomain.com",
    "from": "MyFrom@yourdomain.com",
    "subject": "my subject",
    "body1": "Extra content for the HTML part",
    "body2": "Extra content for the text part"
  },
  "recipients": [
    {
      "substitution_data": {},
      "address": {
        "email": "test1@domain.com",
        "name": "test1"
      }
    }
  ],
  "content": {
    "template_id": "xxxxxxx",
    "use_draft_template": true
  }
}

Templates from a Mail Client’s API

If you are using a programming language that has a library for composing an email, you can use that to programmatically create the template or even send the message inline. Here is an example of using JavaMail through SparkPost’s transmission endpoint. This method should be easily translated to PHP or your language of choice.

/**
 * Demonstration of using JavaMail MIME message with the SparkPost RESTful interface
 */
public class App extends SparkPostBaseApp {
    public static void main(String[] args) throws Exception {
        Logger.getRootLogger().setLevel(Level.DEBUG);
        App app = new App();
        app.runApp();
    }
    private void runApp() throws Exception {
        Message message = createMultipartMessage();
        // Convert JavaMail message into a string for transmission
        String rfc822Content = getMessageAsString(message);
        // Add dynamic To and From using SparkPost substitution syntax
        rfc822Content = "To: {{address.email}}\r\nFrom: {{from}}\r\n" + rfc822Content;
        String fromAddress = getFromAddress();
        String[] recipients = getTestRecipients();
        sendEmail(fromAddress, recipients, rfc822Content);
    }
    private void sendEmail(String from, String[] recipients, String email)
            throws SparkPostException, IOException {
        Client sparkpostClient = newConfiguredClient();
        TransmissionWithRecipientArray transmission = new TransmissionWithRecipientArray();
        // Populate Recipients
        List<RecipientAttributes> recipientArray = new ArrayList<>();
        for (String recipient : recipients) {
            RecipientAttributes recipientAttribs = new RecipientAttributes();
            recipientAttribs.setAddress(new AddressAttributes(recipient));
            recipientArray.add(recipientAttribs);
        }
        transmission.setRecipientArray(recipientArray);
        transmission.setReturnPath(from);
        // Populate Substitution Data
        Map<String, String> substitutionData = new HashMap<>();
        substitutionData.put("from", from);
        substitutionData.put("name", "Your Name Here");
        transmission.setSubstitutionData(substitutionData);
        // Populate Email Body with RFC822 MIME
        TemplateContentAttributes contentAttributes = new TemplateContentAttributes();
        contentAttributes.setEmailRFC822(email);
        transmission.setContentAttributes(contentAttributes);
        // Send Email
        RestConnection connection = new RestConnection(sparkpostClient, getEndPoint());
        Response response = ResourceTransmissions.create(connection, 0, transmission);
        if (response.getResponseCode() == 200) {
            System.out.println("✅ Transmission Response: " + response);
        } else {
            System.err.println("❌ TRANSMISSION ERROR: " + response);
        }
    }
    /**
     * Builds an email with text, HTML, and attachment parts
     */
    private Message createMultipartMessage() throws MessagingException {
        Properties props = new Properties();
        props.put("mail.smtp.host", "none"); // Required for JavaMail to work
        Session session = Session.getDefaultInstance(props, null);
        Message message = new MimeMessage(session);
        message.setSubject("A multipart MIME message demo");
        // Main multipart container
        Multipart multiPart = new MimeMultipart("mixed");
        // Sub multipart for text + HTML
        MimeMultipart altPart = new MimeMultipart("alternative");
        // Text part
        MimeBodyPart textPart = new MimeBodyPart();
        textPart.setText("{{name}},\r\nplain text content", "utf-8");
        // HTML part
        MimeBodyPart htmlPart = new MimeBodyPart();
        htmlPart.setContent("<b>{{name}},<br><br>Our HTML content</b>", "text/html; charset=utf-8");
        // Add text and HTML to the alternative container
        altPart.addBodyPart(textPart);
        altPart.addBodyPart(htmlPart);
        // Wrap alternative part in a MimeBodyPart so it can be added to mixed container
        MimeBodyPart altBodyPart = new MimeBodyPart();
        altBodyPart.setContent(altPart);
        // Add alternative section to mixed container
        multiPart.addBodyPart(altBodyPart);
        // Add attachment
        MimeBodyPart attachmentPart = new MimeBodyPart();
        String filename = "java_SparkPost_background.pdf";
        DataSource source = new FileDataSource(filename);
        attachmentPart.setDataHandler(new DataHandler(source));
        attachmentPart.setFileName(filename);
        multiPart.addBodyPart(attachmentPart);
        // Set full content
        message.setContent(multiPart);
        return message;
    }
    /**
     * Converts a JavaMail message into an RFC822 string
     */
    private String getMessageAsString(Message msg) throws IOException, MessagingException {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try {
            msg.writeTo(out);
            return out.toString("UTF-8");
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("UTF-8 not found! " + e.getMessage());
        } finally {
            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

If you are using a programming language that has a library for composing an email, you can use that to programmatically create the template or even send the message inline. Here is an example of using JavaMail through SparkPost’s transmission endpoint. This method should be easily translated to PHP or your language of choice.

/**
 * Demonstration of using JavaMail MIME message with the SparkPost RESTful interface
 */
public class App extends SparkPostBaseApp {
    public static void main(String[] args) throws Exception {
        Logger.getRootLogger().setLevel(Level.DEBUG);
        App app = new App();
        app.runApp();
    }
    private void runApp() throws Exception {
        Message message = createMultipartMessage();
        // Convert JavaMail message into a string for transmission
        String rfc822Content = getMessageAsString(message);
        // Add dynamic To and From using SparkPost substitution syntax
        rfc822Content = "To: {{address.email}}\r\nFrom: {{from}}\r\n" + rfc822Content;
        String fromAddress = getFromAddress();
        String[] recipients = getTestRecipients();
        sendEmail(fromAddress, recipients, rfc822Content);
    }
    private void sendEmail(String from, String[] recipients, String email)
            throws SparkPostException, IOException {
        Client sparkpostClient = newConfiguredClient();
        TransmissionWithRecipientArray transmission = new TransmissionWithRecipientArray();
        // Populate Recipients
        List<RecipientAttributes> recipientArray = new ArrayList<>();
        for (String recipient : recipients) {
            RecipientAttributes recipientAttribs = new RecipientAttributes();
            recipientAttribs.setAddress(new AddressAttributes(recipient));
            recipientArray.add(recipientAttribs);
        }
        transmission.setRecipientArray(recipientArray);
        transmission.setReturnPath(from);
        // Populate Substitution Data
        Map<String, String> substitutionData = new HashMap<>();
        substitutionData.put("from", from);
        substitutionData.put("name", "Your Name Here");
        transmission.setSubstitutionData(substitutionData);
        // Populate Email Body with RFC822 MIME
        TemplateContentAttributes contentAttributes = new TemplateContentAttributes();
        contentAttributes.setEmailRFC822(email);
        transmission.setContentAttributes(contentAttributes);
        // Send Email
        RestConnection connection = new RestConnection(sparkpostClient, getEndPoint());
        Response response = ResourceTransmissions.create(connection, 0, transmission);
        if (response.getResponseCode() == 200) {
            System.out.println("✅ Transmission Response: " + response);
        } else {
            System.err.println("❌ TRANSMISSION ERROR: " + response);
        }
    }
    /**
     * Builds an email with text, HTML, and attachment parts
     */
    private Message createMultipartMessage() throws MessagingException {
        Properties props = new Properties();
        props.put("mail.smtp.host", "none"); // Required for JavaMail to work
        Session session = Session.getDefaultInstance(props, null);
        Message message = new MimeMessage(session);
        message.setSubject("A multipart MIME message demo");
        // Main multipart container
        Multipart multiPart = new MimeMultipart("mixed");
        // Sub multipart for text + HTML
        MimeMultipart altPart = new MimeMultipart("alternative");
        // Text part
        MimeBodyPart textPart = new MimeBodyPart();
        textPart.setText("{{name}},\r\nplain text content", "utf-8");
        // HTML part
        MimeBodyPart htmlPart = new MimeBodyPart();
        htmlPart.setContent("<b>{{name}},<br><br>Our HTML content</b>", "text/html; charset=utf-8");
        // Add text and HTML to the alternative container
        altPart.addBodyPart(textPart);
        altPart.addBodyPart(htmlPart);
        // Wrap alternative part in a MimeBodyPart so it can be added to mixed container
        MimeBodyPart altBodyPart = new MimeBodyPart();
        altBodyPart.setContent(altPart);
        // Add alternative section to mixed container
        multiPart.addBodyPart(altBodyPart);
        // Add attachment
        MimeBodyPart attachmentPart = new MimeBodyPart();
        String filename = "java_SparkPost_background.pdf";
        DataSource source = new FileDataSource(filename);
        attachmentPart.setDataHandler(new DataHandler(source));
        attachmentPart.setFileName(filename);
        multiPart.addBodyPart(attachmentPart);
        // Set full content
        message.setContent(multiPart);
        return message;
    }
    /**
     * Converts a JavaMail message into an RFC822 string
     */
    private String getMessageAsString(Message msg) throws IOException, MessagingException {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try {
            msg.writeTo(out);
            return out.toString("UTF-8");
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("UTF-8 not found! " + e.getMessage());
        } finally {
            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

If you are using a programming language that has a library for composing an email, you can use that to programmatically create the template or even send the message inline. Here is an example of using JavaMail through SparkPost’s transmission endpoint. This method should be easily translated to PHP or your language of choice.

/**
 * Demonstration of using JavaMail MIME message with the SparkPost RESTful interface
 */
public class App extends SparkPostBaseApp {
    public static void main(String[] args) throws Exception {
        Logger.getRootLogger().setLevel(Level.DEBUG);
        App app = new App();
        app.runApp();
    }
    private void runApp() throws Exception {
        Message message = createMultipartMessage();
        // Convert JavaMail message into a string for transmission
        String rfc822Content = getMessageAsString(message);
        // Add dynamic To and From using SparkPost substitution syntax
        rfc822Content = "To: {{address.email}}\r\nFrom: {{from}}\r\n" + rfc822Content;
        String fromAddress = getFromAddress();
        String[] recipients = getTestRecipients();
        sendEmail(fromAddress, recipients, rfc822Content);
    }
    private void sendEmail(String from, String[] recipients, String email)
            throws SparkPostException, IOException {
        Client sparkpostClient = newConfiguredClient();
        TransmissionWithRecipientArray transmission = new TransmissionWithRecipientArray();
        // Populate Recipients
        List<RecipientAttributes> recipientArray = new ArrayList<>();
        for (String recipient : recipients) {
            RecipientAttributes recipientAttribs = new RecipientAttributes();
            recipientAttribs.setAddress(new AddressAttributes(recipient));
            recipientArray.add(recipientAttribs);
        }
        transmission.setRecipientArray(recipientArray);
        transmission.setReturnPath(from);
        // Populate Substitution Data
        Map<String, String> substitutionData = new HashMap<>();
        substitutionData.put("from", from);
        substitutionData.put("name", "Your Name Here");
        transmission.setSubstitutionData(substitutionData);
        // Populate Email Body with RFC822 MIME
        TemplateContentAttributes contentAttributes = new TemplateContentAttributes();
        contentAttributes.setEmailRFC822(email);
        transmission.setContentAttributes(contentAttributes);
        // Send Email
        RestConnection connection = new RestConnection(sparkpostClient, getEndPoint());
        Response response = ResourceTransmissions.create(connection, 0, transmission);
        if (response.getResponseCode() == 200) {
            System.out.println("✅ Transmission Response: " + response);
        } else {
            System.err.println("❌ TRANSMISSION ERROR: " + response);
        }
    }
    /**
     * Builds an email with text, HTML, and attachment parts
     */
    private Message createMultipartMessage() throws MessagingException {
        Properties props = new Properties();
        props.put("mail.smtp.host", "none"); // Required for JavaMail to work
        Session session = Session.getDefaultInstance(props, null);
        Message message = new MimeMessage(session);
        message.setSubject("A multipart MIME message demo");
        // Main multipart container
        Multipart multiPart = new MimeMultipart("mixed");
        // Sub multipart for text + HTML
        MimeMultipart altPart = new MimeMultipart("alternative");
        // Text part
        MimeBodyPart textPart = new MimeBodyPart();
        textPart.setText("{{name}},\r\nplain text content", "utf-8");
        // HTML part
        MimeBodyPart htmlPart = new MimeBodyPart();
        htmlPart.setContent("<b>{{name}},<br><br>Our HTML content</b>", "text/html; charset=utf-8");
        // Add text and HTML to the alternative container
        altPart.addBodyPart(textPart);
        altPart.addBodyPart(htmlPart);
        // Wrap alternative part in a MimeBodyPart so it can be added to mixed container
        MimeBodyPart altBodyPart = new MimeBodyPart();
        altBodyPart.setContent(altPart);
        // Add alternative section to mixed container
        multiPart.addBodyPart(altBodyPart);
        // Add attachment
        MimeBodyPart attachmentPart = new MimeBodyPart();
        String filename = "java_SparkPost_background.pdf";
        DataSource source = new FileDataSource(filename);
        attachmentPart.setDataHandler(new DataHandler(source));
        attachmentPart.setFileName(filename);
        multiPart.addBodyPart(attachmentPart);
        // Set full content
        message.setContent(multiPart);
        return message;
    }
    /**
     * Converts a JavaMail message into an RFC822 string
     */
    private String getMessageAsString(Message msg) throws IOException, MessagingException {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try {
            msg.writeTo(out);
            return out.toString("UTF-8");
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("UTF-8 not found! " + e.getMessage());
        } finally {
            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

Conclusion

Now that you see how SparkPost can be used to send email of almost any complexity you may want to have a look at “SparkPost Supports Sending Email on Apple Watch” or have a look at the substitution syntax to see how it can be used with “if then else”, “expressions in conditionals” or “array Iteration” right inside your template or transmission content.

Now that you see how SparkPost can be used to send email of almost any complexity you may want to have a look at “SparkPost Supports Sending Email on Apple Watch” or have a look at the substitution syntax to see how it can be used with “if then else”, “expressions in conditionals” or “array Iteration” right inside your template or transmission content.

Now that you see how SparkPost can be used to send email of almost any complexity you may want to have a look at “SparkPost Supports Sending Email on Apple Watch” or have a look at the substitution syntax to see how it can be used with “if then else”, “expressions in conditionals” or “array Iteration” right inside your template or transmission content.

Other news

Read more from this category

A person is standing at a desk while typing on a laptop.

The complete AI-native platform that scales with your business.

© 2025 Bird

A person is standing at a desk while typing on a laptop.

The complete AI-native platform that scales with your business.

© 2025 Bird