Drafts each planned blog topic with AI, emails it to your reviewer, and records their decision in your sheet.
Uses
Google Sheets
OpenAI
Draft blog posts and email them for approval
Once a day, the topics on your content plan that have not been through review yet are written up as blog posts by AI in your house style, and each full draft is emailed to your reviewer to approve or reject, from the email itself. The run waits for their answer, so nothing counts as done until a person has looked at it.
Each topic ends up as a draft in the reviewer's inbox and a row in a 'Draft Approvals' tab in the same spreadsheet: the topic, whether it was approved or changes were requested, the reviewer's notes and the date. Each topic is logged as awaiting review before the reviewer is asked and that log is checked again before each request, so a topic is never drafted twice, and a topic nobody answers is recorded as 'No answer' rather than resent. Your plan tab is only ever read, a topic whose draft fails is picked up next time, and nothing is published anywhere.
You need a plan tab with a 'Topic' column, optionally an 'Angle' column for extra direction, the reviewer's email address and a short description of your audience and house style.
planSpreadsheetUrl to blank.Set planSpreadsheetUrl to blank.planTabName to blank.Set planTabName to blank.reviewerEmail to blank.Set reviewerEmail to blank.audienceAndStyle to blank.Set audienceAndStyle to blank.approvalLogTabName to Draft Approvals.Set approvalLogTabName to Draft Approvals.maxDraftsPerRun to 3.Set maxDraftsPerRun to 3.logTabErrorIf a step below fails, save the error as logTabErrorapprovalLogTabName in planSpreadsheetUrl.Create the tab approvalLogTabName in planSpreadsheetUrl.logTabError as text.Show in the output: Using the existing '@approvalLogTabName' tab: logTabError as text.planSpreadsheetUrl. Save it as approvalLog.Read the rows of planSpreadsheetUrl. Save it as approvalLog.approvalLog.headers is emptyIf approvalLog.headers is emptyapprovalLogTabName in planSpreadsheetUrl.Add a row to approvalLogTabName in planSpreadsheetUrl.reviewedTopics.Start an empty list and save it as reviewedTopics.loggedTopic in approvalLog.rows:For each loggedTopic in approvalLog.rows:loggedTopicErrorIf a step below fails, save the error as loggedTopicErrorloggedTopic.Topic in lowercase to the end of reviewedTopics and save it as reviewedTopics.Add loggedTopic.Topic in lowercase to the end of reviewedTopics and save it as reviewedTopics.planSpreadsheetUrl. Save it as plan.Read the rows of planSpreadsheetUrl. Save it as plan.draftsThisRun to 0.Set draftsThisRun to 0.plannedTopic in plan.rows:For each plannedTopic in plan.rows:draftsThisRun is greater than or equal to maxDraftsPerRunIf draftsThisRun is greater than or equal to maxDraftsPerRunReached this run's limit of drafts. The rest of the plan is picked up on the next run..Show in the output: Reached this run's limit of drafts. The rest of the plan is picked up on the next run..topic to blank.Set topic to blank.topicErrorIf a step below fails, save the error as topicErrorplannedTopic.Topic and save it as topic.Trim plannedTopic.Topic and save it as topic.topic is emptyIf topic is emptyreviewedTopics contains topic in lowercaseIf reviewedTopics contains topic in lowercasetopic.Show in the output: Already sent for approval, skipping: topic.angle to blank.Set angle to blank.angleErrorIf a step below fails, save the error as angleErrorplannedTopic.Angle and save it as angle.Trim plannedTopic.Angle and save it as angle.angle to blank.Change angle to blank.brief to Topic: topic.Set brief to Topic: topic.angle isn't emptyIf angle isn't emptybrief to briefChange brief to brief
Angle to take: angle.prompt to You are writing one post for a company blog.Set prompt to You are writing one post for a company blog.
Audience and house style:
audienceAndStyle
brief
Write the finished post as plain text an editor can read in an email. Open with a short introduction, put each section heading on its own line, and close with a brief conclusion. Aim for 600-900 words, use concrete examples, and do not invent statistics, quotes, customer names or links. Return only the post itself - no title line and no commentary before or after it.draft to blank.Set draft to blank.writingErrorIf a step below fails, save the error as writingErrordraft.Trim the response text from the OpenAI โAsk OpenAIโ step and save it as draft.writingError as text.Show in the output: Could not draft '@topic': writingError as text.draft is emptyIf draft is emptyloggedNow to nothing.Set loggedNow to nothing.logReadErrorIf a step below fails, save the error as logReadErrorapprovalLogTabName in planSpreadsheetUrl. Save it as loggedNow.Read column 'A' of approvalLogTabName in planSpreadsheetUrl. Save it as loggedNow.logReadError as text.Show in the output: Could not read the '@approvalLogTabName' tab, so '@topic' stays on the plan for the next run: logReadError as text.loggedNow.values contains topicIf loggedNow.values contains topictopic.Show in the output: Already waiting on another run's review, skipping: topic.claimErrorIf a step below fails, save the error as claimErrorapprovalLogTabName in planSpreadsheetUrl.Add a row to approvalLogTabName in planSpreadsheetUrl.claimError as text.Show in the output: Could not log '@topic' as awaiting review, so it stays on the plan for the next run: claimError as text.approvalStatus to No answer.Set approvalStatus to No answer.reviewerNotes to blank.Set reviewerNotes to blank.approvalErrorIf a step below fails, save the error as approvalError@topic? Save it as review.Request approval: Approve the draft for @topic? Save it as review.
Who to ask: reviewerEmailApprove to record the draft as approved. Reject it, with a comment saying what you want changed, and it is recorded as changes requested. Nothing is published either way.
draftreview.approved is yesIf review.approved is yesapprovalStatus to Approved.Change approvalStatus to Approved.approvalStatus to Changes requested.Change approvalStatus to Changes requested.review.comment and save it as reviewerNotes.Trim review.comment and save it as reviewerNotes.reviewerEmail: approvalError as text.Show in the output: The review of '@topic' could not be sent to reviewerEmail: approvalError as text.approvalStatus to Not sent for review.Change approvalStatus to Not sent for review.reviewerNotes to approvalError.message.Change reviewerNotes to approvalError.message.logErrorIf a step below fails, save the error as logErrorapprovalLogTabName in planSpreadsheetUrl.Add a row to approvalLogTabName in planSpreadsheetUrl.logError as text.Show in the output: Could not record the outcome for '@topic': logError as text.approvalStatus is Not sent for reviewIf approvalStatus is Not sent for reviewreviewerEmail, so the run stopped rather than use up more topics. Check 'reviewerEmail', then delete '@topic' from the '@approvalLogTabName' tab to have it drafted again.Stop and report a problem: The review of '@topic' could not be sent to reviewerEmail, so the run stopped rather than use up more topics. Check 'reviewerEmail', then delete '@topic' from the '@approvalLogTabName' tab to have it drafted again.draftsThisRun.Add 1 to draftsThisRun.