Create Drafts From Rows

A sheet of recipients and a template, and a human who still wants to read every email before it goes out. createDraftsFromRows(rows, buildFn) builds one Gmail draft per row (to, subject, HTML body), skips blank emails, and returns { created, skipped } so you can log how many landed in Drafts.

When to use

  • Batch outreach where a person hits Send after a quick review
  • Reminder or renewal emails that need a last look at the subject line
  • Any flow where sending straight from the script is one mistake away from a blast

How to use this snippet

/** * Create Gmail drafts from row objects. Skips blank `to`. * * @param {Object[]} rows * @param {function(Object): {to: string, subject?: string, htmlBody?: string, body?: string, cc?: string, bcc?: string, name?: string, attachments?: *}} buildFn * @return {{created: number, skipped: number}} */ function createDraftsFromRows(rows, buildFn) { var created = 0; var skipped = 0; (rows || []).forEach(function (row) { var msg = buildFn(row); if (!msg || msg.to == null || !String(msg.to).trim()) { skipped++; return; } var to = String(msg.to).trim(); var subject = msg.subject == null ? '' : String(msg.subject); var htmlBody = msg.htmlBody != null ? String(msg.htmlBody) : (msg.body == null ? '' : String(msg.body)); var options = { htmlBody: htmlBody }; if (msg.cc) options.cc = msg.cc; if (msg.bcc) options.bcc = msg.bcc; if (msg.name) options.name = msg.name; if (msg.attachments) options.attachments = msg.attachments; GmailApp.createDraft(to, subject, htmlBody, options); created++; }); return { created: created, skipped: skipped }; }

Example

function draftRenewals() { var sh = SpreadsheetApp.getActive().getSheetByName('Renewals'); var data = sh.getDataRange().getValues(); var headers = data[0]; var rows = data.slice(1).map(function (r) { var o = {}; headers.forEach(function (h, i) { if (h) o[h] = r[i]; }); return o; }); var result = createDraftsFromRows(rows, function (row) { return { to: row.Email, subject: 'Renewal — ' + row.Client, htmlBody: '<p>Hi ' + row.Contact + ', your plan renews on ' + row.RenewalDate + '.</p>' }; }); console.log(result.created + ' drafts, ' + result.skipped + ' skipped'); }

Tips:

  • Drafts land in the running user's Gmail Drafts. Run it as the person who will hit Send.
  • Blank or whitespace-only to values are skipped, not errors. Use skipped to catch empty Email columns.
  • Optional cc, bcc, name, and attachments on the built object are passed through to GmailApp.createDraft.
  • This does not send. Pair with a human review pass, or a later script that sends only drafts you've tagged.

Tip: NitroGAS Co-Pilot can turn your template Doc or HTML into the buildFn. Paste one sample row.

Happy Coding!

NitroGAS Chrome Extension

Want this snippet handy inside the Apps Script editor? NitroGAS gives you free snippets — plus optional Co-Pilot when you want a boost (Day Pass — $3, or 1-week, 1-month, or yearly passes — no subscription). Happy Coding!

Get the Extension