Emails each customer about their purchase in your tone, reads their feedback first, and adds a coupon when the feedback is negative.
Uses
Claude
Gmail
Google Sheets
Thank happy customers and win back unhappy ones with a coupon
Once a day, your sheet of purchases and feedback is read, AI works out whether each customer's feedback is positive, neutral or negative, and each one gets a personalised email from your own Gmail account in the tone you choose. Negative feedback earns an apology and a coupon code; everyone else gets a thank you that answers what they said.
Your sheet is never changed; a separate 'Emails sent' tab records one line per email: address, product, how the feedback was read, whether a coupon went out and the subject. That tab is checked before every send, so nobody is emailed twice about the same purchase, and if it cannot be written after a send the run stops at once. Rows with no feedback yet wait until it arrives, rows with a bad address are reported and skipped, and no coupon is ever offered on the thank you path. Each run sends a capped number of emails.
Your sheet needs 'Name', 'Email', 'Product' and 'Feedback' columns, plus a coupon code and what it is worth.
sheetUrl to blank.Set sheetUrl to blank.sheetTabName to blank.Set sheetTabName to blank.businessName to blank.Set businessName to blank.senderName to blank.Set senderName to blank.emailTone to warm and friendly.Set emailTone to warm and friendly.couponCode to blank.Set couponCode to blank.couponDetails to blank.Set couponDetails to blank.optOutLine to blank.Set optOutLine to blank.sentLogTabName to Emails sent.Set sentLogTabName to Emails sent.maxEmailsPerRun to 1.Set maxEmailsPerRun to 1.sentLogIsNew to true.Set sentLogIsNew to true.createLogTabErrorIf a step below fails, save the error as createLogTabErrorsentLogTabName in sheetUrl.Create the tab sentLogTabName in sheetUrl.sentLogIsNew to false.Change sentLogIsNew to false.sentLogIsNew is yesIf sentLogIsNew is yessentLogTabName in sheetUrl.Add a row to sentLogTabName in sheetUrl.alreadyEmailed to an empty list.Set alreadyEmailed to an empty list.sentLogKeys to an empty list.Set sentLogKeys to an empty list.sentLogReadErrorIf a step below fails, save the error as sentLogReadErrorsentLogTabName in sheetUrl. Save it as sentLog.Read column 'A' of sentLogTabName in sheetUrl. Save it as sentLog.sentLogKeys to sentLog.values.Change sentLogKeys to sentLog.values.loggedKey in sentLogKeys:For each loggedKey in sentLogKeys:loggedKeyErrorIf a step below fails, save the error as loggedKeyErrorloggedKey in lowercase to the end of alreadyEmailed and save it as alreadyEmailed.Add loggedKey in lowercase to the end of alreadyEmailed and save it as alreadyEmailed.sentThisRun to 0.Set sentThisRun to 0.couponsSent to 0.Set couponsSent to 0.alreadyEmailedRows to 0.Set alreadyEmailedRows to 0.waitingForFeedbackRows to 0.Set waitingForFeedbackRows to 0.skippedRows to 0.Set skippedRows to 0.sendFailures to 0.Set sendFailures to 0.sheetUrl. Save it as customerSheet.Read the rows of sheetUrl. Save it as customerSheet.customerSheet.count is 0If customerSheet.count is 0row in customerSheet.rows:For each row in customerSheet.rows:sentThisRun is greater than or equal to maxEmailsPerRunIf sentThisRun is greater than or equal to maxEmailsPerRunrowLabel to row row.row_number as text.Set rowLabel to row row.row_number as text.customerName to blank.Set customerName to blank.customerEmail to blank.Set customerEmail to blank.product to blank.Set product to blank.feedback to blank.Set feedback to blank.rowIsReadable to true.Set rowIsReadable to true.rowErrorIf a step below fails, save the error as rowErrorrow.Name and save it as customerName.Trim row.Name and save it as customerName.row.Email and save it as customerEmail.Trim row.Email and save it as customerEmail.row.Product and save it as product.Trim row.Product and save it as product.row.Feedback and save it as feedback.Trim row.Feedback and save it as feedback.rowIsReadable to false.Change rowIsReadable to false.rowIsReadable is noIf rowIsReadable is norowLabel: the sheet needs Name, Email, Product and Feedback columns, spelled exactly like that, and one of them is missing.Show in the output: Skipped rowLabel: the sheet needs Name, Email, Product and Feedback columns, spelled exactly like that, and one of them is missing.skippedRows.Add 1 to skippedRows.customerEmail is a valid email addressIf not customerEmail is a valid email addressrowLabel: the Email cell is empty or does not hold an email address.Show in the output: Skipped rowLabel: the Email cell is empty or does not hold an email address.skippedRows.Add 1 to skippedRows.feedback is emptyIf feedback is emptywaitingForFeedbackRows.Add 1 to waitingForFeedbackRows.sendKey to customerEmail in lowercase | product in lowercase.Set sendKey to customerEmail in lowercase | product in lowercase.alreadyEmailed contains sendKeyIf alreadyEmailed contains sendKeyalreadyEmailedRows.Add 1 to alreadyEmailedRows.feedback to 1500 characters and save it as feedbackForPrompt.Shorten feedback to 1500 characters and save it as feedbackForPrompt.sentimentPrompt to You are reading feedback a customer left after buying a product. Decide the overall sentiment of the feedback.Set sentimentPrompt to You are reading feedback a customer left after buying a product. Decide the overall sentiment of the feedback.
positive - they are pleased with the product or the experience
neutral - mixed, matter-of-fact, or too little to tell either way
negative - they are disappointed or frustrated, or something went wrong
Reply with EXACTLY one of these words and nothing else: positive, neutral, negative.
Product: product
Feedback: feedbackForPrompt.sentimentPrompt. Save it as sentimentAnswer.Ask Claude: sentimentPrompt. Save it as sentimentAnswer.
Maximum response length: 16sentiment to unclear.Set sentiment to unclear.sentimentErrorIf a step below fails, save the error as sentimentErrorsentimentAnswer.response_text with . replaced by lowercase and save it as sentiment.Make sentimentAnswer.response_text with . replaced by lowercase and save it as sentiment.sentiment to unclear.Change sentiment to unclear.sentiment is not positive and sentiment is not neutral and sentiment is…If sentiment is not positive and sentiment is not neutral and sentiment is…rowLabel (customerEmail): could not tell whether the feedback is positive, neutral or negative. It will be tried again on the next run.Show in the output: Skipped rowLabel (customerEmail): could not tell whether the feedback is positive, neutral or negative. It will be tried again on the next run.skippedRows.Add 1 to skippedRows.customerNameForPrompt to customerName.Set customerNameForPrompt to customerName.customerNameForPrompt is emptyIf customerNameForPrompt is emptycustomerNameForPrompt to not known.Change customerNameForPrompt to not known.productForPrompt to product.Set productForPrompt to product.productForPrompt is emptyIf productForPrompt is emptyproductForPrompt to not recorded - refer to it as their recent purchase.Change productForPrompt to not recorded - refer to it as their recent purchase.customerContext to Business: businessNameSet customerContext to Business: businessName
Customer's name: customerNameForPrompt
What they bought: productForPrompt
What they said in their feedback: feedbackForPrompt
Tone to write in: emailTone
Sign off as: senderName.subject to blank.Set subject to blank.body to blank.Set body to blank.couponFlag to no.Set couponFlag to no.sentiment is negativeIf sentiment is negativecouponLine to Coupon code couponCode - couponDetails.Set couponLine to Coupon code couponCode - couponDetails.couponSubjectPrompt to Write the subject line for a short email from businessName to a customer whose feedback about their purchase was negative.Set couponSubjectPrompt to Write the subject line for a short email from businessName to a customer whose feedback about their purchase was negative.
customerContext
The email apologises for what went wrong, shows that their feedback was read, and offers a coupon to make it right. Do not put the coupon code in the subject line.
Rules: under 60 characters, no quotation marks, no emoji, no placeholders such as [Name]. Write in the tone given above. Reply with the subject line only.couponSubjectPrompt. Save it as couponSubjectAnswer.Ask Claude: couponSubjectPrompt. Save it as couponSubjectAnswer.
Maximum response length: 60couponSubjectErrorIf a step below fails, save the error as couponSubjectErrorcouponSubjectAnswer.response_text and save it as subject.Trim couponSubjectAnswer.response_text and save it as subject.subject to blank.Change subject to blank.couponBodyPrompt to Write the body of a short email (under 170 words) from businessName to a customer whose feedback about their purchase was negative.Set couponBodyPrompt to Write the body of a short email (under 170 words) from businessName to a customer whose feedback about their purchase was negative.
customerContext
Apologise sincerely and specifically for what they described, without being defensive or making excuses, and thank them for telling you. Then offer them this coupon as an apology, quoting the code exactly as written: couponLine.
Rules: plain text only - no subject line, no markdown, no bullet points and no placeholders such as [Name]. Write in the tone given above. If the customer's name is not known, open with a plain greeting rather than inventing one. Do not invent facts about the product or the order. Do not promise a refund, a replacement or anything beyond the coupon. Sign off as senderName. Reply with the email body only.couponBodyPrompt. Save it as couponBodyAnswer.Ask Claude: couponBodyPrompt. Save it as couponBodyAnswer.
Maximum response length: 700couponBodyErrorIf a step below fails, save the error as couponBodyErrorcouponBodyAnswer.response_text and save it as body.Trim couponBodyAnswer.response_text and save it as body.body to blank.Change body to blank.body isn't empty and body doesn't contain couponCode (ignoring case)If body isn't empty and body doesn't contain couponCode (ignoring case)body to bodyChange body to body
couponLine.couponFlag to yes.Change couponFlag to yes.thanksSubjectPrompt to Write the subject line for a short follow-up email from businessName to a customer, thanking them for a recent purchase.Set thanksSubjectPrompt to Write the subject line for a short follow-up email from businessName to a customer, thanking them for a recent purchase.
customerContext
The email thanks them, shows that their feedback was read, and invites them to shop with businessName again.
Rules: under 60 characters, no quotation marks, no emoji, no placeholders such as [Name]. Write in the tone given above. Reply with the subject line only.thanksSubjectPrompt. Save it as thanksSubjectAnswer.Ask Claude: thanksSubjectPrompt. Save it as thanksSubjectAnswer.
Maximum response length: 60thanksSubjectErrorIf a step below fails, save the error as thanksSubjectErrorthanksSubjectAnswer.response_text and save it as subject.Trim thanksSubjectAnswer.response_text and save it as subject.subject to blank.Change subject to blank.thanksBodyPrompt to Write the body of a short follow-up email (under 150 words) from businessName to a customer, thanking them for a recent purchase.Set thanksBodyPrompt to Write the body of a short follow-up email (under 150 words) from businessName to a customer, thanking them for a recent purchase.
customerContext
Thank them for their purchase, respond in a sentence or two to what they said in their feedback, and warmly invite them to shop with businessName again.
Rules: plain text only - no subject line, no markdown, no bullet points and no placeholders such as [Name]. Write in the tone given above. If the customer's name is not known, open with a plain greeting rather than inventing one. Do not invent facts about the product or the order. Do not offer a discount, a coupon or a refund. Sign off as senderName. Reply with the email body only.thanksBodyPrompt. Save it as thanksBodyAnswer.Ask Claude: thanksBodyPrompt. Save it as thanksBodyAnswer.
Maximum response length: 700thanksBodyErrorIf a step below fails, save the error as thanksBodyErrorthanksBodyAnswer.response_text and save it as body.Trim thanksBodyAnswer.response_text and save it as body.body to blank.Change body to blank.subject with replaced by " and save it as subject.Trim subject with replaced by " and save it as subject.body with ** replaced by and save it as body.Trim body with ** replaced by and save it as body.subject is empty or body is emptyIf subject is empty or body is emptyrowLabel (customerEmail): no usable subject line or email body came back. It will be tried again on the next run.Show in the output: Skipped rowLabel (customerEmail): no usable subject line or email body came back. It will be tried again on the next run.skippedRows.Add 1 to skippedRows.optOutLine isn't blankIf optOutLine isn't blankbody to bodyChange body to body
optOutLine.emailSent to true.Set emailSent to true.sendErrorIf a step below fails, save the error as sendErrorcustomerEmail.Send an email to customerEmail.
Subject: subjectbodyemailSent to false.Change emailSent to false.customerEmail (rowLabel): sendError as text.Show in the output: Could not send to customerEmail (rowLabel): sendError as text.emailSent is noIf emailSent is nosendFailures.Add 1 to sendFailures.logWriteErrorIf a step below fails, save the error as logWriteErrorsentLogTabName in sheetUrl.Add a row to sentLogTabName in sheetUrl.customerEmail but could not add it to the '@sentLogTabName' tab. The run stopped here so that nobody is emailed twice on the next run - fix the spreadsheet, then run it again.Stop and report a problem: Sent the email to customerEmail but could not add it to the '@sentLogTabName' tab. The run stopped here so that nobody is emailed twice on the next run - fix the spreadsheet, then run it again.sendKey to the end of alreadyEmailed and save it as alreadyEmailed.Add sendKey to the end of alreadyEmailed and save it as alreadyEmailed.sentThisRun.Add 1 to sentThisRun.couponFlag is yesIf couponFlag is yescouponsSent.Add 1 to couponsSent.sentThisRun as text email(s) this run, couponsSent as text with a coupon. alreadyEmailedRows as text row(s) had already been emailed, waitingForFeedbackRows as text row(s) have no feedback yet, skippedRows as text row(s) were skipped and sendFailures as text send(s) failed. Raise 'maxEmailsPerRun' to send more in one run.Show in the output: Sent sentThisRun as text email(s) this run, couponsSent as text with a coupon. alreadyEmailedRows as text row(s) had already been emailed, waitingForFeedbackRows as text row(s) have no feedback yet, skippedRows as text row(s) were skipped and sendFailures as text send(s) failed. Raise 'maxEmailsPerRun' to send more in one run.