Each day, picks an article for every lead due one, by their interests and stage, writes a personal note around it and emails it.
Uses
Claude
Gmail
Google Sheets
Nurture leads with a personalised article
Every morning, your leads tab and your articles tab are read from one spreadsheet. For each lead who is due a touch, one article is chosen for them: one whose topics overlap their interests and whose stage matches theirs is preferred, and the pick within that tier is random so similar leads do not all get the same piece. The AI then writes a short note explaining why it is worth their time, from the facts on those two rows and nothing else, and it goes out from your Gmail with the article title as the subject and your own sign-off and opt-out line added.
Nobody hears from you too often and nobody gets the same article twice. Every send is written to a record tab before the email leaves, and that tab is read back at the start of each run, so a rerun cannot repeat a send and a lead is left alone until the gap you set has passed. A lead without a valid address or a name is reported and skipped, and the leads and articles tabs are never written to.
Before the first run you need a leads tab with Email, Name, Interests and Stage columns, an articles tab with Title, URL, Summary, Topics and Stage, an empty record tab headed Email, Article, URL and Sent at, and a short description of who the emails come from.
spreadsheetUrl to blank.Set spreadsheetUrl to blank.leadsTabName to blank.Set leadsTabName to blank.articlesTabName to blank.Set articlesTabName to blank.sentTabName to blank.Set sentTabName to blank.companyContext to blank.Set companyContext to blank.signOff to blank.Set signOff to blank.optOutLine to If you would rather not get these, just reply and say so..Set optOutLine to If you would rather not get these, just reply and say so..daysBetweenEmails to 7.Set daysBetweenEmails to 7.maxEmailsPerRun to 20.Set maxEmailsPerRun to 20.secondsBetweenEmails to 5.Set secondsBetweenEmails to 5.spreadsheetUrl. Save it as leadsRead.Read the rows of spreadsheetUrl. Save it as leadsRead.spreadsheetUrl. Save it as articlesRead.Read the rows of spreadsheetUrl. Save it as articlesRead.spreadsheetUrl. Save it as sentRead.Read the rows of spreadsheetUrl. Save it as sentRead.articlesRead.count is 0If articlesRead.count is 0leadsRead.count is 0If leadsRead.count is 0leads, articles, sent, days_between, and now_text and save it as plan.Run Python code on leads, articles, sent, days_between, and now_text and save it as plan.note in plan.notes:For each note in plan.notes:note.Show in the output: note.plan.articles is 0If plan.articles is 0plan.picks is emptyIf plan.picks is emptysentCount to 0.Set sentCount to 0.skippedCount to 0.Set skippedCount to 0.pick in plan.picks:For each pick in plan.picks:sentCount is greater than or equal to maxEmailsPerRunIf sentCount is greater than or equal to maxEmailsPerRunleadEmail to pick.email.Set leadEmail to pick.email.articleTitle to pick.title.Set articleTitle to pick.title.articleUrl to pick.url.Set articleUrl to pick.url.emailBody to blank.Set emailBody to blank.writeErrorIf a step below fails, save the error as writeErrorcompanyContext
The lead:
Name: pick.name
What they are interested in: pick.interests
Where they are in the funnel: pick.stage
The article to share:
Title: pick.title
Link: pick.url
Summary: pick.summary
It was chosen because it pick.why.
Write the email body only, as plain text with no subject line, no markdown and no sign-off (the sender's own sign-off is added afterwards). Start with 'Hi ' followed by the lead's first name and a comma. Then two or three short paragraphs: why this article is worth their time given what they care about and where they are, what it covers in a sentence or two drawn only from the summary, and the link on its own line exactly as written above. Close with one light sentence offering to talk it through if useful, with no pressure. Keep the whole thing under 150 words.'. Save it as written.
Maximum response length: 600written.response_text and save it as emailBody.Trim written.response_text and save it as emailBody.leadEmail: writeError as text.Show in the output: Could not write the note for leadEmail: writeError as text.emailBody is emptyIf emailBody is emptyleadEmail, so they are left for the next run.Show in the output: No note came back for leadEmail, so they are left for the next run.skippedCount.Add 1 to skippedCount.emailBody doesn't contain articleUrlIf emailBody doesn't contain articleUrlemailBody to emailBodyChange emailBody to emailBody
articleUrl.emailBody to emailBodyChange emailBody to emailBody
signOff
optOutLine.recorded to false.Set recorded to false.recordErrorIf a step below fails, save the error as recordErrorsentTabName in spreadsheetUrl.Add a row to sentTabName in spreadsheetUrl.recorded to true.Change recorded to true.leadEmail on the '@sentTabName' tab, so nothing was sent to them: recordError as text.Show in the output: Could not record the send for leadEmail on the '@sentTabName' tab, so nothing was sent to them: recordError as text.recorded is noIf recorded is noskippedCount.Add 1 to skippedCount.sentCount is greater than 0If sentCount is greater than 0secondsBetweenEmails seconds.Wait for secondsBetweenEmails seconds.sendErrorIf a step below fails, save the error as sendErrorleadEmail.Send an email to leadEmail.
Subject: articleTitleemailBodysentCount.Add 1 to sentCount.leadEmail.Show in the output: Sent '@articleTitle' to leadEmail.leadEmail: sendError as text - delete that row from the '@sentTabName' tab to try again next run.Show in the output: Recorded but could not send '@articleTitle' to leadEmail: sendError as text - delete that row from the '@sentTabName' tab to try again next run.sentCount as text article email(s), skipped skippedCount as text, out of the length of plan.picks as text lead(s) due today.Show in the output: Sent sentCount as text article email(s), skipped skippedCount as text, out of the length of plan.picks as text lead(s) due today.