Drafts an introduction email to each new HubSpot contact, written from what your CRM already knows about them.
Uses
Claude
Gmail
HubSpot
Introduce yourself to new CRM contacts
Once a day, the contacts added to HubSpot in the last three days are read, oldest first, and each gets a short first introduction written by AI from what your CRM holds: their name, job title, company, website, lifecycle stage and how they first found you. It uses at most one of those details, so the email reads as interest rather than surveillance.
Each introduction is left in your Gmail drafts for you to read, change and send, unless you have switched sending on. The AI is told never to invent a meeting, a referral or a shared contact. Nobody is introduced to twice: your whole mailbox is searched for that address first, and anyone you have written to or heard from is skipped. Up to 10 introductions are written a day and the run says how many contacts are still waiting. A contact with no valid email address is skipped, an introduction that cannot be written or saved marks the run as failed and is tried again the next day, and HubSpot itself is only ever read.
You need HubSpot, Gmail and Anthropic connected, a few sentences on who you are and what you do, and a name to sign off with.
aboutYou to blank.Set aboutYou to blank.senderName to blank.Set senderName to blank.lookBackDays to 3.Set lookBackDays to 3.maxContacts to 10.Set maxContacts to 10.sendAutomatically to blank.Set sendAutomatically to blank.lookBackDays days and save it as windowStart.Move the current date and time by 0 minus lookBackDays days and save it as windowStart.windowStart. Save it as newContacts.Find contacts created since windowStart. Save it as newContacts.contacts to newContacts.results.Set contacts to newContacts.results.contacts is emptyIf contacts is emptycontacts and save it as oldestFirst.Reverse the order of contacts and save it as oldestFirst.waitingCount to 0.Set waitingCount to 0.introductionProblems to an empty list.Set introductionProblems to an empty list.introducedThisRun.Start an empty list and save it as introducedThisRun.contact in oldestFirst:For each contact in oldestFirst:crm to contact.properties.Set crm to contact.properties.rawEmail to crm.email.Set rawEmail to crm.email.rawEmail is emptyIf rawEmail is emptySkipped a new HubSpot contact: the record has no email address on it..Show in the output: Skipped a new HubSpot contact: the record has no email address on it..rawEmail lowercase and save it as emailAddress.Make rawEmail lowercase and save it as emailAddress.emailAddress is a valid email addressIf not emailAddress is a valid email addressintroducedThisRun contains emailAddressIf introducedThisRun contains emailAddressemailAddress: that address appears more than once in this batch of new contacts and has already been handled.Show in the output: Skipped emailAddress: that address appears more than once in this batch of new contacts and has already been handled.emailAddress OR to:emailAddress'. Save it as previousMail.Find email matching 'from:emailAddress OR to:emailAddress'. Save it as previousMail.previousMail.email isn't emptyIf previousMail.email isn't emptyemailAddress: your mailbox already holds a message to or from this address, so they are not somebody to introduce yourself to.Show in the output: Skipped emailAddress: your mailbox already holds a message to or from this address, so they are not somebody to introduce yourself to.introducedThisRun is greater than or equal to maxContactsIf the length of introducedThisRun is greater than or equal to maxContactswaitingCount.Add 1 to waitingCount.emailAddress to the end of introducedThisRun and save it as introducedThisRun.Add emailAddress to the end of introducedThisRun and save it as introducedThisRun.emailAddress to the end of a list of [] and save it as crmFacts.Add Email address: emailAddress to the end of a list of [] and save it as crmFacts.crm.firstname isn't empty and crm.firstname isn't blankIf crm.firstname isn't empty and crm.firstname isn't blankcrm.firstname to the end of crmFacts and save it as crmFacts.Add First name: crm.firstname to the end of crmFacts and save it as crmFacts.crm.lastname isn't empty and crm.lastname isn't blankIf crm.lastname isn't empty and crm.lastname isn't blankcrm.lastname to the end of crmFacts and save it as crmFacts.Add Last name: crm.lastname to the end of crmFacts and save it as crmFacts.crm.jobtitle isn't empty and crm.jobtitle isn't blankIf crm.jobtitle isn't empty and crm.jobtitle isn't blankcrm.jobtitle to the end of crmFacts and save it as crmFacts.Add Job title: crm.jobtitle to the end of crmFacts and save it as crmFacts.crm.company isn't empty and crm.company isn't blankIf crm.company isn't empty and crm.company isn't blankcrm.company to the end of crmFacts and save it as crmFacts.Add Company: crm.company to the end of crmFacts and save it as crmFacts.crm.website isn't empty and crm.website isn't blankIf crm.website isn't empty and crm.website isn't blankcrm.website to the end of crmFacts and save it as crmFacts.Add Website: crm.website to the end of crmFacts and save it as crmFacts.crm.lifecyclestage isn't empty and crm.lifecyclestage isn't blankIf crm.lifecyclestage isn't empty and crm.lifecyclestage isn't blankcrm.lifecyclestage to the end of crmFacts and save it as crmFacts.Add Lifecycle stage in our CRM: crm.lifecyclestage to the end of crmFacts and save it as crmFacts.crm.hs_analytics_source isn't empty and crm.hs_analytics_source isn't blankIf crm.hs_analytics_source isn't empty and crm.hs_analytics_source isn't blankcrm.hs_analytics_source to the end of crmFacts and save it as crmFacts.Add How they first found us: crm.hs_analytics_source to the end of crmFacts and save it as crmFacts.crmFacts with a new line and save it as crmSummary.Join crmFacts with a new line and save it as crmSummary.introductionErrorIf a step below fails, save the error as introductionErroraboutYou
Everything our CRM records about this contact:
crmSummary
Write one subject line of at most nine words. Use only the facts above - never imply a meeting, a past conversation, a referral or a shared contact that is not stated there. Reply with the subject line and nothing else: no quotation marks, no explanation.'. Save it as subjectResponse.
Maximum response length: 60 with " in subjectResponse.response_text and save it as subject.Replace with " in subjectResponse.response_text and save it as subject.aboutYou
Everything our CRM records about this contact:
crmSummary
Write the body of the email:
- Open with their first name if the CRM gives one, and with 'Hello,' if it does not.
- Say who you are and what you do, in the sender's own terms.
- Draw on what the CRM already knows about them - their role, their company, or how they first came to us - where it is genuinely relevant. Use at most one such detail; reciting everything we hold on somebody reads as surveillance rather than interest.
- Never invent a meeting, a referral, a shared contact or anything else the facts above do not state. If the CRM knows almost nothing about them, write a short honest introduction rather than guessing.
- Close with one low-pressure question or offer.
- Keep it under 150 words and sign off as senderName.
Reply with the body of the email and nothing else - no subject line, no explanation.'. Save it as bodyResponse.
Maximum response length: 700bodyResponse.response_text and save it as introduction.Trim bodyResponse.response_text and save it as introduction.subject is blank or introduction is emptyIf subject is blank or introduction is emptyemailAddress: the AI's introduction came back empty, so nothing was drafted to the end of introductionProblems and save it as introductionProblems.Add emailAddress: the AI's introduction came back empty, so nothing was drafted to the end of introductionProblems and save it as introductionProblems.sendAutomatically in lowercase is yesIf sendAutomatically in lowercase is yesemailAddress.Send an email to emailAddress.
Subject: subjectintroductionemailAddress.Show in the output: Sent an introduction to emailAddress.emailAddress.Draft an email to emailAddress.
Subject: subjectintroductionemailAddress in Gmail for you to read and send.Show in the output: Left a draft introduction to emailAddress in Gmail for you to read and send.emailAddress: introductionError.message to the end of introductionProblems and save it as introductionProblems.Add emailAddress: introductionError.message to the end of introductionProblems and save it as introductionProblems.introducedThisRun and save it as preparedCount.Count the items in introducedThisRun and save it as preparedCount.introductionProblems isn't emptyIf introductionProblems isn't emptyintroductionProblems with a new line and save it as problemList.Join introductionProblems with a new line and save it as problemList.problemList.waitingCount is greater than 0If waitingCount is greater than 0preparedCount. New contacts still waiting: waitingCount - they are picked up on the next run; raise 'maxContacts' to handle more in a day.Finish the run: Introductions prepared: preparedCount. New contacts still waiting: waitingCount - they are picked up on the next run; raise 'maxContacts' to handle more in a day.preparedCount.Finish the run: Introductions prepared: preparedCount.