Campaign Generator Help

Email For All - Text Messaging Supported for Enterprise Accounts


Welcome to Campaign Generator. First things first.... This tool was not created nor is it supported by SparkPost. This tool was created by some SparkPost community contributors and is open for anyone to use. This tool is free to use at your own risk and should be up and running at all times but is not monitored for uptime. The Campaign Generator DOES NOT hold any of your information, it simply uses your API key to obtain enough information in order to create, schedule or cancel your campaigns. The code behind this site is available from my Github repository at:
I created this campaign generation tool to fill in missing functionality from SparkPost. Since SparkPost is geared toward developers, anyone who needs to send Campaigns either had to build the functionality themselves or use one of SparkPost's partners. So I created this tool as a quick way to either launch a campaign now or to schedule it out for a later release. When using this tool, please understand the nuances of SparkPost because I won't document them in this tool. For example as of Sept 2016, campaigns can only be cancelled if they are scheduled for a time more than one day out. I'm not going to document all of these nuances and will let SparkPost documentation speak for itself; so if something isn't exactly working the way you expected it to work, please check their documentation first at, SparkPost Documentation

Logging in with a SparkPost API Key

The first step in using the generator is to obtain a SparkPost API key which will give this application access to your account. As stated before this key is NOT stored by this application and is only used during your sessions. If you don't have an API Key and need some help in generating one, here is a SparkPost article on the subject: Create API Keys.

At a minimum, the API Key needs the following authentication rights:

'Recipient Lists: Read/Write, Templates: Read/Write, Transmissions: Read/Write' and Sending Domains: Read.

Once you have the key, you can use it on the login/entry page to start the campaign process. In order to protect your key, the system will obfuscate your key through the rest of the session. Also, I have turned off auto-fill for the field that requests the API key, so the browser probably won't fill in that field the next time you come back to that page. I state this because the SparkPost API generator does not hold the key either so if you loose that key you will have to create another one (luckily you can create as many as you wish). The only other field on this page is the API Root directory. The Campaign Generator defaults to the API Root directory of (, but if you are using this application onsite after downloading it from Github, or you are an Elite/Enterprise customer, you need to enter in the appropriate URL. The system doesn't care if you have a trailing '/' after 'v1' or not; it doesn't need/want it, but will strip it off if you put it into the field.

Campaign Generator Login Page

Using Stored Templates and Recipient Lists for Campaign Generation

Here is one of two ways to submit a campaign. This approach uses the API Key to call the SparkPost server to compile a list of your published Templates and Recipient Lists that have been uploaded to the system. The first step is to select which Templates and Recipients you are interested in using for this campaign.


