レシピ · 7手 · 1プロンプト実装

ニュースレターを運用する(連絡先取込 → セグメント → 一斉配信 → 配信解除)

配線済みの @event/mailer + @event/leads を土台に、自社の連絡先へニュースレターを特定電子メール法に沿って一斉配信する運用フロー(入口 → セグメント → キャンペーン → 配信解除 → 計測)を回す

前提: add-mailer解説 ch.31解説 ch.33解説 ch.36module: マッチング & ディレクトリmodule: メールmodule: リード獲得

このレシピは配線済みの @event/mailer(recipe add-mailer)と @event/leads(recipe add-leads)を土台に、 自社の連絡先へニュースレター(一斉配信)を特定電子メール法に沿って送る運用フロー を通しで示すランブック。 パッケージを組む手順ではなく、「連絡先の入口 → セグメント → キャンペーン → 配信解除 → 計測」を実際に回す運用が対象。 対象 app を apps/<app> とする(ZFILMS 本番は zfilms-lp)。

設計の芯: 送信の可否は常に 同意(consent)と suppression で決まる。氏名・メール等の PII は AES-256-GCM で暗号化して保存し、平文カラムを持たない。配信解除は取り消せない一方向操作として、 ハッシュ(blind index)だけを残して本文(メールアドレス)は crypto-shred する。

1. 前提と法令(特定電子メール法)

広告・宣伝を含むメール(ニュースレターはこれに当たる)は 原則オプトイン(事前同意)が必要。 ただし例外がある — Web サイト等で自ら公表している企業アドレス(かつ「広告メール受信拒否」の表示が無い)宛には、 事前同意なしで送れる(特電法 施行規則の公表アドレス例外)。この例外に乗る場合でも、次の3点は 常に必須 である。

要件内容
① 送信者表示送信者の氏名・名称と、問い合わせを受け付ける連絡先(メール / URL)を本文に明記する
② オプトアウトの常設配信解除(受信拒否)の手段を全メールに常設し、解除後は送らない
③ 収集元の記録いつ・どこで取得したアドレスかを記録し、求めに応じて示せるようにする

個人アドレス・フリーメール(gmail.com 等)は公表アドレス例外の対象外なので、原則オプトインが要るemailDomain がフリーメールなら「公開フォームからの自発同意(basis=form_opt_in)」がある連絡先だけを 送信対象にする、という運用で切り分ける(→ 3. セグメント)。

PII の保存: 氏名・メール・会社等は AES-256-GCM で暗号化して保存し、平文カラムを持たない。 検索・重複判定・suppression 照合は本文ではなく 決定的ハッシュ(blind index) で行う。これにより、 配信解除時に暗号文を破棄(crypto-shred)してもハッシュだけは残せ、「解除済みアドレスの再取込」を後段で弾ける(→ 5. 配信解除)。

2. 連絡先の入口(3系統)

送信対象の連絡先は3経路から入るが、出口(suppression / 配信解除)は全経路で共通 にする。 どの経路で入っても、解除済み・バウンス済みのアドレスには二度と送らない。

(a) 公開フォーム(自発オプトイン) — recipe add-leads で 配線済みの /contact。ここからの登録は本人の自発同意なので basis=form_opt_in として記録する。 個人アドレスでも送信できる、最も強い同意根拠。

(b) 過去問い合わせの取込 — Webflow / Framer で受けた過去の問い合わせを CSV エクスポートし、取込スクリプトで 連絡先へ変換する。取込時に 収集元(source)と同意根拠(basis)を必ず刻む

# DRY-RUN(既定・書き込まない): 差分と検出カラムだけ表示
node scripts/import-inquiries.mjs ./exports/webflow-inquiries.csv --source webflow

# 実適用: --apply で連絡先を upsert(source=inquiry / basis=past_inquiry を記録)
node scripts/import-inquiries.mjs ./exports/webflow-inquiries.csv --source webflow --apply
node scripts/import-inquiries.mjs ./exports/framer-contact.csv     --source framer  --apply

--sourcewebflow / framer を取り、各エクスポートの列名差を吸収する。取込レコードは source=inquiry / basis=past_inquiry(過去に自社へ問い合わせた = 取引関係のある連絡先)として consent を記録する。 取込でも既存の suppression とは常に照合し、解除済みは復活させない

(c) Web 収集リード(プロスペクティング) — 公開情報から収集した企業アドレス。これは別レシピ collect-web-leads準備中)で扱う。 収集しただけの生リードは送信対象に しない。担当が内容を確認し 承認したものだけ を連絡先へ昇格させ、 公表アドレス例外(1.)の条件を満たすかをここで人が判断する。

