So this has consumed me a bit lately. Because SendGrid has no javascript, if you want an HTML table you have to send it to them. Here’s how I did it.

The table body and table head can be built in Sendgrid but the rows and data in the rows are dynamic - shopping cart items for different shopping carts.

I already have a Workflow on a List that writes the individual items in the cart into a table called, funny enough, cartItems. And I write about 10 elements for each item into the database.

Each record is a row. Just like the rows of the HTML table.

So I added text column and in the column I added the HTML for a row and columns and then used the dynamic content from the list to populate the table row. I even styled the row and cells a bit (no quotes allowed, no &quot’ but single quotes are read)…

The last step in the workflow for each iteration is to update a cart record with the HTML element I just created. Since the workflow is on a list, each time it looped through, I appended the new record to the old and saved it. At then, the table details were in one text column on the cart table.

I then select that in a workflow using the API Connector and SendGrid and template I created, I sent the JSON body to SendGrid and mapped elements into the email and then one large element into the table body.

You can see the text I used to create the HTML table data, the JSON load, and the final email to the customer.

Kind of cool.


![Screen Shot 2025-01-17 at 4.18.14 PM|501x500]
(upload://khX8yAryQPEXEMgygciHIP04dwL.png)

{
  "from": {
    "email": "deals@iwantthat.io",
    "name": "I Want That!"
  },
  "personalizations": [
    {
      "to": [
        {
          "email": "<consumerEmail>"
        }
      ],
              "bcc": [
        {
          "email": "deals@iwantthat.io"
        }
      ],
      "dynamic_template_data": {
        "consumer": {
          "consumerName": "<consumerName>",
              
          "consumerFirstName": "<firstName>",
              
          "consumerEmail": "<consumerEmail>",
              
          "consumerMobile": "<consumerMobile>",
              
          "consumerPostalCode": "<consumerPostalCode>"
        },
        "offer": {
          "offerCreateDate": "<offerCreateDate>",
              
          "offerPrice": "<offerPrice>",
              
          "offerDiscountPrice": "<offerDiscountPrice>",
              
          "offerStatus": "<offerStatus>"
              
        },
        "response" : {
              
          "offerAcceptedDate" : "<offerAcceptedDate>",
          
          "offerAcceptedPrice" : "<offerAcceptedPrice>",
          
          "offerAcceptedDiscount" : "<offerAcceptedDiscount>",
              
          "offerAcceptedUnits" : "<offerAcceptedUnits>",
           
          "offerAcceptedItems" : "<offerAcceptedItems>",
          
          "offerExpiryMinutes": "<offerExpiryMinutes>",
              
          "offerExpiryTime": "<offferExpiryTime>"            
              
        },
              
        "cart": {
          "cartTotalPrice": "<cartTotalPrice>",
              
          "checkoutURL": "<checkoutURL>",
              
          "cartItemsCount": "<cartItemsCount>",
              
          "cartUnitsSum": "<cartUnitsSum>"
              
        },
       "store": {
          "storeBrand": "<storeBrand>"
        },
           "cartItems" :{
              "cartProductsList" : ["<cartProductsList>"]
        }
      }
    }
  ],
        "template_id": "<sgTemplateId>"
}

for simple looping you can also use search for list formated as text

this way it doesn’t have to loop in a workflow it just prints the text for each record (you can add data from each record into the format as text)

format as text is fine for simple data (like your table) but you can’t go too nested with it. and it can be a bit of a pain for complex setups.

another approach I’ve used is to offload the html creation to something like make.com where it is much easier to build and maintain the html (just send the data over, then send the html back - set it as an api data call)

1 Like

I tried the format as text on a list and it did not work. I verified the list in a different flow and could not figure that out. BUT because I was writing the products as items to a table anyway, just adding the HTML (using format as text) and then appending them during the loop was a cool little hack.

The only part of the html I sent to sendgrid was really the table data in rows. The header and body are all a container waiting for the data. I’ll check out make - good stuff!

yeah the format as text hack is quite tricky to get right - it’s hard to break the code and only iterate a chunk of it

for tables you’d have to iterate just the row part of the table and then have the table html outside the format as text… can get tricky to remember where to break and connect it

1 Like