皆さんこんにちは、横浜で清掃業をしているヤスです。
会社のリストを上から順に見て、サイトを開き、メールアドレスを探して、1通ずつ営業メールを書く。この相手に送っていいのか少し気になりつつ、前に送った相手かどうかは記憶が頼り。送信ボタンを押すたびに、少し不安になる。
このシリーズでは、設備工事会社の求人票を教材にして、第1話から第9話までバックオフィスの自動化を練習してきました。今回も続きではあるのですが、性質がまったく違います。練習ではなく、自分が実際に営業で使うために作った、商品としての仕組みです。
先に核心を書くと、今回の軸は「商品の価値は、送る機能ではなく、送らない機能にある」です。10件のリストを入れて、送信できたのは1件。残りの9件を、全部違う理由で止めました。これが正常な動きです。
※この記事に登場する会社名・メールアドレス・サイトは、すべて検証のために私が用意した架空のものです。実在の会社に送った話ではありません。また、特定電子メール法に触れますが、この記事は法律の解説ではありません。実際に使う前には、必ずご自身で条文を確認してください。
完成形:10件入れて、送るのは1件
お客様が用意したリスト(社名・メールアドレス・サイトURL)
↓
1件ずつ判定する(ここではメールを1通も送らない)
├ アドレスの形式は正しいか
├ リスト内で重複していないか
├ 配信停止の申し出を受けていないか
├ 過去90日以内に送っていないか
├ サイトURLがあるか/サイトを開けるか
└ AI:サイトにそのアドレスが公開されているか、営業お断りの記載がないか
↓
送信候補シートに、判定・理由・件名・本文を書き出す
↓
人が読んで、承認列を「承認」にする(文面はここで直せる)
↓
送信直前にもう一度チェックして、問題がなければ送る
↓
送信履歴に記録する(次回から自動で除外される)
使ったのは、GAS(Googleのサービスを自動で動かすためのプログラム)、Gmail、Googleスプレッドシート、Googleフォーム、OpenAI API(プログラムからAIを呼び出すための窓口)です。