3. セグメント(動的絞り込み)

セグメントは連絡先を 動的に絞り込む保存済みクエリ で、送信のたびに評価される(送信時点の母集団を毎回数え直す)。 絞り込みキーは4つ。

キー用途
statussubscribed / unsubscribed / bounced送信可能な状態か
sourceform_opt_in / inquiry / prospecting入口別(同意の強さ)で分ける
tagsivs2026 / vip手動 or 取込時に付与したラベル
emailDomaingmail.com / 4s.linkフリーメール(要オプトイン)と企業ドメインの切り分け

システム定義の既定セグメントを2つ持つ。

フリーメール宛に送るときは「フォーム購読」を基点にし、企業ドメインの公表アドレス(例外適用)は emailDomain で別セグメントに切って条件を分ける。

4. キャンペーン作成 → プレビュー → テスト送信 → 送信 / 予約

キャンペーンは admin から作る。エントリは2つ — 一覧の /admin/email/campaigns、または各画面から開く作成モーダル。 作成から送信までは次の順で進む。

  1. 作成 — 件名・本文(テンプレ)・対象セグメントを選ぶ。差出人は @event/mailerMAIL_FROM(検証済みドメイン)を使う(recipe add-mailer)。
  2. プレビュー — 本文は iframesrcDoc で隔離描画し、実際の受信 HTML を確認する。
  3. セグメント人数の live count — 対象セグメントの現在の該当人数をその場で数えて出す(送信規模の最終確認。0 件や桁違いをここで気づく)。
  4. テスト送信 — 自分のアドレスに1通だけ実送信し、差出人・件名・解除リンク・レンダリングを実機確認する。
  5. 送信 / 予約 — 「今すぐ」か「日時指定」を選ぶ。どちらも即時には飛ばさず、キャンペーンを queued(or scheduled)にして キューに積むだけ

実送信はキュー + cron。毎分走る dispatch cron が scheduled の到来分と queued を拾い、Resend の batch 送信(1 リクエスト最大 100 通) に分割して送る。UI の送信操作と実配信を分離することで、 大量送信でもリクエストがタイムアウトせず、失敗分だけ再送できる。

// vercel.json — 毎分 dispatch(送信キューを掃く)
{
  "crons": [{ "path": "/api/cron/email/dispatch", "schedule": "* * * * *" }]
}

cron は毎回 suppression を再照合してから送る(キュー投入後に解除された宛先を除外する)。

5. 配信解除(unsubscribe)

解除は 2経路 で常設する。どちらも即時・確認画面なしで反映する(ワンクリック解除)。

List-Unsubscribe: <https://<app>/api/email/unsubscribe?t=TOKEN>, <mailto:unsubscribe@<domain>?subject=unsubscribe>
List-Unsubscribe-Post: List-Unsubscribe=One-Click

解除すると連絡先は status=unsubscribed になり全セグメントから外れる。このとき suppression にはハッシュ(blind index)だけを残し、 暗号化された本文は破棄(crypto-shred) する。ハッシュを残すのは、後で 2(b) / 2(c) のどの経路から同じアドレスが 再取込されても 入口で照合して弾く ため。「一度解除した人を名簿の入れ替えで復活させない」を保証する仕組みである。

バウンス・苦情の自動 suppression — Resend の webhook(email.bounced / email.complained)を受けたら、 その宛先を自動で suppression に入れて status=bounced にする。ハードバウンスや「迷惑メール報告」への再送は 送達レピュテーションを直接痛めるので、人手を介さず即時に止める。

6. 計測(webhook 集計)

配信の質は Resend の webhook を集計して見る。dispatch した各メールに紐づくイベントを受けて、 キャンペーン単位で開封・クリック・バウンス・苦情を積み上げる。

webhook イベント集計用途
email.delivered到達分母(送達できた数)
email.opened開封件名・差出人の当たり判定
email.clickedクリック本文 CTA の効き
email.bouncedバウンス名簿の劣化(→ 自動 suppression)
email.complained苦情迷惑メール報告(→ 自動 suppression)

webhook 受け口は署名検証(Resend の署名ヘッダ)を通してから記録する。開封・クリックはキャンペーンのレポートに、 バウンス・苦情は 5. の自動 suppression にそれぞれ流れる。

7. 運用チェックリスト

送信前(テスト送信の直後・本送信の直前に毎回):

送信後(配信開始〜数時間・翌日):

一般に バウンス率・苦情率が上がると送達レピュテーションが落ち、正当なメールも迷惑メール送りになる。 数値が悪化したら送信を止め、セグメントを source=form_opt_in(自発同意)中心に絞り直すのが復帰の定石。

関連レシピ