寄信這件事,看起來單純,真的上線自動化時卻容易出包:收件人抄錯、附件忘了、內容沒對齊品牌樣式。
與其直接 send,不如先用 createDraft() 把信生出來、放進草稿匣,給自己或同事最後一眼。這篇會用「能實際複製就能跑」的方式,帶你從 Apps Script 的 GmailApp.createDraft() 開始,把 HTML 版型、inline 圖片、附件、CC/BCC、寄件別名一次講清楚;
也會補上 Gmail API users.drafts.create 的寫法,順手整理權限、配額與大小限制。最後收斂常見錯誤與避雷清單,讓你在正式寄出前,養成穩健的寄信流程。希望本篇文章能夠幫助到需要的您。
目錄
{tocify} $title={目錄}
為什麼要先建立草稿(Draft)?
在自動化寄信流程裡,先建立草稿、後審核再送出能降低誤寄與內容錯誤風險;也方便團隊在寄信前加註標籤、微調措辭與附件。對於用 Google Apps Script(下稱 GAS)做通知或行銷郵件的人來說,createDraft() 幾乎是「安全寄信流」的第一站。
在 GAS 裡,這個能力由 GmailApp.createDraft() 提供;若你走的是 REST 方案,則使用 Gmail API 的 users.drafts.create。二者共同點:都會在使用者信箱建立一封帶有 DRAFT 系統標籤的未寄出郵件;差異在於 GAS 與 API 的開發體驗與權限。
createDraft() 是什麼?能做什麼?
GAS:GmailApp.createDraft(recipient, subject, body, options)
建立一封草稿郵件;options 可帶 attachments、cc、bcc、from、name、replyTo、htmlBody、inlineImages 等。若同時提供 body 與 htmlBody,支援 HTML 的郵件用戶端會優先顯示 htmlBody。
Gmail API:users.drafts.create
需先組一封 RFC 2822 的 MIME 訊息、轉成 base64url 後放到 draft.message.raw,再呼叫 API 建立草稿。適合需要跨平台、服務端或 Kotlin/Java 等語言整合的情境。
小提醒:Draft 本身只允許 DRAFT 標籤;寄出時 Draft 會被刪除並產生一封新的 SENT 郵件(ID 也會不同)。
權限、配額與大小限制(一定要先看)
GAS 權限(Scopes)
操作草稿需要 https://mail.google.com/。官方 GmailApp.createDraft() 與 GmailDraft 相關方法皆明確要求此範圍。
Gmail API 權限
可用 https://mail.google.com/、https://www.googleapis.com/auth/gmail.modify 或 https://www.googleapis.com/auth/gmail.compose。
Apps Script 服務配額
GAS 各服務皆有每日與執行限制,超過會丟出例外並中止執行;大量產草稿前,務必熟悉配額頁。
郵件大小與附件限制
Gmail 單封郵件附件上限為 25 MB(多個附件相加也受限),超過會自動轉成 Drive 連結(並非實體附件)。這個限制也適用你用 createDraft() 建立草稿時。
郵件本體大小(含標頭)有配額
官方在 createDraft() 與 sendEmail() 文件中註記「大小(含標頭,但不含附件)受配額限制」。
寄信上限(非建立草稿)
寄送量的限制由 Gmail 規範(例如 Workspace 常見 2,000 封/日;個人信箱常見 500 封/日),建立草稿不等於寄出,但真正寄送時仍會受限。請以官方頁面為準。
快速開始:用
GmailApp.createDraft()
建立你的第一封草稿
最小可行範例(純文字)
function createDraft_basic() {
GmailApp.createDraft(
'user@example.com',
'主旨:草稿測試',
'這是一封用 Apps Script 建立的草稿'
);
}
說明:三個必要參數 recipient、subject、body。收件人支援「逗號分隔」的多筆地址。
HTML 內容 + Inline 圖片 + 附件
function createDraft_html_inline_attachments() {
// 1) 從雲端硬碟抓檔案,轉成 Blob 當作附件或內嵌圖
const file = DriveApp.getFileById('YOUR_FILE_ID'); // e.g. PDF
const logo = DriveApp.getFileById('YOUR_IMAGE_ID').getBlob(); // 內嵌圖
// 2) HTML 內容請用 <img src="cid:logo1"> 對應 inlineImages 的 key
const html = `
<div style="font-family:system-ui">
<h2>您好</h2>
<p>這是一封 HTML 草稿,下面那張圖是 inline:</p>
<img src="cid:logo1" alt="logo" width="120">
</div>
`;
GmailApp.createDraft(
'user@example.com',
'主旨:HTML + Inline 圖 + 附件',
'(純文字備援內容)',
{
htmlBody: html, // HTML 優先顯示
inlineImages: { logo1: logo }, // key 與 cid 對應
attachments: [file.getAs(MimeType.PDF)] // 官方範例用法
}
);
}
重點:
1. htmlBody 存在時,多數用戶端會優先顯示 HTML;body 仍必填,作為純文字備援。
2. inlineImages 要用 物件對應 key→Blob,HTML 以 cid:key 內嵌。
3. 附件請提供 BlobSource 陣列,Drive 檔案可 getAs(MimeType.PDF)。
多收件者、CC/BCC、寄件別名
function createDraft_cc_bcc_alias() {
const aliases = GmailApp.getAliases(); // 取得可用的寄件別名
const fromAlias = aliases.length ? aliases[0] : null;
GmailApp.createDraft(
'a@ex.com,b@ex.com',
'主旨:多人與別名示範',
'內容',
{
cc: 'c@ex.com,d@ex.com', // 逗號分隔
bcc: 'e@ex.com',
from: fromAlias // 必須是 getAliases() 回傳的其中一個
}
);
}
cc/bcc 接受 逗號分隔 的電子郵件清單;from 必須是 GmailApp.getAliases() 回傳清單之一,否則無法套用。
進階控管:更新、送出或刪除草稿
建立後你可以取得 GmailDraft 物件並進一步操作:
function after_created_manage() {
const draft = GmailApp.getDrafts()[0]; // 只是示範,實務上建議存 ID
draft.update(
'user@example.com',
'主旨:更新後的草稿',
'更新 body',
{ replyTo: 'noreply@example.com' }
);
// 寄出
// const sent = draft.send();
// 或刪除
// draft.deleteDraft();
}
GmailDraft 提供 update()(可帶 options)、send() 與 deleteDraft() 等方法,權限仍需 https://mail.google.com/。
實務常見情境
A. 表單回覆 → 自動產生草稿 → 人工審核後批次寄出
1. Google 表單寫入試算表。
2. 透過 GAS 讀取新行資料,把收件人與佔位文字套進 HTML 模板。
3. 用 createDraft() 建立草稿;同時在試算表記下 Draft ID。
4. 主管在 Gmail 的「草稿匣」審核(或在試算表把狀態改為「核可」)。
5. 以排程(Trigger)定時讀取「核可」列:GmailApp.getDraft(draftId).send() 寄出。
注意:Draft 只能帶 DRAFT 標籤;想先分類,請在寄出後再以 Thread API/Apps Script 對「寄出的郵件」進行標籤處理。
B. 產出含附件的草稿(自動轉 PDF)
從 Drive 抓原始檔(如 Google 文件/試算表),用 getAs(MimeType.PDF) 轉 PDF 作為附件,再建立草稿。附件總量請控 25 MB 以內;超過會被轉為 Drive 連結。
C. 企業多寄件身分(Alias)
先在 Gmail 設定寄件別名;程式端用 GmailApp.getAliases() 取得清單,options.from 指定其一。若別名不存在,系統會忽略或無法套用。
常見錯誤與雷點
1. 附件或內嵌圖型別不正確
典型症狀:拋出類型錯誤、圖片不顯示。
原因與解法:attachments 必須是 BlobSource[];Drive 檔需先 getAs(MimeType.PDF) 等。Inline 圖需在 HTML 以 cid:key 對應 inlineImages 的 key。
2. HTML 與純文字的關係搞混
典型症狀:收件端只看到純文字、或 HTML 樣式沒有套用。
重點:htmlBody 會在支援的用戶端優先顯示,但 body 仍必填 做為備援。
3. 寄件別名(from)無法生效
典型症狀:顯示還是主帳號;或出現權限/別名錯誤。
解法:options.from 只能用 getAliases() 回傳的地址。先確認 Gmail 設定中真的有該別名。
4. CC/BCC 格式錯誤
典型症狀:收件人欄位被忽略或出現無效地址。
解法:官方說明為逗號分隔字串;確保沒有多餘空白與分號。
5. 超過附件或郵件大小限制
典型症狀:建立草稿失敗、或附件被自動換成 Drive 連結。
解法:附件總量 ≤ 25 MB;更大檔案改用 Drive 連結、壓縮或分檔。
6. API 端建立 Draft raw 格式錯誤(Gmail API)
典型症狀:400/422 類錯誤,訊息提到 payload/encoding。
解法:確保 MIME 內容合規(RFC 2822),並使用 base64url(非一般 base64);放入 draft.message.raw。
7. 誤以為 Draft 可先加自訂標籤
典型症狀:試圖對 Draft 打標籤沒反應。
事實:Draft 只能具有 DRAFT 系統標籤;若要分類,請在寄出後對郵件處理。
8. 忽略配額與節流
典型症狀:GAS 執行中斷、丟出配額錯誤。
作法:先閱讀 GAS 配額;大量產草稿時加入 sleep/批次,或改由 API/背景任務分流。
品質與可維護性:幾個實用做法
保留 Draft ID:
建立後把 draft.getId() 寫回資料庫/試算表,後續更新、寄送、刪除都靠它。GmailDraft 亦提供 update() 與 send()。
模板化 HTML:
用 <span data-field="name"></span> 搭配簡單的字串取代,或用 HtmlService 產生;寄送前總是產草稿,讓人眼檢一次。
附件前先壓縮/轉檔:降低 25 MB 風險與收件端負擔。
寄件別名治理:
多人共用同一腳本時,事先規範 alias 與 replyTo;避免客服信箱被誤用。
問題集
Q1:建立 Draft 會吃到 Gmail 的寄信上限嗎?
不會直接算寄出量;但真正寄送時仍受 Gmail 寄件限制(例如 Workspace 常見 2,000 封/日)。Apps Script 本身也有服務配額與執行限制,要同步留意。
Q2:如何在 API 端建立含內嵌圖片的 Draft?
用 multipart/related 的 MIME,圖片以 Content-ID 對應 HTML 中的 cid: ;再整封 MIME 做 base64url,放到 draft.message.raw。官方草稿指南說明了建立與更新 Draft 的流程。
Q3:能不能先對 Draft 加上自訂標籤或分類?
不行。Draft 只能帶 DRAFT。建議寄出後再打標籤或移動分類。
Q4:createDraft() 的 options 有哪些?
attachments、cc、bcc、from(必須是 getAliases() 回傳的其中之一)、name、replyTo、htmlBody、inlineImages。
