Sends a personalised email to each row of a Google Sheet, records every send, and never emails the same person twice.
Uses
Gmail
Google Sheets
Mail merge a personalised email to every row of a spreadsheet
sheetUrl to blank.Set sheetUrl to blank.sheetTabName to blank.Set sheetTabName to blank.emailSubject to blank.Set emailSubject to blank.emailBody to blank.Set emailBody to blank.sentLogTabName to Sent log.Set sentLogTabName to Sent log.maxEmailsThisRun to 1.Set maxEmailsThisRun to 1.usesFirstName to emailSubject contains {{First Name}} or emailBody contains {{First Name}}.Set usesFirstName to emailSubject contains {{First Name}} or emailBody contains {{First Name}}.usesCompany to emailSubject contains {{Company}} or emailBody contains {{Company}}.Set usesCompany to emailSubject contains {{Company}} or emailBody contains {{Company}}.sentLogIsNew to true.Set sentLogIsNew to true.createLogTabErrorIf a step below fails, save the error as createLogTabErrorTry
sentLogTabName in sheetUrl.Create the tab sentLogTabName in sheetUrl.On error
sentLogIsNew to false.Change sentLogIsNew to false.sentLogIsNew is yesIf sentLogIsNew is yesYes
sentLogTabName in sheetUrl.Add a row to sentLogTabName in sheetUrl.No
No steps
sentLogTabName in sheetUrl. Save it as sentLog.Read column 'A' of sentLogTabName in sheetUrl. Save it as sentLog.alreadySent to an empty list.Set alreadySent to an empty list.loggedAddress in sentLog.values:For each loggedAddress in sentLog.values:loggedAddressErrorIf a step below fails, save the error as loggedAddressErrorTry
loggedAddress in lowercase to the end of alreadySent and save it as alreadySent.Add loggedAddress in lowercase to the end of alreadySent and save it as alreadySent.On error
sentThisRun to 0.Set sentThisRun to 0.skippedRows to 0.Set skippedRows to 0.alreadySentRows to 0.Set alreadySentRows to 0.sheetUrl. Save it as recipientSheet.Read the rows of sheetUrl. Save it as recipientSheet.row in recipientSheet.rows:For each row in recipientSheet.rows:sentThisRun is greater than or equal to maxEmailsThisRunIf sentThisRun is greater than or equal to maxEmailsThisRunYes
No
No steps
rowLabel to row row.row_number as text.Set rowLabel to row row.row_number as text.recipient to blank.Set recipient to blank.recipientErrorIf a step below fails, save the error as recipientErrorTry
row.Email lowercase and save it as recipient.Make row.Email lowercase and save it as recipient.On error
recipient to blank.Change recipient to blank.recipient is empty or recipient doesn't contain @If recipient is empty or recipient doesn't contain @Yes
rowLabel: the Email column is empty or does not hold an email address.Show in the output: Skipped rowLabel: the Email column is empty or does not hold an email address.skippedRows.Add 1 to skippedRows.No
No steps
alreadySent contains recipientIf alreadySent contains recipientYes
rowLabel: recipient is already listed in the '@sentLogTabName' tab.Show in the output: Skipped rowLabel: recipient is already listed in the '@sentLogTabName' tab.alreadySentRows.Add 1 to alreadySentRows.No
No steps
firstName to blank.Set firstName to blank.firstNameErrorIf a step below fails, save the error as firstNameErrorTry
row["First Name"] and save it as firstName.Trim row["First Name"] and save it as firstName.On error
firstName to blank.Change firstName to blank.company to blank.Set company to blank.companyErrorIf a step below fails, save the error as companyErrorTry
row.Company and save it as company.Trim row.Company and save it as company.On error
company to blank.Change company to blank.usesFirstName is yes and firstName is emptyIf usesFirstName is yes and firstName is emptyYes
rowLabel: your message uses {{First Name}} but that cell is empty.Show in the output: Skipped rowLabel: your message uses {{First Name}} but that cell is empty.skippedRows.Add 1 to skippedRows.No
No steps
usesCompany is yes and company is emptyIf usesCompany is yes and company is emptyYes
rowLabel: your message uses {{Company}} but that cell is empty.Show in the output: Skipped rowLabel: your message uses {{Company}} but that cell is empty.skippedRows.Add 1 to skippedRows.No
No steps
{{Email}} with recipient in emailSubject with {{First Name}} replaced by firstName with {{Company}} replaced by company and save it as mergedSubject.Replace {{Email}} with recipient in emailSubject with {{First Name}} replaced by firstName with {{Company}} replaced by company and save it as mergedSubject.{{Email}} with recipient in emailBody with {{First Name}} replaced by firstName with {{Company}} replaced by company and save it as mergedBody.Replace {{Email}} with recipient in emailBody with {{First Name}} replaced by firstName with {{Company}} replaced by company and save it as mergedBody.emailSent to true.Set emailSent to true.sendErrorIf a step below fails, save the error as sendErrorTry
recipient.Send an email to recipient.
Subject: mergedSubjectmergedBodyOn error
emailSent to false.Change emailSent to false.recipient: sendError as text.Show in the output: Could not send to recipient: sendError as text.emailSent is noIf emailSent is noYes
skippedRows.Add 1 to skippedRows.No
No steps
logWriteErrorIf a step below fails, save the error as logWriteErrorTry
sentLogTabName in sheetUrl.Add a row to sentLogTabName in sheetUrl.On error
recipient but could not add it to the '@sentLogTabName' tab. The run stopped here so that nobody is emailed twice next time - fix the spreadsheet, then run it again.Stop and report a problem: Sent the email to recipient but could not add it to the '@sentLogTabName' tab. The run stopped here so that nobody is emailed twice next time - fix the spreadsheet, then run it again.recipient to the end of alreadySent and save it as alreadySent.Add recipient to the end of alreadySent and save it as alreadySent.sentThisRun.Add 1 to sentThisRun.sentThisRun as text email(s) this run. alreadySentRows as text row(s) had already been emailed and skippedRows as text row(s) were skipped. Raise 'maxEmailsThisRun' and run it again to carry on down the list.Show in the output: Sent sentThisRun as text email(s) this run. alreadySentRows as text row(s) had already been emailed and skippedRows as text row(s) were skipped. Raise 'maxEmailsThisRun' and run it again to carry on down the list.