Every hour, turns new call transcripts in a Google Sheet into a structured action plan and emails it to the rep who ran the call.
Uses
Claude
Gmail
Google Sheets
Turn sales calls into action plans
Every hour, your call log in Google Sheets is read for calls whose transcript has landed but whose plan has not been written yet. For each one, AI turns the transcript into a plan the rep can work from: where the deal stands, what was heard, next steps with an owner and a date, the risks worth acting on, and a draft follow-up email. The plan is emailed to the rep who ran the call, so every rep gets their own calls and nobody else's.
Once the plan is sent, the row's Status cell is marked with the date and recipient, so no call gets two plans. A row with no rep goes to a shared address instead, and so does a row whose Rep Email is not an address, with a note saying which row to fix. If no usable plan comes back, the row is marked for review and nothing is sent; if the email goes out but the row cannot be marked, the run stops and names the row. Five plans go out an hour at most, and each run says how many calls are still waiting. Only your own team is emailed, never the customer, and the AI is told to say something was not stated rather than guess.
You need Google Sheets, Anthropic and Gmail connected, a call log tab with Status, Call Date, Account, Rep Email and Transcript columns in any order, its link, and a shared address for plans with no rep. For one breakdown of every call sent to a manager instead, use 'Email a deal breakdown after every sales call'.
spreadsheetUrl to blank.Set spreadsheetUrl to blank.sheetName to Calls.Set sheetName to Calls.defaultRecipient to blank.Set defaultRecipient to blank.maxPerRun to 5.Set maxPerRun to 5.minTranscriptChars to 400.Set minTranscriptChars to 400.spreadsheetUrl. Save it as sheet.Read the rows of spreadsheetUrl. Save it as sheet.headers to sheet.headers.Set headers to sheet.headers.headers doesn't contain StatusIf headers doesn't contain Statusheaders doesn't contain Call DateIf headers doesn't contain Call Dateheaders doesn't contain AccountIf headers doesn't contain Accountheaders doesn't contain Rep EmailIf headers doesn't contain Rep Emailheaders doesn't contain TranscriptIf headers doesn't contain Transcriptrows to sheet.rows.Set rows to sheet.rows.plansSent to 0.Set plansSent to 0.waitingCount to 0.Set waitingCount to 0.row in rows:For each row in rows:row.Status and save it as rowStatus.Trim row.Status and save it as rowStatus.rowStatus isn't emptyIf rowStatus isn't emptyrow.Transcript and save it as transcript.Trim row.Transcript and save it as transcript.transcript is less than minTranscriptCharsIf the length of transcript is less than minTranscriptCharsplansSent is greater than or equal to maxPerRunIf plansSent is greater than or equal to maxPerRunwaitingCount.Add 1 to waitingCount.row.Account and save it as account.Trim row.Account and save it as account.account is emptyIf account is emptyaccount to Unnamed account.Change account to Unnamed account.row["Call Date"] and save it as callDate.Trim row["Call Date"] and save it as callDate.callDate is emptyIf callDate is emptycallDate to Not recorded.Change callDate to Not recorded.row["Rep Email"] and save it as repEmail.Trim row["Rep Email"] and save it as repEmail.sendTo to defaultRecipient.Set sendTo to defaultRecipient.recipientNote to blank.Set recipientNote to blank.repEmail isn't emptyIf repEmail isn't emptyrepEmail is a valid email addressIf repEmail is a valid email addresssendTo to repEmail.Change sendTo to repEmail.This plan came to you because the Rep Email cell on row , row.row_number as text, of your call log holds , @repEmail, ", which is not an email address. Put the rep's address in that column so their next plans go to them.Join [This plan came to you because the Rep Email cell on row , row.row_number as text, of your call log holds , @repEmail, , which is not an email address. Put the rep's address in that column so their next plans go to them.
] with nothing and save it as recipientNote.account
Call date: callDate
Transcript:
<<<
transcript
>>>
Write the plan in Markdown using exactly these five sections, in this order, and nothing outside them.
## Where the deal stands
Two or three sentences on what this call changed about the opportunity: what moved forward, what stalled, and how likely this is to close on the timeline discussed. Say plainly if the call revealed the deal is weaker than it looked.
## What we heard
One bullet each, in this order, using the customer's own words in quotes wherever you can:
- Pain: the problem they are trying to solve, and what it costs them today.
- Impact: what changes for them if it is solved, in their numbers if they gave any.
- Decision process: who signs, what steps come before that, and any procurement, security or legal review named.
- Budget: any figure, range, or budget-cycle detail stated.
- Timeline: the dates or events they tied a decision to.
- Stakeholders: everyone named on the call or referred to, with their role and where they stand.
- Objections and competition: what they pushed back on, and any other vendor or in-house option mentioned.
## Next steps
One line per action, formatted exactly as: owner - action - due date. The owner is a named person from the call. Include only actions that were agreed on the call or that follow directly from an explicit commitment. If a date was not given, write no date agreed rather than inventing one. If no next step was agreed at all, write a single line saying so and nothing else, because that is the most important thing this plan can tell the rep.
## Risks
The two or three things most likely to stall or lose this deal, each with what the rep should do about it. Base each one on something in the transcript, not on general sales advice.
## Suggested follow-up email
A short draft the rep can send to the main contact: subject line, then the body. Recap what was agreed, confirm the next step and its date, and ask for anything you need from them. Six sentences at most. This is a draft for the rep to review and send themselves; it is not sent by this workflow.
Rules:
- Use only what is in the transcript. Never invent a name, number, date, company or commitment.
- Where the transcript does not cover a field, write Not stated on this call for that field. Do not fill a gap with an inference and do not drop the bullet.
- Do not open with a preamble, close with a summary, or restate these instructions.'. Save it as plan.
Maximum response length: 4000plan.response_text and save it as planText.Trim plan.response_text and save it as planText.planText is less than 200If the length of planText is less than 200row of sheetName in spreadsheetUrl.Set Status: Needs review - no plan came back from the AI on row row of sheetName in spreadsheetUrl.plansSent.Add 1 to plansSent.]( with ( in planText and save it as planOpenLinks.Replace ]( with ( in planText and save it as planOpenLinks.[ with in planOpenLinks and save it as planPlainLinks.Replace [ with in planOpenLinks and save it as planPlainLinks.** with in planPlainLinks and save it as planUnbolded.Replace ** with in planPlainLinks and save it as planUnbolded.
- with
• in planUnbolded and save it as planBulleted.@recipientNoteAction plan from the call with @account on @callDate. @planBulleted and save it as planIntroduced.Build the text @recipientNoteAction plan from the call with @account on @callDate. @planBulleted and save it as planIntroduced.planIntroduced by "Split planIntroduced by
## and save it as planSections.Written by Claude from the call transcript in row , row.row_number as text, of your call log. Check it against the recording before you act on anything that commits you to a date or a price.] joined by nothing to the end of planSections and save it as planParts.Add [Written by Claude from the call transcript in row , row.row_number as text, of your call log. Check it against the recording before you act on anything that commits you to a date or a price.] joined by nothing to the end of planSections and save it as planParts.planParts with "Join planParts with
──────────────────────────────
and save it as emailBody.sendTo.Send an email to sendTo.
Subject: Action plan: account - callDateemailBodymarkErrorIf a step below fails, save the error as markErrorPlan sent , today's date, to , sendTo] joined by nothing on row row of sheetName in spreadsheetUrl.Set Status: [Plan sent , today's date, to , sendTo] joined by nothing on row row of sheetName in spreadsheetUrl.The action plan for row , row.row_number as text, (, account, ) was emailed to , sendTo, , but the row could not be marked as sent: , markError.message, . Type anything into that row's Status cell (for example Plan sent), or the same plan will be sent again next hour.] joined by nothing.Stop and report a problem: [The action plan for row , row.row_number as text, (, account, ) was emailed to , sendTo, , but the row could not be marked as sent: , markError.message, . Type anything into that row's Status cell (for example Plan sent), or the same plan will be sent again next hour.] joined by nothing.plansSent.Add 1 to plansSent.Action plans sent this run: , plansSent as text, . Calls still waiting for a later run: , waitingCount as text, .] joined by nothing.Show in the output: [Action plans sent this run: , plansSent as text, . Calls still waiting for a later run: , waitingCount as text, .] joined by nothing.