Every day, emails the next few leads in your sheet a random article with a short AI note, and comes back to each after a set gap.
Uses
Claude
Gmail
Google Sheets
Drip helpful articles to your leads
Every morning, the next few leads in your spreadsheet each get an email sharing one article from your list, chosen at random from the ones they have not had yet. The AI writes a short note on why it might be worth their time, from the facts on the lead's row and the article's row and nothing else, and it goes out from your Gmail with the article's title as the subject and your own sign-off and opt-out line added. Each follow-up carries something useful instead of a 'just checking in'.
The list is worked through a few leads a day. Whoever has never had an article goes first, in sheet order, then whoever was emailed longest ago, and nobody hears from you again until the gap you set has passed. Every send is written to a record tab before the email leaves and read back at the start of each run, so a rerun cannot repeat a send and nobody gets the same article twice.
Before the first run you need a leads tab with Email and Name columns, an articles tab with Title and URL columns (a Summary column helps the note), 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.senderContext to blank.Set senderContext 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..leadsPerDay to 5.Set leadsPerDay to 5.daysBetweenEmails to 7.Set daysBetweenEmails to 7.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, now_text, leads_per_day, and days_between and save it as plan.Run Python code on leads, articles, sent, now_text, leads_per_day, and days_between 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.failedCount to 0.Set failedCount to 0.pick in plan.picks:For each pick in plan.picks:leadEmail 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 writeErrorsenderContext
The lead:
pick.facts
The article to share:
pick.article_facts
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: one line on why this seemed worth sending them, 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 that leaves the door open, 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.sentCount is greater than 0If sentCount is greater than 0secondsBetweenEmails seconds.Wait for secondsBetweenEmails seconds.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.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.failedCount.Add 1 to failedCount.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), failedCount as text failed to send, skipped skippedCount as text, out of the length of plan.picks as text lead(s) in today's batch.Show in the output: Sent sentCount as text article email(s), failedCount as text failed to send, skipped skippedCount as text, out of the length of plan.picks as text lead(s) in today's batch.