If you want to check to see that you have a good match, press the 'Preview & Validate' button directly below the Recipient list. At this time, the system will do several things:

  1. The Campaign Generator will grab the substitution data from the first recipient in the selected recipient list.

  2. Display what the email will look like by merging the recipient data with the template. The preview will be displayed at the bottom of the page in the Preview section. Please look at the content closely to see if it looks like the data is filling out the template correctly.

  3. The Campaign Generator will compare how the data and the template matches up in two different ways (see the lower table to the right of the Campaign Submission Form):

    • The first approach is to compare the fields in the substitution data against what fields the template is using. For each substitution field, the system will scan the template looking for it's usage (this can be in the form of a conditional or a substitution field). In the summary section, you will see a break out for several different counts:

      • How many fields were found in the substitution data
      • How many substitution fields match template fields
      • How many fields did not match
      • How many fields did not match, but were probably index fields within the data and would not expect to be found in the template. For example; let's say that the selected template is used to display 'x' number of products, where the number of products can change for each email. The products could be indexed in either the global substitution (or recipient) data section in the following manner:
        "products" : {
            "17823": {
                    "url": "",
                    "height" : "720",
                    "width" : "1420",
                    "price" : "$355.00",
                    "reduction" : "$10",
                    "title": "ALPS Mountaineering Hiking Shoes",
                    "owner" : "",
                    "category" : "Outdoor",
                    "image": ""
                "18056": {
                    "url": "",
                    "height" : "952",
                    "width" : "1200",
                    "price" : "$199.99 and Up",
                    "reduction" : "Up To 62%",
                    "owner" : "EggWedge",
                    "category" : "Outdoor",
                    "title": "Hike the World With Blue Lake",
                    "image": ""
                "18111": {
                    "url": "",
                    "height" : "952",
                    "width" : "1200",
                    "price" : "$259.00",
                    "reduction" : "Up To 65% Off",
                    "category" : "Outdoor",
                    "title": "One Man's Hotel...Live Under the Stars",
                    "image": ""
                "18152": {
                    "url": "",
                    "height" : "952",
                    "width" : "1200",
                    "price" : "$295.00",
                    "reduction" : "Up to 60% Off",
                    "owner" : "Stanza",
                    "category" : "Research",
                    "title": "Easy Six Course Meals",
                    "image": ""
        The numeric indexed products can then be used in a myriad number of ways. One example would be in a loop where the recipients data would have an array of which products to display: "products": ["18152", "18056"], while another users recipient data might have "products": ["17823", "18056", "18111" , "18152"]. Because the fields are solely being used within the json data structure for reference we wouldn't expect to see them referenced in the template fields themselves.

      • The last number represents what I call 'system indexes'. I plan on getting rid of those in the near future, but due to the language I'm using for some of the coding, the system adds array index numbers to my substitution fields when I make an array out of them before my comparison process (I know...blah, blah, blah). Anyway, any numeric substitution field under 50 is going to be labeled as a probably system index and the row will be displayed in 'yellow highlight' color. The only thing those index number affect is the comparison process. They don't show up in your template or change anything in the campaign. I'm only bringing it up because I haven't done the code to get rid of them and I don't want any confusion about where they came from.

    • The second approach is the reverse of the first. The Campaign Generator scans for all variables being used in the template and checks to see if there is matching substitution data. The summary is much simpler since it doesn't have to deal with the indexed products or system index numbers. So the summary simply shows:

      • How many fields were found in the template
      • How many matches were found in the substitution data
      • How many fields did not find a match in the substitution data

    We haven't discussed how to add in 'global substitution data' into the system just yet, but YES, the system does compare both recipient and global data to the template if any Global Data has been entered.

  4. If there is an error message from the server when it tries to create the preview, the system will warn you in three different ways:

    • An Alert Box will display telling you of an issue. If you wish to stop getting those warnings, some browsers let you select an option to stop receiving them. That option will stay in affect until the next time you come to this application with a new browser session/tab.
    • The error will display in the Preview Section
    • The submit button will turn off and turn red. You will not be able to submit a campaign until you either press the reset button, or you use the Preview button on a good Template/Recipient combination. The submit button will turn red only on significant errors that the SparkPost server found when trying to use the selected recipient data with that template. You can easily have recipient data that just doesn't match the template and the server won't care. So again, please look very carefully at the preview output. The server errors tend to be when conditional, loop or global data fields are missing.

Next to the 'Preview and Validate' button are two other buttons. The 'Show Global Sub' button exposes a new data entry field that allows the user to enter guessed, Global Substitution data. It is very very very important that you enter in this data properly. The field does have a sample in it that will only display when there is no data in that field; so as-soon-as you start to type data, it will disappear. The expected format is a json structure with the key name "substitution_data" at the beginning and the value(s) following it. It must be a proper json structure without the curly bracket before the substitution_data key word. Here is a 'simple' example:

"substitution_data" : 
     "company_home_url" : "",
     "company_logo" : "",
     "logo_height" : "20",
     "logo_width" : "75",
     "header_color" : "#cc6600",
     "header_box_color" : "#fff4ea",
     "order_generic_comments" : "Many thanks for your phone order!",
     "company" : "Slippery Bikes"

The substitution data can be as complex as you need, but does have a length limit of 70K characters. As explained before, this data will be used in the preview and the data comparison features discussed earlier. Notice that this field is expandable by pulling on the lower right-hand corner of the box. This is extremely useful in helping you see what you have entered.

The Global Data section will also be used during the campaign itself. Global Substitution fields are a great way to minimize your templates. I use Global Substitution for product arrays and white labeling a template by changing CSS fields dynamically with substitution data!

The next button is the 'Send Test Email' button that allows you to send a test email to a comma separated list of email addresses. It uses the same data that the preview is using, so those emails should be identical.

Now you can pick when you want the campaign to go out. You can either have it go out immediately, or you can schedule it out. Currently, SparkPost will reject any scheduled campaign farther out than a month. If you enter in a scheduled date/time, that will take precedence over the 'now' flag. The hours are 0-23 military hours. The SparkPost Server thinks in GMT so you will want to set the offset for when you want the campaign to go out compared to GMT.

You will notice that there are fields to allow for 5 metadata key value pairs to be added to the campaign. These key value pairs will be added to the campaign which in turn are added to the web hook events generated by SparkPost. I am guessing that 5 is enough, if not please give me feedback and/or make the changes yourself and submit them to the Github repository.

If you are using ip_pools or bindings, you can use meta data to guide your emails to the proper location. For example, Enterprise users may have a meta data field called "binding" and the data would be "outbound".

Once you submit the data, the campaign will be scheduled and you will be taken to the Receipts page.

Campaign Generator Login Page

Using CSV or JSON data for Campaign Generation

The second approach to submitting a campaign is very similar to the previous one (please refer to Using Stored Templates and Recipient Lists for Campaign Generation) except that the recipient data is entered by you. While this is a lot more flexible, it can be more challenging to get the data correct. There are three large data entry fields; CSV and Recipients and Global Substitution Data fields. The Global field is initially hidden and can be displayed by pressing the 'Show Global Sub' button. The text on that button will toggle from 'show' or 'hide' depending on if the text field is being displayed or not.

The CSV field expects a typical csv list of data. The only field that MUST be in the list is the "address" field; all others are optional. Once entered, press the 'Convert to JSON' button in order to change your CSV file into something the server can understand. The first are the names of the fields, separated by commas and incased in quotes. If you have substitution data, you must have a field name "substitution" and all data following it on the row will be considered substitution_data fields. Here is an example:


"","Sam Smith","_","Sam",342,"Princeton"
"","Fred Frankly","_","Fred",38232,"Nowhere"
"","Zachary Zupers","_","Zack",9,"Hidden Town"

In each data row, use an underscore as the data for the "substitution" field . IT MUST BE AN UNDERSCORE!! The system will use the combination of the substitution/underscore to determine the beginning of the substitution data. The above sample will change to the following JSON format which will be displayed in the Recipient data entry field (if you have data in the Recipient data field prior to hitting the 'Convert to JSON' button, that data will be overridden):


{"recipients":[{"address":"","UserName":"Sam Smith","substitution_data":{"first":"Sam","id":"342","city":"Princeton"}}, {"address":"","UserName":"Fred Frankly","substitution_data":{"first":"Fred","id":"38232","city":"Nowhere"}}, {"address":"","UserName":"Zachary Zu

Yes that is ugly; so by using a json formatter (JSONLINT), you can see what the output looks like in human readable form. Notice that the dash does not show up in your data:

 "recipients": [{
  "address": "",
  "UserName": "Sam Smith",
  "substitution_data": {
   "first": "Sam",
   "id": "342",
   "city": "Princeton"
 }, {
  "address": "",
  "UserName": "Fred Frankly",
  "substitution_data": {
   "first": "Fred",
   "id": "38232",
   "city": "Nowhere"
 }, {
  "address": "",
  "UserName": "Zachary Zupers",
  "substitution_data": {
   "first": "Zack",
   "id": "9",
   "city": "Hidden Town"

NOTE: JSONLINT is a great tool. There is also a PRO version: PRo JSONLINT. JSON LINT is open source GitHub repository available. These tools can save you a ton of time in figuring out why your JSON is being rejected.

Once you have your CSV converted to JSON, you can go ahead and leverage the "Preview and Validate" process to see how the email may look and how well the data and the template match. For more information on the "Preview and Validate" process, please refer to the Validate Process Description given above. (I'm to lazy to upkeep both places :-).

If you wish, you can skip the CSV process and enter (cut/paste) JSON directly into the Recipient data field, go right ahead. There is a sample of what the structure needs to look like in the data field itself. The field is expected to be a good JSON format and can have as many users in it as you with up to 700K of data. Depending on the number of substitution fields you have that could be a lot of users!!

The Global data field works the same way as it does on the Campaign Generation Process that uses stored templates and recipient lists. Please refer to that text for further information.

The only other difference between this process and using stored information is that you may need to enter a 'global return path'. This is only needed for Enterprise/Elite users. The system will automatically get a list of validated domains that are available for your API Key. Enter in the name you want to use and the system will combine the two into a proper email address.

A subtle difference between this Campaign Generation process and the one that uses stored recipients is that we don't have a 'Send Test Email' button on this page. The reason for that is because you can enter in a set of users to get test emails by placing them in the Recipient data field. By taking this approach, you can send different data sets to different people so you can really see how your template is performing. Unlike the stored recipient approach that only uses the data from the first recipient, this will send to everyone in the recipient data field. So you if you want 10 people to get a test email; have 10 recipients along with their individual data and submit the campaign just like you would the real campaign.

Campaign Generator Login Page

Commit Page

Once you have pressed the submit button on the Campaign Generation page, the campaign is ready for one final review before submission. If everything looks good, hit the "Press to Send/Schedule Campaign' button at the bottom of the page. Once pressed, a box will appear that will reflect the output from the server. Check the text closely to make sure that your campaign was scheduled or sent. If everything went well, you will see output similar to:

{ "results": { "total_rejected_recipients": 0, "total_accepted_recipients": 1, "id": "102445114446875802" } }.

If not, you will see some server errors that might look like:

{ "errors": [ { "message": "Failed to generate message", "description": "[internal] Error while rendering part html: line 64: substitution value 'dynamic_html' did not exist or was null", "code": "1901" } ], "results": { "total_rejected_recipients": 0, "total_accepted_recipients": 1, "id": "66416320824814490" } }

As a precaution, the Schedule Campaign Button will change text and color to reflect that the campaign was submitted to the server and it either worked or didn't. So again, check the results closely. If you entered in an email address in the Generation page, an email should be sent out immediately.

Campaign Generator Login Page

Scheduled Campaigns

While the Generation page does show a list off all scheduled campaigns, you need to go to the Scheduled Campaigns page to see details and to cancel them. This page is a rather simple interface. Copy the 'id' of the campaign you want to cancel, paste it into the field and press 'cancel campaign'. This will cancel the campaign and refresh the page with a list of remaining campaigns.

**Warning** Campaigns that are targeted to kick off within 10 minutes can not be cancelled at this time due to SparkPost rules. There is nothing in this codebase that stops it, so if that rule changes some day, this application will work appropriately. If you try to cancel a campaign and it keeps showing up in the list, it is probably due to this rule!

How to Send Text Messages (Enterprise Accounts Only)

Sending SMS is actually very similar to sending an email as-long-as you have your Enterprise account configured to send messages to your aggregator(s). There are only two differences between sending an email and sending a text message. The first difference is that you must have a 'text' portion for your template. SMS will use the 'text' message, NOT the 'hmtl' message of your template; but rest assured you can still use substitution just like in an email. Second, the address will be in the form of <phone number>@<sparkpost aggregator id>. When an Enterprise system is configured with an aggregator, your Technical Account Manager will give you the aggregator id to use in the address. For example, if you request to have mGage set up as an aggregator in your account, your aggregator id might be "". This would mean, to send a message to (213) 555-1212 the recipient address would be! SparkPost will translate the to the appropriate aggregator for your account and send the message.

To further drill down, let's use an example use case where Sloppy Pig BBQ in Chicken, Alaska, wants to tell all of their customers of their new holiday late night hours. The stored template might have a nice fancy html layout, but in the 'text' section would simply have the following message, "Sloppy Pig BBQ will be open until 2am every Th-Sat to support your shopping hunger pains until Jan 8th". Now all you need is to change each recipient address from an email address to something like 12135551212@mgage.sloppypig. From there, SparkPost will do the rest.

The Campaign Generator will work with either stored recipients or via the CSV/JSON entry page, just make sure you point to the right template and use the right addresses.

As a reminder, SMS messages are only 160 characters, so if you message is longer, your aggregator will probably break each 160 characters into an individual message (page) and charge you for each page, so don't go to wild.