Checks each new enquiry against who you sell to, sends good ones to the right rep in Slack with a reply drafted, and parks the rest.
Uses
Claude
Apollo
Gmail
Google Sheets
HubSpot
Slack
ZeroBounce
Route every new sales enquiry to the right rep
Every two minutes, new rows on the 'New leads' tab of your spreadsheet are picked up. Throwaway and undeliverable addresses are dropped, the company is matched against accounts you already own, and the AI reads what the person wrote to judge how well they fit who you sell to and how ready they are to talk. A good lead goes to the rep who owns the account, or to the next available rep in turn, is added to HubSpot and is posted in Slack mentioning that rep, with a reply drafted. Leads in no hurry are posted for nurture, partners and existing customers for someone to pick up, and pitches, job seekers, students and spam are recorded and left out.
When the AI is unsure what an enquiry is, it still goes to a person, flagged to be read first, though a throwaway or undeliverable address is always dropped. Every lead is written to the 'Ledger' tab, which is how none is handled twice; up to 10 are handled per run and the run says how many are still waiting. If Slack, the AI, HubSpot, a Gmail draft or the Ledger fails, the run is marked failed and names the leads.
You need Google Sheets, Slack, Anthropic and HubSpot connected, plus ZeroBounce and Apollo if you want addresses checked and companies looked up, and Gmail if replies should also be saved as drafts. You also need a spreadsheet with 'New leads', 'Accounts', 'Reps' and 'Ledger' tabs, and five settings filled in: who you sell to, who you do not, the spreadsheet link, the Slack channel and your booking link.
whoYouSellTo to blank.Set whoYouSellTo to blank.whoYouDoNotSellTo to blank.Set whoYouDoNotSellTo to blank.spreadsheetUrl to blank.Set spreadsheetUrl to blank.slackChannelId to blank.Set slackChannelId to blank.bookingLink to blank.Set bookingLink to blank.formNamesThatMeanTalkToSalesNow to Request a demo,Contact sales,Pricing enquiry.Set formNamesThatMeanTalkToSalesNow to Request a demo,Contact sales,Pricing enquiry.replyMode to slack.Set replyMode to slack.replyWithinMinutes to 5.Set replyWithinMinutes to 5.loadedNewLeads to {status:,headers:[],rows:[],count:0}.Set loadedNewLeads to {status:,headers:[],rows:[],count:0}.loadedAccounts to {status:,headers:[],rows:[],count:0}.Set loadedAccounts to {status:,headers:[],rows:[],count:0}.loadedReps to {status:,headers:[],rows:[],count:0}.Set loadedReps to {status:,headers:[],rows:[],count:0}.loadedLedger to {status:,headers:[],rows:[],count:0}.Set loadedLedger to {status:,headers:[],rows:[],count:0}.sheetErrorIf a step below fails, save the error as sheetErrorspreadsheetUrl. Save it as loadedNewLeads.Read the rows of spreadsheetUrl. Save it as loadedNewLeads.spreadsheetUrl. Save it as loadedAccounts.Read the rows of spreadsheetUrl. Save it as loadedAccounts.spreadsheetUrl. Save it as loadedReps.Read the rows of spreadsheetUrl. Save it as loadedReps.spreadsheetUrl. Save it as loadedLedger.Read the rows of spreadsheetUrl. Save it as loadedLedger.sheetError.message.Stop and report a problem: Could not read the lead spreadsheet. It needs four tabs named exactly 'New leads', 'Accounts', 'Reps' and 'Ledger', and the Google Sheets connection must have access to it. Run the 'Inbound lead workspace setup' template to build one correctly. The underlying error was: sheetError.message.freeMailDomains to gmail.com,googlemail.com,yahoo.com,hotmail.com,outlook.com,live.com,aol.com,icloud.com,me.com,proton.me,protonmail.com,gmx.com,mail.com,yandex.com,zoho.com,hey.com.Set freeMailDomains to gmail.com,googlemail.com,yahoo.com,hotmail.com,outlook.com,live.com,aol.com,icloud.com,me.com,proton.me,protonmail.com,gmx.com,mail.com,yandex.com,zoho.com,hey.com.throwawayDomains to mailinator.com,10minutemail.com,guerrillamail.com,tempmail.com,trashmail.com,yopmail.com,sharklasers.com,dispostable.com,maildrop.cc.Set throwawayDomains to mailinator.com,10minutemail.com,guerrillamail.com,tempmail.com,trashmail.com,yopmail.com,sharklasers.com,dispostable.com,maildrop.cc.roleAddressPrefixes to info,sales,support,admin,contact,hello,help,noreply,no-reply,billing,office,team,marketing,careers,jobs,hr,press,legal,webmaster,postmaster.Set roleAddressPrefixes to info,sales,support,admin,contact,hello,help,noreply,no-reply,billing,office,team,marketing,careers,jobs,hr,press,legal,webmaster,postmaster.loadedNewLeads.rows isn't empty and loadedNewLeads.headers doesn't containโฆIf loadedNewLeads.rows isn't empty and loadedNewLeads.headers doesn't containโฆloadedLedger.headers doesn't contain Submission ID or loadedLedger.headersโฆIf loadedLedger.headers doesn't contain Submission ID or loadedLedger.headersโฆloadedNewLeads.rows where item["Submission ID"] isn't blank and item.Email contains @ and save it as leadsWithAnAddress.Keep each item in loadedNewLeads.rows where item["Submission ID"] isn't blank and item.Email contains @ and save it as leadsWithAnAddress.leadsWithAnAddress with loadedLedger.rows, matching by Submission ID and save it as comparedWithLedger.Compare leadsWithAnAddress with loadedLedger.rows, matching by Submission ID and save it as comparedWithLedger.Submission ID from comparedWithLedger.only_in_first and save it as newLeads.Remove duplicates of Submission ID from comparedWithLedger.only_in_first and save it as newLeads.newLeads and save it as leadsThisRun.Take the first 10 items of newLeads and save it as leadsThisRun.newLeads from item 11 on and save it as leadsForNextRun.Take newLeads from item 11 on and save it as leadsForNextRun.loadedLedger.rows where item["Routing Rule"] in lowercase is round robin and save it as earlierRoundRobinLeads.Keep each item in loadedLedger.rows where item["Routing Rule"] in lowercase is round robin and save it as earlierRoundRobinLeads.earlierRoundRobinLeads and save it as roundRobinTurn.Count the items in earlierRoundRobinLeads and save it as roundRobinTurn.leadProblems to an empty list.Set leadProblems to an empty list.routedThisRun to 0.Set routedThisRun to 0.droppedThisRun to 0.Set droppedThisRun to 0.lead in leadsThisRun:For each lead in leadsThisRun:leadErrorIf a step below fails, save the error as leadErrorlead["Submission ID"] and save it as submissionId.Trim lead["Submission ID"] and save it as submissionId.submissionId is emptyIf submissionId is emptylead.Email lowercase and save it as leadEmail.Make lead.Email lowercase and save it as leadEmail.leadEmail doesn't contain @If leadEmail doesn't contain @submissionId: no usable email address.Show in the output: Skipped submission submissionId: no usable email address.leadEmail split by @ and save it as emailLocalPart.Get the first item of leadEmail split by @ and save it as emailLocalPart.leadEmail split by @ and save it as emailDomain.Get the last item of leadEmail split by @ and save it as emailDomain.lead.Company and save it as statedCompany.Trim lead.Company and save it as statedCompany.lead.Name and save it as leadName.Trim lead.Name and save it as leadName.lead.Message and save it as leadMessage.Trim lead.Message and save it as leadMessage.lead.Form and save it as formName.Trim lead.Form and save it as formName.statedCompany with , replaced by with . replaced by with Inc replaced by with Ltd replaced by with LLC replaced by lowercase and save it as companyKey.Make statedCompany with , replaced by with . replaced by with Inc replaced by with Ltd replaced by with LLC replaced by lowercase and save it as companyKey.freeMailDomains, contains ,emailDomain, and save it as isFreeMailDomain.Check whether ,freeMailDomains, contains ,emailDomain, and save it as isFreeMailDomain.throwawayDomains, contains ,emailDomain, and save it as isThrowawayDomain.Check whether ,throwawayDomains, contains ,emailDomain, and save it as isThrowawayDomain.roleAddressPrefixes, contains ,emailLocalPart, and save it as isRoleAddress.Check whether ,roleAddressPrefixes, contains ,emailLocalPart, and save it as isRoleAddress.emailStatus to unchecked.Set emailStatus to unchecked.emailTypoSuggestion to blank.Set emailTypoSuggestion to blank.verifyErrorIf a step below fails, save the error as verifyErrorleadEmail. Save it as emailCheck.Verify the email address leadEmail. Save it as emailCheck.emailCheck.status and save it as emailStatus.Trim emailCheck.status and save it as emailStatus.emailCheck.did_you_mean and save it as emailTypoSuggestion.Trim emailCheck.did_you_mean and save it as emailTypoSuggestion.leadEmail - continuing without a deliverability check. verifyError.message.Show in the output: Could not verify leadEmail - continuing without a deliverability check. verifyError.message.loadedAccounts.rows where isFreeMailDomain is no and item.Domain in lowercase is emailDomain and save it as accountMatches.Keep each item in loadedAccounts.rows where isFreeMailDomain is no and item.Domain in lowercase is emailDomain and save it as accountMatches.accountMatches is empty and companyKey isn't emptyIf accountMatches is empty and companyKey isn't emptyloadedAccounts.rows where item["Account Name"] in lowercase is companyKey and save it as accountMatches.Keep each item in loadedAccounts.rows where item["Account Name"] in lowercase is companyKey and save it as accountMatches.accountMatches and save it as matchedAccount.Get the first item of accountMatches and save it as matchedAccount.hasAccountMatch to the length of accountMatches is greater than 0.Set hasAccountMatch to the length of accountMatches is greater than 0.accountOwnerEmail to blank.Set accountOwnerEmail to blank.accountOwnerSlackId to blank.Set accountOwnerSlackId to blank.accountIsCustomer to false.Set accountIsCustomer to false.accountHasOpenDeal to false.Set accountHasOpenDeal to false.hasAccountMatch is yesIf hasAccountMatch is yesmatchedAccount["Owner Email"] lowercase and save it as accountOwnerEmail.Make matchedAccount["Owner Email"] lowercase and save it as accountOwnerEmail.matchedAccount["Owner Slack ID"] and save it as accountOwnerSlackId.Trim matchedAccount["Owner Slack ID"] and save it as accountOwnerSlackId.accountIsCustomer to matchedAccount["Is Customer"] in lowercase is yes.Change accountIsCustomer to matchedAccount["Is Customer"] in lowercase is yes.accountHasOpenDeal to matchedAccount["Open Opportunity"] in lowercase is yes.Change accountHasOpenDeal to matchedAccount["Open Opportunity"] in lowercase is yes.loadedLedger.rows where isFreeMailDomain is no and item.Domain in lowercase is emailDomain andโฆ and save it as sameDomainRecently.Keep each item in loadedLedger.rows where isFreeMailDomain is no and item.Domain in lowercase is emailDomain andโฆ and save it as sameDomainRecently.isBuyingGroup to the length of sameDomainRecently is greater than 0.Set isBuyingGroup to the length of sameDomainRecently is greater than 0.enrichedTitle to blank.Set enrichedTitle to blank.enrichedCompany to statedCompany.Set enrichedCompany to statedCompany.enrichedEmployees to blank.Set enrichedEmployees to blank.enrichedIndustry to blank.Set enrichedIndustry to blank.enrichErrorIf a step below fails, save the error as enrichErrorleadEmail. Save it as personEnrichment.Enrich person leadEmail. Save it as personEnrichment.personEnrichment.person.title and save it as enrichedTitle.Trim personEnrichment.person.title and save it as enrichedTitle.leadEmail - judging on the form alone. enrichError.message.Show in the output: Could not look up the person behind leadEmail - judging on the form alone. enrichError.message.isFreeMailDomain is noIf isFreeMailDomain is noorgErrorIf a step below fails, save the error as orgErroremailDomain. Save it as companyEnrichment.Enrich organization emailDomain. Save it as companyEnrichment.org to companyEnrichment.organization.Set org to companyEnrichment.organization.org.name and save it as enrichedCompany.Trim org.name and save it as enrichedCompany.org.estimated_num_employees and save it as enrichedEmployees.Trim org.estimated_num_employees and save it as enrichedEmployees.org.industry and save it as enrichedIndustry.Trim org.industry and save it as enrichedIndustry.emailDomain. orgError.message.Show in the output: Could not look up the company at emailDomain. orgError.message.enrichedTitle is nullIf enrichedTitle is nullenrichedTitle to blank.Change enrichedTitle to blank.enrichedEmployees is nullIf enrichedEmployees is nullenrichedEmployees to blank.Change enrichedEmployees to blank.enrichedIndustry is nullIf enrichedIndustry is nullenrichedIndustry to blank.Change enrichedIndustry to blank.emailStatus is nullIf emailStatus is nullemailStatus to blank.Change emailStatus to blank.emailTypoSuggestion is nullIf emailTypoSuggestion is nullemailTypoSuggestion to blank.Change emailTypoSuggestion to blank.enrichedCompany is null or enrichedCompany is emptyIf enrichedCompany is null or enrichedCompany is emptyenrichedCompany to statedCompany.Change enrichedCompany to statedCompany.icpFitScore to not scored.Set icpFitScore to not scored.icpFitReason to blank.Set icpFitReason to blank.icpErrorIf a step below fails, save the error as icpErrorenrichedCompany Weโฆ and save it as icpCheck.Run an AI agent: Score how well this company fits our ideal customer profile. Company: enrichedCompany Weโฆ and save it as icpCheck.icpFitScore to icpCheck.output.score.Change icpFitScore to icpCheck.output.score.icpCheck.output.reason and save it as icpFitReason.Trim icpCheck.output.reason and save it as icpFitReason.icpError.message.Show in the output: The company fit check did not run. icpError.message.formNamesThatMeanTalkToSalesNow, in lowercase contains ,formName, in lowercase and save it as isHighIntentForm.Check whether ,formNamesThatMeanTalkToSalesNow, in lowercase contains ,formName, in lowercase and save it as isHighIntentForm.leadDossier to WHO WE SELL TO:Set leadDossier to WHO WE SELL TO:
whoYouSellTo
WHO WE DO NOT SELL TO:
whoYouDoNotSellTo
--- THE ENQUIRY ---
Form: formName plus
High-intent form: isHighIntentForm plus
Name: leadName
Email: leadEmail
Email domain: emailDomain plus
Free-mail address: isFreeMailDomain
Throwaway address: isThrowawayDomain
Role address (info@, sales@): isRoleAddress plus
Deliverability: emailStatus
Company as typed: statedCompany
Company enriched: enrichedCompany
Employees: enrichedEmployees
Industry: enrichedIndustry
Title: enrichedTitle
ICP fit score (0-100, from a separate check): icpFitScore
Already a known account: hasAccountMatch plus
Already a customer: accountIsCustomer
Has an open deal: accountHasOpenDeal
Others from this company enquired recently: isBuyingGroup plus
WHAT THEY WROTE:
leadMessage.triagePrompt to You are triaging one inbound sales enquiry for the sales team described below. Two jobs: decide what this is, and draft the reply.Set triagePrompt to You are triaging one inbound sales enquiry for the sales team described below. Two jobs: decide what this is, and draft the reply.
Judge FIT from the company against WHO WE SELL TO and WHO WE DO NOT SELL TO. Judge INTENT from what the person actually wrote and which form they used. They are different axes: a perfect-fit company idly downloading an ebook is not the same as a poor-fit company demanding a call today, and the two need opposite treatment.
Read the message before anything else. It is the only place that reveals someone is an agency pitching us, a candidate who couldn't find the careers page, a student doing research, or an existing customer who wanted support. Firmographics cannot catch any of those, and all four score well on paper.
Reply in EXACTLY this shape. Every line before the reply delimiter is 'KEY: value' on one line. Do not add commentary, headings or blank lines before the delimiter.
INTENT_CLASS: one of buyer, vendor_pitch, job_seeker, student, existing_customer_support, partner_enquiry, competitor, spam, unclear
FIT_TIER: A, B, C or D. A is squarely in the profile, D is nowhere near.
INTENT_TIER: high, medium or low
CONFIDENCE: a number from 0 to 1
EVIDENCE: one short quote from their message that drove the call. If they wrote nothing usable, put NONE.
REASONING: one sentence a rep can read in three seconds
TEAM_SIZE: a number, or UNKNOWN
TIMELINE: what they said about timing, or UNKNOWN
CURRENT_TOOLING: tools they mentioned, or UNKNOWN
COMPETITOR: a competitor they mentioned switching from, or NONE
---REPLY---
Then the reply itself, written as the rep who will run the call. Open by referencing the specific thing they asked about, in their words, not yours. Say one useful thing. Offer the booking link. Under 90 words, no bullet points, no 'I hope this email finds you well'. If INTENT_CLASS is not buyer, write a short courteous decline or redirect instead - never a sales pitch.
If the enquiry genuinely does not say enough to judge, set INTENT_CLASS to unclear and CONFIDENCE below 0.4 rather than guessing. A person will read those.
Booking link to offer: bookingLink
leadDossier.triagePrompt. Save it as triageResponse.Ask Claude: triagePrompt. Save it as triageResponse.
Maximum response length: 1400triage to {parsed:false,intent_class:unclear,fit_tier:D,intent_tier:low,confidence:0,evidence:,reasoning:"T.Set triage to {parsed:false,intent_class:unclear,fit_tier:D,intent_tier:low,confidence:0,evidence:,reasoning:"T.parseErrorIf a step below fails, save the error as parseErrorraw and save it as triage.Run Python code on raw and save it as triage.leadEmail - routing it to a human instead. parseError.message.Show in the output: Could not read the triage result for leadEmail - routing it to a human instead. parseError.message.intentClass to triage.intent_class.Set intentClass to triage.intent_class.fitTier to triage.fit_tier.Set fitTier to triage.fit_tier.intentTier to triage.intent_tier.Set intentTier to triage.intent_tier.decision to route.Set decision to route.reasonCode to blank.Set reasonCode to blank.needsHumanEye to false.Set needsHumanEye to false.intentClass is spamIf intentClass is spamdecision to drop.Change decision to drop.reasonCode to INVALID_OR_SPAM.Change reasonCode to INVALID_OR_SPAM.intentClass is vendor_pitchIf intentClass is vendor_pitchdecision to drop.Change decision to drop.reasonCode to VENDOR_PITCH.Change reasonCode to VENDOR_PITCH.intentClass is job_seekerIf intentClass is job_seekerdecision to drop.Change decision to drop.reasonCode to JOB_SEEKER.Change reasonCode to JOB_SEEKER.intentClass is studentIf intentClass is studentdecision to drop.Change decision to drop.reasonCode to STUDENT_OR_RESEARCH.Change reasonCode to STUDENT_OR_RESEARCH.intentClass is competitorIf intentClass is competitordecision to drop.Change decision to drop.reasonCode to COMPETITOR.Change reasonCode to COMPETITOR.intentClass is partner_enquiryIf intentClass is partner_enquirydecision to handoff.Change decision to handoff.reasonCode to PARTNER_ENQUIRY.Change reasonCode to PARTNER_ENQUIRY.intentClass is existing_customer_supportIf intentClass is existing_customer_supportdecision to handoff.Change decision to handoff.reasonCode to EXISTING_CUSTOMER.Change reasonCode to EXISTING_CUSTOMER.triage.parsed is no or triage.confidence is less than 0.4If triage.parsed is no or triage.confidence is less than 0.4needsHumanEye to true.Change needsHumanEye to true.decision to route.Change decision to route.reasonCode to LOW_CONFIDENCE.Change reasonCode to LOW_CONFIDENCE.isThrowawayDomain is yesIf isThrowawayDomain is yesdecision to drop.Change decision to drop.reasonCode to INVALID_OR_SPAM.Change reasonCode to INVALID_OR_SPAM.,invalid,spamtrap,abuse,do_not_mail, contains ,emailStatus,If ,invalid,spamtrap,abuse,do_not_mail, contains ,emailStatus,decision to drop.Change decision to drop.reasonCode to INVALID_OR_SPAM.Change reasonCode to INVALID_OR_SPAM.decision is routeIf decision is routereasonCode is blank and fitTier is A or fitTier is BIf reasonCode is blank and fitTier is A or fitTier is BintentTier is lowIf intentTier is lowdecision to nurture.Change decision to nurture.reasonCode to GOOD_FIT_NOT_NOW.Change reasonCode to GOOD_FIT_NOT_NOW.reasonCode to PURSUE.Change reasonCode to PURSUE.intentTier is highIf intentTier is highdecision to handoff.Change decision to handoff.reasonCode to LOW_FIT_HIGH_INTENT.Change reasonCode to LOW_FIT_HIGH_INTENT.decision to nurture.Change decision to nurture.reasonCode to LOW_FIT.Change reasonCode to LOW_FIT.routedToName to blank.Set routedToName to blank.routedToEmail to blank.Set routedToEmail to blank.routedToSlackId to blank.Set routedToSlackId to blank.routingRule to blank.Set routingRule to blank.decision is not dropIf decision is not dropaccountHasOpenDeal is yes and accountOwnerEmail isn't emptyIf accountHasOpenDeal is yes and accountOwnerEmail isn't emptyroutedToEmail to accountOwnerEmail.Change routedToEmail to accountOwnerEmail.routedToSlackId to accountOwnerSlackId.Change routedToSlackId to accountOwnerSlackId.routingRule to open-opportunity owner.Change routingRule to open-opportunity owner.routedToEmail to accountOwnerEmail.Change routedToEmail to accountOwnerEmail.routedToSlackId to accountOwnerSlackId.Change routedToSlackId to accountOwnerSlackId.routingRule to existing account owner.Change routingRule to existing account owner.routedToEmail is emptyIf routedToEmail is emptyloadedReps.rows where item.Available in lowercase is not no and item.Email isn't blank and save it as availableReps.Keep each item in loadedReps.rows where item.Available in lowercase is not no and item.Email isn't blank and save it as availableReps.availableReps is emptyIf availableReps is emptyroutingRule to no rep available.Change routingRule to no rep available.reasonCode to OWNER_UNAVAILABLE.Change reasonCode to OWNER_UNAVAILABLE.needsHumanEye to true.Change needsHumanEye to true.rotationIndex to the remainder of roundRobinTurn divided by the length of availableReps.Set rotationIndex to the remainder of roundRobinTurn divided by the length of availableReps.chosenRep to availableReps[@rotationIndex].Set chosenRep to availableReps[@rotationIndex].chosenRep.Name and save it as routedToName.Trim chosenRep.Name and save it as routedToName.chosenRep.Email lowercase and save it as routedToEmail.Make chosenRep.Email lowercase and save it as routedToEmail.chosenRep["Slack ID"] and save it as routedToSlackId.Trim chosenRep["Slack ID"] and save it as routedToSlackId.routingRule to round robin.Change routingRule to round robin.roundRobinTurn.Add 1 to roundRobinTurn.crmContactId to blank.Set crmContactId to blank.decision is routeIf decision is routecrmLookupErrorIf a step below fails, save the error as crmLookupErrorleadEmail. Save it as crmLookup.Find the contact leadEmail. Save it as crmLookup.crmLookup.id and save it as crmContactId.Trim crmLookup.id and save it as crmContactId.crmCreateErrorIf a step below fails, save the error as crmCreateErrorleadEmail. Save it as crmCreated.Create the contact leadEmail. Save it as crmCreated.crmCreated.id and save it as crmContactId.Trim crmCreated.id and save it as crmContactId.leadEmail was posted and recorded but could not be added to HubSpot, so add them by hand (crmCreateError.message). to the end of leadProblems and save it as leadProblems.Add leadEmail was posted and recorded but could not be added to HubSpot, so add them by hand (crmCreateError.message). to the end of leadProblems and save it as leadProblems.decision in a table and save it as decisionLabel.Look up decision in a table and save it as decisionLabel.fitTier in a table and save it as fitLabel.Look up fitTier in a table and save it as fitLabel.intentTier in a table and save it as intentLabel.Look up intentTier in a table and save it as intentLabel.*@decisionLabel* - @fitLabel, @intentLabel and save it as alertHeadline.Build the text *@decisionLabel* - @fitLabel, @intentLabel and save it as alertHeadline.whoLine to leadName.Set whoLine to leadName.enrichedTitle isn't emptyIf enrichedTitle isn't emptywhoLine to whoLine, enrichedTitle.Change whoLine to whoLine, enrichedTitle.enrichedCompany isn't emptyIf enrichedCompany isn't emptywhoLine to whoLine at enrichedCompany.Change whoLine to whoLine at enrichedCompany.ownerMention to routedToEmail.Set ownerMention to routedToEmail.routedToSlackId isn't emptyIf routedToSlackId isn't empty<@@routedToSlackId> and save it as ownerMention.Build the text <@@routedToSlackId> and save it as ownerMention.routedToEmail is emptyIf routedToEmail is emptyownerMention to nobody yet.Change ownerMention to nobody yet.alertBody to alertHeadlineSet alertBody to alertHeadline
whoLine
<mailto:leadEmail|leadEmail> ยท form: formName
> triage.evidence
triage.reasoning
Routed to: ownerMention (routingRule).isBuyingGroup is yesIf isBuyingGroup is yesalertBody to alertBodyChange alertBody to alertBody
:busts_in_silhouette: Someone else from emailDomain also enquired recently - treat this as one buying group.needsHumanEye is yesIf needsHumanEye is yesreasonCode in a table and save it as humanEyeReason.Look up reasonCode in a table and save it as humanEyeReason.@alertBody :eyes: Read this one yourself before replying - @humanEyeReason and save it as alertBody.Build the text @alertBody :eyes: Read this one yourself before replying - @humanEyeReason and save it as alertBody.emailTypoSuggestion isn't emptyIf emailTypoSuggestion isn't emptyalertBody to alertBodyChange alertBody to alertBody
:warning: Possible typo in the address - did they mean emailTypoSuggestion?.decision is not dropIf decision is not dropalertBody to alertBodyChange alertBody to alertBody
*Suggested reply*
triage.reply.slackErrorIf a step below fails, save the error as slackErrorslackChannelId.Send a message to slackChannelId.alertBodyLodol. The underlying error was: slackError.message.Stop and report a problem: Could not post to Slack, so nobody would hear about this lead. Check the channel id on the step called 'Create slackChannelId', and make sure the Lodol app has been invited to that channel with /invite Lodol. The underlying error was: slackError.message.routedThisRun.Add 1 to routedThisRun.droppedThisRun.Add 1 to droppedThisRun.replyMode in lowercase is draft and decision is routeIf replyMode in lowercase is draft and decision is routedraftErrorIf a step below fails, save the error as draftErrorleadEmail.Draft an email to leadEmail.
Subject: Re: formNametriage.replyleadEmail was posted to Slack and recorded, but the Gmail draft could not be saved, so reply from the suggested text in Slack (draftError.message). to the end of leadProblems and save it as leadProblems.Add leadEmail was posted to Slack and recorded, but the Gmail draft could not be saved, so reply from the suggested text in Slack (draftError.message). to the end of leadProblems and save it as leadProblems.replyWithinMinutes minutes and save it as replyDueAt.Move the current date and time by replyWithinMinutes minutes and save it as replyDueAt.ledgerErrorIf a step below fails, save the error as ledgerErrorspreadsheetUrl.Add a row to 'Ledger' in spreadsheetUrl.leadEmail (submission submissionId) was handled but could not be added to the 'Ledger' tab. The next run will handle it again, so if it was posted to Slack it will be posted a second time. Check that the Google Sheets connection can edit the spreadsheet and that the 'Ledger' tab is still there. The error was: ledgerError.message.Stop and report a problem: Stopped: leadEmail (submission submissionId) was handled but could not be added to the 'Ledger' tab. The next run will handle it again, so if it was posted to Slack it will be posted a second time. Check that the Google Sheets connection can edit the spreadsheet and that the 'Ledger' tab is still there. The error was: ledgerError.message.lead.Email (submission lead["Submission ID"]) could not be triaged and was not recorded, so the next run tries it again (leadError.message). to the end of leadProblems and save it as leadProblems.Add lead.Email (submission lead["Submission ID"]) could not be triaged and was not recorded, so the next run tries it again (leadError.message). to the end of leadProblems and save it as leadProblems.handledThisRun to routedThisRun plus droppedThisRun.Set handledThisRun to routedThisRun plus droppedThisRun.leadsForNextRun and save it as waitingCount.Count the items in leadsForNextRun and save it as waitingCount.leadProblems isn't emptyIf leadProblems isn't emptyleadProblems with a new line and save it as problemList.Join leadProblems with a new line and save it as problemList.handledThisRun. Some need attention:Stop and report a problem: New leads handled: handledThisRun. Some need attention:
problemList.waitingCount is greater than 0If waitingCount is greater than 0handledThisRun (routedThisRun posted to Slack, droppedThisRun dropped). Still waiting: waitingCount - they are picked up by the next runs, 10 at a time.Finish the run: New leads handled: handledThisRun (routedThisRun posted to Slack, droppedThisRun dropped). Still waiting: waitingCount - they are picked up by the next runs, 10 at a time.handledThisRun is 0If handledThisRun is 0handledThisRun (routedThisRun posted to Slack, droppedThisRun dropped).Finish the run: New leads handled: handledThisRun (routedThisRun posted to Slack, droppedThisRun dropped).