返回指南列表

自動寄信的三道防線:白名單、狀態過濾、額度上限

2026年7月31日
黃志宏
AI 導入實作

結論先講

自動化寄信最常見的事故不是寄不出去,是:

  • 寄錯人——測試時不小心寄給了真實客戶名單
  • 重複寄——流程重跑,同一個人收到三封
  • 一次爆量——迴圈寫錯,五百封同時飛出去

這三種都不是技術難題,是沒有設防線。三道防線各擋一種:

防線擋什麼實作成本
白名單寄錯人約十行程式
狀態過濾重複寄一個資料庫欄位
額度上限一次爆量一個計數器

第一道:白名單

為什麼測試階段最需要

事故幾乎都發生在測試時。 你以為在測,結果真的寄給了客戶名單。

做法

用環境變數存允許的收件者清單,寄出前檢查:

import os

# 環境變數:ALLOWED_RECIPIENTS="me@example.com,test@example.com"
ALLOWED = [
    e.strip()
    for e in os.getenv("ALLOWED_RECIPIENTS", "").split(",")
    if e.strip()
]
# 空清單代表未設定 → 一律不寄,避免「忘了設定」變成「全部放行」
DRY_RUN = os.getenv("EMAIL_DRY_RUN", "true").lower() == "true"


def can_send(to_addr: str) -> tuple[bool, str]:
    if DRY_RUN:
        return False, "DRY_RUN 模式,未實際寄出"
    if not ALLOWED:
        return False, "白名單未設定,拒絕寄送"
    if to_addr not in ALLOWED:
        return False, f"收件者不在白名單:{to_addr}"
    return True, "OK"

兩個設計重點

一、預設為「不寄」。 EMAIL_DRY_RUN 預設 true、空白名單一律拒絕——忘記設定時的行為應該是安全的,不是全部放行。

這一點反過來寫,就是事故的來源。

二、跳過要留紀錄。 不在白名單時記錄下來,不要靜默略過。否則你會以為寄了,實際上沒寄。

正式上線時才把白名單換成真實名單來源,並關閉 dry run。


第二道:狀態過濾

為什麼需要

自動化流程會重跑。 網路中斷、程式報錯、手動重試——都可能讓同一批名單被處理兩次。

沒有狀態標記,客戶就會收到重複的信。

做法

每筆記錄加一個寄送狀態欄位:

欄位用途
email_statuspending / sent / failed / skipped
sent_at寄出時間
attempt_count嘗試次數
last_error最後一次的錯誤訊息

流程變成:

  1. 只取 status = pending 的名單
  2. 寄出成功後立刻更新為 sent
  3. 失敗更新為 failed 並記錄錯誤

順序很重要

先更新狀態,還是先寄信?

正確答案是:寄出成功後立刻更新,而且更新失敗要視為嚴重錯誤並停止流程。

理由:如果先標記 sent 再寄,寄送失敗時這個人就永遠不會再被寄到(漏掉)。如果寄了但沒更新成功,下次重跑會重複寄(打擾)。兩害相權,漏掉比打擾更難發現,但重複寄對客戶的傷害更直接——所以更新失敗要當成事故處理,不要讓流程繼續。

attempt_count 的作用

它防的是無限重試。設一個上限(例如 3),超過就標記 failed 不再嘗試。

否則一個永遠寄不出去的地址(例如網域已失效),會讓流程每次都卡在同一筆。


第三道:額度上限

設多少

設成你能接受的最大損失,不是服務商的上限。

一封寄錯的代價建議上限
道歉一次就過去20 封
會影響商譽5 封
涉及敏感內容1 封(等於強制逐封確認)

上限的真正作用

不是省額度,是限制單次事故的規模

程式出錯時,這個數字決定了你要收拾 5 封還是 500 封。

實作

BATCH_LIMIT = int(os.getenv("EMAIL_BATCH_LIMIT", "5"))

sent = 0
for row in pending_rows:
    if sent >= BATCH_LIMIT:
        log(f"已達單次上限 {BATCH_LIMIT} 封,剩餘 {len(pending_rows) - sent} 筆留待下一輪")
        break
    # ... 寄送邏輯
    sent += 1

達到上限時要記錄剩餘筆數。 靜默停止會讓你以為寄完了。


台灣寄行銷信:個資法的三件事