架空の10件を入れて動かした結果です。
判定:10件/送信可:1件
送信:1通/見送り:0件/失敗:0件(テストモード)
私の役割は、何を止めるかを決め、確かめる順番を決め、動かして結果を確かめることでした。
なぜ作ったか:実績0件で、待っていても依頼は来ない
私は今、受注実績が0件の状態で、ココナラとランサーズに登録しています。登録しただけでは依頼は来ません。こちらから声をかける必要があります。
ただ、営業メールには法律があります。特定電子メール法です。私が設計の前提にした理解は、次の4点です。
- 原則は、同意した相手にしか広告メールを送れない
- 例外として、自分のサイトなどで事業用のメールアドレスを公開している法人には、同意がなくても送れる場合がある
- 送るときは、送信者の名前や住所、配信停止の方法を表示しなければならない
- 配信停止の申し出を受けたら、以後は送ってはいけない
これは私の理解であって、法律の解説ではありません。細かい条件は、ご自身で条文を確認してください。
この前提に立つと、「リストのアドレスへ順番に送る」作りは、最初から成り立ちません。送っていい相手かどうかを1件ずつ判定する部分が、この仕組みの中身そのものになりました。
なお、リストは私が集めません。お客様が持ち込む形にしました。集める部分まで請け負うと、集め方の責任まで負うことになるからです。
設計①:止める理由を先に列挙する
送るだけなら、GASで10行ほどの処理で済みます。難しいのは、送ってはいけない相手を止めることです。判定は次のように分けました。
| 判定 | 意味 |
|---|---|
| 送信可 | すべての条件を満たした |
| お断り表記あり | サイトに営業メールお断りの記載がある |
| 公開を確認できず | そのアドレスがサイトに載っていない |
| 配信停止済み | 配信停止の申し出を受けている |
| 送信済み | 90日以内に送っている |
| 重複 | リスト内に同じアドレスがある |
| 形式不正 | アドレスの形が正しくない |
| 読み取り失敗 | サイトを開けなかった |
大事にしたのは、判定できないものは送らないことです。迷ったら、止める側に倒します。
設計②:表示義務の項目が空なら、送信そのものを止める
送信者名・住所・配信停止URLは、本文の末尾に自動で付く形にしました。人が毎回書く形にすると、いつか忘れるからです。
さらに、設定シートでこの3つのどれかが空欄なら、警告ではなく、その場でエラーを出して止まるようにしました。ここは「気をつける」で守れる場所ではないからです。
設計③:除外チェックを2回やる
取り込み時と、送信する直前の2回、配信停止と送信履歴を確認します。判定してから人が承認するまでの間に、配信停止の連絡が届くかもしれないからです。1回しか見ない作りだと、その申し出を無視して送ることになります。
設計④:AIに、送るかどうかを決めさせない
AIの役割は、サイトを読んで事実を報告するところまでです。アドレスが載っているか、営業お断りの記載があるか、何をしている会社か。送るかどうかを決めるのは人で、AIの報告は材料にすぎません。
設計⑤:テストモードを標準でONにする
標準設定では、宛先を自分に差し替えて送り、件名の頭に 【テスト/本来の宛先:◯◯】 が付きます。初めて使う人がいちばんやってはいけない失敗は、実在の相手にいきなり送ることです。設定を変えない限り、それが起きない形にしました。
検証:架空の会社サイトを6つ作った
本番の会社に送って試すわけにはいきません。そこで、架空の会社サイトを6つ作り、GitHub Pages(無料でWebページを公開できる仕組み)に置きました。
- メールアドレスのドメインは
example.com(検証用に予約されていて、実在しないドメイン) - 各ページの末尾に「検証用の架空サイトである」と明記
- 1社は営業お断りの記載あり
- 1社は問い合わせフォームのみで、アドレスは非公開
そのうえで、10行のリストに、わざと引っかかるデータを仕込みました。
| 仕込み | |
|---|---|
| ① | 正常系 |
| ② | サイトに営業お断りの記載 |
| ③ | アドレスが公開されていない |
| ④ | 配信停止リストに登録済み |
| ⑤ | 2か月前に送信済み |
| ⑥ | ①と同じアドレス(重複) |
| ⑦ | サイトが存在しない(404) |
| ⑧ | 法人でない個人。URLなし |
| ⑨ | @が抜けている |
| ⑩ | サイトURLが空欄 |
10件入れて、送信可は1件だけ。これが正常な動きです。

つまずき①:エラーが出ないまま、違うシートを読んでいた
ここからが、今回いちばん書きたかったことです。
最初に動かしたとき、ログにこう出ました。
配信停止:0件
配信停止リストには、④の1件を登録してあります。それが0件。エラーは出ていません。
原因は、仕組みに入れていた保険でした。
sheet = book.getSheetByName('配信停止') || book.getSheets()[0];
「配信停止という名前のシートが見つからなければ、1枚目のシートを読む」という1行です。シート名が多少違っても動くように、という保険です。
ところが、そのファイルの1枚目には、設定シートが残っていました。仕組みは設定シートを配信停止リストだと思って読み、メールアドレスの列が無いので、0件と返していたのです。
エラーは出ません。ただ0件と出るだけです。気づかなければ、配信停止を申し出た相手に送るという、この仕組みでいちばんやってはいけない事故の一歩手前でした。
直し方
- フォームの回答タブの名前を
配信停止に変え、名前で確実に当てる - 見出しの判定を「メール」だけでなく「アドレス」「mail」にも広げる
- それでも見つからなければ、中身に
@が入っている列を探す - どうしても見つからないときは、見出しの一覧をログに出す
効いたのは、最後の1つでした。
※配信停止シートにメールアドレスの列が見つかりません。見出し:項目 / 値 / 説明
「項目 / 値 / 説明」は設定シートの見出しです。これで、違うものを読んでいると分かりました。
つまずき②:IDが別のファイルを指していた
タブ名を直しても、結果は変わりませんでした。
原因は、設定シートに入れていた配信停止ファイルのIDが、別のファイルを指していたことです。1つのファイルを「本体」と「配信停止」の2つに分けたとき、コピー側には新しいIDが振られます。設定に書いてあったのは、分ける前のIDでした。
これもエラーは出ません。存在するファイルを正しく開いていて、ただ中身が違うだけだからです。
ここで気づきました。お客様に「ファイルのIDを入れてください」と頼む作りは、ここで必ず詰まります。IDを保存した時点で、開いたファイル名とシート名を画面に出して確かめてもらう必要があります。「入力してもらう」で終わらせず、「入力したものが正しいか見せる」ところまでが設計でした。
結果:送ったら、次は0件になった
テストモードで1通送ったあと、もう一度判定し直すと、こう変わりました。
判定:10件/送信可:0件
①が「送信済み」に変わっています。送った相手が履歴に入り、自動で除外されました。
届いたメールには、相手のサイトから読み取った事業内容が入っていました。
貴社が空調・給排水設備の設計から施工・保守まで幅広く手掛けられていることを拝見し──
末尾には、送信者名・住所・電話番号・配信停止URLが自動で付いていました。

