Auto-draft email replies with AI
Stage contextual draft replies in Gmail for every incoming Northwind support thread.
Published Jul 18, 2025
Northwind’s support inbox is full of messages that need a reply but not a lot of thought — order chases, “how do I” questions, polite acknowledgements. Writing each one from a blank box is slow, and the slowness is what makes the queue back up. The team did not want AI sending mail unsupervised; they wanted a head start.
This script reads every unread support thread, asks Claude to draft a friendly, concise reply, and stages it as a Gmail draft on the thread. Nothing is sent — an agent opens the thread, reads the draft, tweaks it if needed, and hits send. The slow part is done; the human stays in control.
What you’ll need
- A Gmail
supportlabel applied to incoming support threads, by filter or by hand. The script only looks at threads carrying that label. - An Anthropic API key saved as
ANTHROPIC_API_KEYin Script Properties — see Store API keys and secrets securely. - Nothing else — the script creates the
support/ai-draftedlabel itself.
The script
// Gmail search that selects threads needing a draft: unread support
// mail that has not already been drafted.
const SEARCH_QUERY = 'label:support is:unread -label:support/ai-drafted';
// Label applied once a draft has been staged, so threads are not
// drafted twice on the next run.
const DRAFTED_LABEL = 'support/ai-drafted';
/**
* Finds unread support threads, asks Claude to draft a reply for each,
* stages it as a Gmail draft, and labels the thread as drafted.
*/
function draftReplies() {
const threads = GmailApp.search(SEARCH_QUERY);
if (!threads.length) {
Logger.log('No support threads need a draft — nothing to do.');
return;
}
// Find or create the marker label once, outside the loop.
const drafted = GmailApp.getUserLabelByName(DRAFTED_LABEL)
|| GmailApp.createLabel(DRAFTED_LABEL);
for (const t of threads) {
// 1. Use the first message in the thread as the customer's question.
const msg = t.getMessages()[0];
// 2. Ask Claude for a reply based on the subject and body.
const reply = generateReply(msg.getSubject(), msg.getPlainBody());
// 3. Stage the draft and label the thread so it is skipped next run.
if (reply) {
t.createDraftReply(reply);
t.addLabel(drafted);
}
}
}
/**
* Builds the prompt and asks Claude for a single support reply.
*/
function generateReply(subject, body) {
const prompt =
'Draft a friendly, concise Northwind support reply (≤120 words). ' +
'Sign off "— Northwind support".\n\n' +
'Subject: ' + subject + '\n\n' + body;
return callClaude(prompt);
}
/**
* Minimal Anthropic API call. The key lives in Script Properties — it
* is never pasted into the code.
*/
function callClaude(prompt) {
const key = PropertiesService.getScriptProperties()
.getProperty('ANTHROPIC_API_KEY');
const res = UrlFetchApp.fetch('https://api.anthropic.com/v1/messages', {
method: 'post',
contentType: 'application/json',
headers: { 'x-api-key': key, 'anthropic-version': '2023-06-01' },
payload: JSON.stringify({
model: 'claude-sonnet-4-6',
max_tokens: 400,
messages: [{ role: 'user', content: prompt }],
}),
muteHttpExceptions: true,
});
return JSON.parse(res.getContentText()).content[0].text.trim();
}
How it works
draftRepliesruns a Gmail search for unread threads labelledsupportthat do not already carrysupport/ai-drafted. If none match, it logs a message and stops — no wasted API calls.- It resolves the
support/ai-draftedlabel once, creating it the first time the script runs. - For each thread it takes the first message as the customer’s enquiry and
passes the subject and plain-text body to
generateReply. generateReplywraps the message in a prompt that asks for a friendly reply under 120 words with a fixed sign-off, then calls Claude.callClaudesends the prompt to the Anthropic API with the key pulled from Script Properties, and returns the model’s text.- Back in the loop, the returned reply is staged on the thread with
createDraftReply— staged, never sent — and the thread is labelled so the next run skips it.
Example run
A customer sends a thread labelled support:
Subject: Where’s my order #4821?
Hi, I ordered the medium prints on Monday and haven’t had a dispatch email yet. Could you check?
After a run, a draft sits on the thread, ready for an agent to review:
Hi there,
Thanks for getting in touch about order #4821. I can see it’s with our print team now and dispatch confirmations usually follow within one working day — you should have yours shortly. If it hasn’t arrived by tomorrow, just reply here and I’ll chase it directly.
— Northwind support
The thread is now labelled support/ai-drafted, so it will not be drafted
again.
Trigger it
Run this on a time-based trigger so drafts are waiting when an agent opens the queue:
- In the Apps Script editor open Triggers (the clock icon).
- Add a trigger for
draftReplies, Time-driven, Minutes timer, every 15 minutes.
Fifteen minutes keeps drafts fresh without hammering the API. For a quieter inbox, an hourly trigger is plenty.
Watch out for
createDraftReplyonly stages a draft — it never sends. A human must open each thread and click send. Keep it that way: an AI reply that is wrong on facts is fine in a draft and harmful in a sent message.- The script reads only the first message in the thread. On a long back-and-forth
it will miss later context — use
t.getMessages().slice(-1)[0]if you want the most recent message instead. - Each thread is one API call. A burst of 200 unread threads is 200 calls and
200 lots of tokens; watch your Anthropic spend and Apps Script’s daily
UrlFetchAppquota. muteHttpExceptionsmeans a failed call returns an error body, not an exception. If the API key is missing or rate-limited,JSON.parsewill throw on the error JSON — check the logs if drafts stop appearing.- The model has no access to order systems or account data. It writes a plausible, friendly reply; the agent reviewing it is responsible for the facts.
Related
Generate and test email subject lines
A/B test AI-written Northwind subject lines for open rate — outputs ranked by past performance.
Updated Mar 3, 2026
Build retrieval-augmented Q&A over your data
Answer Northwind questions grounded in your own Sheet data — pass relevant rows as context.
Updated Feb 27, 2026
Build an AI weekly-report narrator
Turn Northwind metrics into a written executive summary — numbers in, prose out.
Updated Feb 23, 2026
Build a multi-step AI agent workflow
Chain Claude prompts to complete a Northwind task end to end — research → draft → critique → finalise.
Updated Feb 11, 2026
Adapt marketing copy per region
Localise Northwind tone and references by market with AI — same message, regional flavour.
Updated Jan 30, 2026