這一節不是加分項,是義務

一、蒐集時要明確告知

依個人資料保護法,蒐集個資時應告知當事人:蒐集目的、個資類別、利用期間與地區、對象及方式,以及當事人的權利。

實務落地:表單旁邊要有清楚的說明,不能只放一個沒有內容的勾選框。

二、行銷利用要在原告知目的範圍內

如果當初蒐集的目的是「訂單處理」,後來拿去寄行銷信,可能超出原本告知的特定目的。

實務落地:表單上就明確寫「同意接收行銷資訊」,並與其他同意項目分開勾選——不要綁在「同意服務條款」裡。

三、每封信都要有可用的退訂方式

當事人表示拒絕接受行銷時,必須立即停止利用其個資行銷。

實務落地

  • 每封信底部有明顯的退訂連結
  • 退訂免費,且不需要登入
  • 退訂後立刻更新狀態,下一輪流程要讀取這個狀態

退訂要接回第二道防線

退訂狀態必須成為狀態過濾的一部分:

取名單條件:status = pending AND unsubscribed = false

很多系統做了退訂按鈕,但流程沒讀那個欄位——於是使用者退訂了還是收到信。這比沒有退訂按鈕更糟,因為它是可證明的違規。


用個人信箱寄行銷信的問題

技術上可行,但有兩個實際問題:

  1. 每日發送量有限,超量容易被暫時鎖定
  2. 會影響寄件信譽——大量發送後,連你正常的工作信件都可能被判為垃圾信

行銷信建議使用專門的寄信服務,它們處理了退信管理、退訂機制與信譽維護。用個人信箱省下的錢,可能要用「重要信件寄不到客戶」來付。


上線前的檢查清單

  • 白名單預設為空、dry run 預設開啟(忘記設定時是安全的)
  • 被跳過的收件者有留紀錄
  • 每筆名單有寄送狀態欄位,寄成功後立刻更新
  • 有重試次數上限
  • 單次批量有上限,達到上限時記錄剩餘筆數
  • 蒐集表單有完整的告知內容
  • 行銷同意與服務條款分開勾選
  • 每封信有免費、免登入的退訂連結
  • 取名單的查詢條件包含了退訂狀態

最後一項是最容易漏、後果也最明確的一項。


常見問題

測試階段就要做白名單嗎?感覺很麻煩。

測試階段最需要。事故幾乎都發生在測試時——你以為在測,結果真的寄給了客戶名單。做法很簡單:設一個環境變數存允許的收件者清單,程式在寄出前檢查收件者是否在清單內,不在就記錄並跳過。這段程式大約十行,但它擋掉的是最不可挽回的錯誤。

為什麼需要「狀態過濾」?

因為自動化流程會重跑。網路中斷、程式報錯、手動重試,都可能讓同一批名單被處理兩次。如果沒有在資料庫標記「這個人已經寄過了」,客戶就會收到重複的信。做法是每筆記錄加一個寄送狀態欄位,寄成功就標記,寄之前先檢查。

額度上限該設多少?

設成你能接受的最大損失,而不是服務商的上限。如果一封信寄錯的代價是道歉一次,那設 20 封;如果會影響商譽,設 5 封。上限的目的不是省額度,是限制單次事故的規模——程式出錯時,它決定了你要收拾 5 封還是 500 封。

在台灣寄行銷信,法律上要注意什麼?

三件事:蒐集個資時要明確告知用途、寄行銷信前要取得當事人同意、每封信都要提供免費且有效的退訂方式。個人資料保護法要求蒐集時就告知特定目的,而後續用於行銷需在該目的範圍內;當事人表示拒絕接受行銷時,必須立即停止。這三項不是加分項,是義務。

用自己的個人信箱寄行銷信可以嗎?

技術上可以,但不建議。個人信箱的每日發送量有限,超量容易被暫時鎖定;而且大量發送會影響該信箱的寄件信譽,之後連正常的工作信件都可能被判為垃圾信。行銷信建議使用專門的寄信服務,它們處理了退信、退訂與信譽管理。

想把這套方法用在自己的團隊?

梵亞行銷提供企業 AI 轉型顧問與內訓服務,從流程盤點、工具選型到落地驗收,陪你把 AI 真正接進日常工作。

了解 AI 轉型顧問服務