作ってみて分かったこと
- 商品の価値は、送らない部分にある。送る機能は10行で済む。止める理由をそろえるところに時間がかかった
- 保険が事故を隠す。「見つからなければ1枚目を読む」は親切のつもりが、違うものを静かに読み続ける動きになった
- エラーが出ない間違いが、いちばんこわい。2回とも動いてはいた。ただ、違うものを見ていた
- 「入力してもらう」までが設計ではない。入力されたものが正しいか見せるところまでが設計
- 法律は、あとから足せない。表示義務を「あとで付ける」にすると、付け忘れたまま送れてしまう
- 練習と商品の違いは、手を抜いた場所が自分に返ってくるかどうか。今回は、返ってくる
まとめ
設計で決めたことをまとめます。
- 止める理由を先に列挙し、判定できないものは送らない
- 表示義務の3項目は本文に自動で付け、空欄なら送信そのものを止める
- 配信停止と送信履歴の確認は、取り込み時と送信直前の2回やる
- AIは事実を報告するだけ。送るかどうかは人が決める
- テストモードを標準にして、実在の相手にいきなり送れない形にする
同じようなものを作るなら、「リストを順に送る仕組み」として設計を始めないことをおすすめします。最初の一歩として、送らない理由を紙に書き出すところから始めてみてください。私の場合は、その一覧がそのまま仕組みの骨格になりました。
実際私はリストがないのでまずはそこを作るところからやっていこうと思います。これを使ってお客さんが来てくれたらいいなと思い次の作品を考えます
それではまた!
関連記事
設備工事会社のバックオフィス自動化シリーズ(求人票を教材にした練習編)です。
- 第1話:請求漏れをスプレッドシートで防ぐ仕組みをGASで作ってみた記録
- 第2話:Gmailの依頼メールをAIで案件表に自動登録|同じメールでも答えが変わるので「承認」を挟みました
- 第3話:Google Driveの書類不足をGASで自動チェック|エラーが出ないほうが怖いと分かった話
- 第4話:エラーも出ずに、前の見積書PDFが消えていた|メールから見積書を自動で作って気づいたこと
- 第5話:発注書と見積書の金額照合をGASで自動化|「読めないはず」という予想が外れた話
- 第6話:請求漏れ 防ぐ仕組みをGASで作り直したら、「見積中」のまま61日放置の案件が見つかった|ステータスを信じない判定
- 第7話:PDF ファイル名 統一をAIで自動化|「スキャン20260915.pdf」が「2026-011_見積書.pdf」に変わった
- 第8話:GAS Googleカレンダー 自動登録でダブルブッキングを防ぐ|承認の瞬間にもう一度確認する理由
- 第9話:GAS 通知 まとめる|自動化したら毎朝のメールが3通に増えた。1通にまとめて見えた「指摘11件→案件9件」

コメント