linkedin-outreach について
linkedin-outreach は、Supabase から適格なリードを選び、LinkedIn メッセージのシーケンスを個別化して、活動を記録するスキルです。ブラウザー型ツールを自動操作せず、選択されたツール向けの CSV を作成します。
主な機能
- 01Supabase から対象リードを選択
- 02条件と 84 日間のクールダウンを適用
- 03個別化したメッセージシーケンスを作成
- 04LinkedIn の文字数制限を遵守
- 05ツール別の CSV ファイルを出力
- 06Supabase に活動を記録
活用シーン
- 01適格なリード向け LinkedIn キャンペーンの準備
- 02Dripify または Expandi 用のインポートファイル作成
- 03複数チャネルで最近連絡したリードの除外
- 04Supabase でアウトリーチ活動を記録
MCP 対応
LinkedIn アウトリーチツールの操作に API や MCP 自動化は使わず、手動インポート用の CSV を作成します。
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ gooseworks-ai/ goose-skills --skill linkedin-outreachインストール後、Claude Code で/linkedin-outreachを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
skill.meta.jsontemplates/tone-presets.jsontemplates/sequence-templates/competitor-engagement.mddatabase-search.mdevent-attendee.mdhiring-signal.mdkol-engagement.mdpain-language.md詳細
LinkedIn アウトリーチ
cold-email-outreach に対応する LinkedIn 版です。Supabase から条件を満たした見込み顧客を取得し、パーソナライズした LinkedIn メッセージのシーケンスを作成して、ユーザーが使う LinkedIn アウトリーチツール用にエクスポートし、その内容をすべて Supabase に記録します。
ツールに依存しません: 使用している LinkedIn ツールをユーザーに尋ねます。すべてのツールは CSV インポート方式です。LinkedIn ツールはブラウザー上で動作するため、API/MCP による自動化には対応していません。アダプターが、ツールごとの列のマッピングや形式の違いに対応します。
自動読み込みのタイミング
次の場合に、このスキルを読み込みます。
- ユーザーが「LinkedIn アウトリーチ」「この見込み顧客に LinkedIn でつながる」「LinkedIn メッセージを送る」「LinkedIn キャンペーンを設定する」と言った場合
- 上流のスキルが「LinkedIn キャンペーンを作成する」と連携する場合、または「supabase-eligible-leads」を渡し、ユーザーが LinkedIn を指定した場合
- ユーザーが
lead-qualificationを完了し、LinkedIn で連絡を取りたい場合
対応しているアウトリーチツール
このスキルは特定のツールを前提としません。まずユーザーに尋ねてから、対応します。
| ツール | 連携方法 | 仕組み |
|---|---|---|
| Dripify | CSV インポート | Dripify のインポート形式に合わせた CSV を生成し、ユーザーが手動でアップロードします |
| Botdog | CSV インポート | Botdog 互換の列を含む CSV を生成します |
| Expandi | CSV インポート | Expandi のインポート形式に合わせた CSV を生成します |
| PhantomBuster | CSV インポート | PhantomBuster の LinkedIn シーケンス用 CSV を生成します |
| 手動/その他 | CSV +手順 | 見込み顧客とメッセージを汎用 CSV としてエクスポートし、設定手順を案内します |
ツールの選択ロジック:
- フェーズ 0 でユーザーに「どの LinkedIn アウトリーチツールを使っていますか?」と尋ねます
- 選択内容に基づき、ツール固有のインポート用 CSV を生成します
- その他または不明な場合 → 汎用 CSV(
linkedin_url、first_name、last_name、company、title、connection_request、followup_1、followup_2、followup_3、inmail_subject、inmail_body)を生成し、使用するツールのインポート要件をユーザーに尋ねます
前提条件
Supabase
人物データは、tools/supabase/schema.sql のスキーマに従って Supabase に保存されている必要があります。people テーブルと outreach_log テーブルが存在している必要があります。新規にセットアップする場合は、python3 tools/supabase/setup_database.py を実行してください。
.env 内の環境変数:
SUPABASE_URL=https://xxx.supabase.co
SUPABASE_SERVICE_ROLE_KEY=eyJ...
LinkedIn ツール
CSV をエクスポートするだけでよく、API キーは必要ありません。ユーザーが CSV を手動でツールにインポートします。
文字数制限
LinkedIn には厳格な文字数制限があります。生成するすべてのメッセージは、この制限を守る必要があります。
| メッセージの種類 | 制限 | 備考 |
|---|---|---|
| 接続リクエストのメッセージ | 300 文字 | 厳格な上限です。すべての文字がカウントされます。 |
| 通常のメッセージ | 8,000 文字 | 接続が承認された後に送信されます |
| InMail の件名 | 200 文字 | InMail(プレミアム機能)のみ |
| InMail の本文 | 1,900 文字 | InMail のみ |
制限の適用: メッセージを生成したら、文字数を数えます。上限を超えている場合は、書き直してください。切り詰めてはいけません。切り詰められたメッセージは不自然に見えます。
フェーズ 0:情報の確認
質問はまとめて一度に尋ねます。カテゴリごとに整理してください。上流のスキルですでに回答済みの質問は省略します。
キャンペーンの目標
- 目的は何ですか?(面談の予約、デモの申し込み促進、返信の獲得、関係構築、見込み顧客の育成)
- アウトリーチの切り口やフックは何ですか?(採用の兆候、競合製品からの乗り換え、イベントをきっかけにする、課題を軸にする、コールドデータベース、KOL とのエンゲージメント、共通の知人)
- このキャンペーンにどのような名前を付けますか?
アウトリーチツール
- どの LinkedIn アウトリーチツールを使っていますか?(Dripify/Botdog/Expandi/PhantomBuster/その他/CSV だけを作成)
リードの選定
- どのリードをターゲットにしますか?次の選択肢があります:
- 特定の
client_nameに該当するすべてのリード - 特定の
icp_segment - 役職のパターン(例:「VP Operations」、「Director of Sales」)
- 業界または所在地によるフィルター
qualification_scoreがしきい値を超えるリード- 特定の
source(crustdata、apollo、linkedin など) - カスタムフィルター(希望する条件を説明してください)
- 特定の
- 除外する対象はありますか?(特定の企業、最近連絡したリード、特定の役職など)
- キャンペーンの最大規模は?(デフォルト:100 — LinkedIn ツールの1日あたりの上限は、メールより低く設定されています)
トーンとスタイル
- どのトーンプリセットを使用しますか?次の選択肢を提示してください:
- カジュアル・プロフェッショナル — 親しみやすく、人間味があり、ややくだけた表現です。同僚にメッセージを送るような雰囲気です。(デフォルト)
- ソートリーダー — 洞察や通説に反する見解から始めます。送信者を専門家として位置付けます。
- 挑発的 — 既成概念に疑問を投げかけ、相手の意表を突きます。リスクもリターンも高くなります。
- エンタープライズ・フォーマル — 洗練され、構成の整った表現です。規制の厳しい業界や経営幹部層をターゲットにする場合に適しています。
- カスタム — 以前うまくいった参考メッセージを貼り付けるか、希望する雰囲気を説明してください。
- うまくいった参考メッセージはありますか?(例を貼り付けてください。トーンプリセットよりも優先されます)
シーケンスの構成
- コネクション後のフォローアップは何回にしますか?(デフォルト:3回)
- メッセージ間隔はどのくらいにしますか?(デフォルト:0日目 コネクション/3日目 FU1/7日目 FU2/14日目 FU3)
- コネクションを承認しないリード向けに、InMail を別のステップとして含めますか?(デフォルト:はい)
パーソナライズ
- これらのリードについて、どのようなシグナルデータを利用できますか?(コメントのテキスト、反応した投稿、共通のつながり、採用シグナル、イベントへの参加)
- 参照できる実績や事例はありますか?(顧客名、指標、推薦コメント)
フェーズ 1:Supabase からのリード選定
接続
共有 Supabase クライアントを使用します:
import sys, os
sys.path.insert(0, os.path.join("tools", "supabase"))
from supabase_client import SupabaseClient
client = SupabaseClient(os.environ["SUPABASE_URL"], os.environ["SUPABASE_SERVICE_ROLE_KEY"])
フィルターの作成
ユーザーの条件を people テーブルの PostgREST クエリパラメーターに対応付けます:
| ユーザーの指定 | PostgREST フィルター |
|---|---|
| 「VP Operations」 | title=ilike.*VP Operations* |
| クライアント「happy-robot」 | client_name=eq.happy-robot |
| スコア > 7 | qualification_score=gte.7 |
| LinkedIn URL がある | linkedin_url=neq.(空ではない) |
| 業界「logistics」 | industry=ilike.*logistics* |
| 所在地「San Francisco」 | location=ilike.*San Francisco* |
| ソース「crustdata」 | source=eq.crustdata |
| 84日間連絡していない | or=(last_contacted.is.null,last_contacted.lt.{84_days_ago}) |
重要: LinkedIn でアプローチする場合、対象者には必ず linkedin_url が必要です。LinkedIn URL のない対象者は除外してください。LinkedIn では連絡できません。
クールダウンフィルター(必須)
メールまたは LinkedIn を問わず、いずれかのチャネルで過去84日間に連絡した対象者は必ず除外してください。これは任意ではありません。
共有クライアントの check_cooldown() メソッドを使用します:
in_cooldown = client.check_cooldown(client_name="happy-robot", cooldown_days=84)
# Returns set of person_id strings still in cooldown
または、直接クエリを実行します:
- 過去84日間に
sent_dateがあるperson_idをoutreach_logから取得します:GET /rest/v1/outreach_log?select=person_id&sent_date=gte.{84_days_ago}&status=neq.bounced&client_name=eq.{client} - それらの
person_idを除外セットに追加します - people クエリに
id=not.in.({excluded_ids})を追加します
注: クールダウンはチャネルをまたいで適用されます。30日前にメールを送った対象者は、LinkedIn でも引き続きクールダウンの対象です。これにより、複数チャネルからの過剰な連絡を防ぎます。
表示して確認
サンプルとして、次の項目を含むリードの表を10~15件表示します:
- 名前、役職、会社、業界、スコア、LinkedIn URL、最終連絡日、シグナルの種類
ユーザーに、対象となるリードの合計数、クールダウンにより除外された数、LinkedIn URL がないために除外された数を伝えます。
先に進む前に、フィルターを確認または調整するようユーザーに尋ねます。
フェーズ 2:シーケンスの設計
コピーを作成する前に、シーケンス案を表にして提示します。
| ステップ | タイミング | メッセージの種類 | アプローチ | CTA |
|---|---|---|---|---|
| 1 | Day 0 | 接続リクエスト(300 文字) | シグナルに基づくパーソナライズされたメッセージ | 軽め — まずはつながるだけ |
| 2 | Day 3 | フォローアップ 1(承認後) | 価値を最優先:インサイト、リソース、または所見 | 質問または提案 |
| 3 | Day 7 | フォローアップ 2 | ソーシャルプルーフまたは事例 | 具体的な依頼 |
| 4 | Day 14 | フォローアップ 3 | 最後の連絡 | いつでも話せることを伝える |
| 5 | Day 7* | InMail(未承認の場合) | 背景情報を添えた単独の提案 | 面談の依頼 |
*接続リクエストが Day 7 までに承認されなかったリードに InMail を送信します。
メールシーケンスとの主な違い:
- 接続リクエストが最初の関門です。承認してもらえる内容にする必要があります。接続メッセージで売り込まないでください。
- フォローアップは一斉送信ではなく、会話形式にします。メールではなく、DM のように読める内容にしてください。
- InMail 以外には件名を付けません。
- 短いほどよい場合がほとんどです。LinkedIn では、5 文のメッセージより 2 文のメッセージのほうが効果的です。
フェーズ 3 でコピーを生成する前に、構成についてユーザーの承認を得ます。
フェーズ 3:メッセージの生成
このスキル内でメッセージを直接生成します(LinkedIn のメッセージは十分短いため、外部のサブスキルは必要ありません)。
シグナルを考慮したテンプレートの選択
リードのシグナルデータに基づいて、適切なシーケンステンプレートを選択します。
| シグナルの種類 | テンプレート | 主なパーソナライズ変数 |
|---|---|---|
| 悩みの内容に反応したユーザー(コメント文あり) | templates/sequence-templates/pain-language.md | {comment_snippet}, {pain_topic} |
| 競合他社の投稿に反応したユーザー | templates/sequence-templates/competitor-engagement.md | {competitor_name}, {post_topic} |
| KOL の投稿に反応したユーザー | templates/sequence-templates/kol-engagement.md | {kol_name}, {post_topic} |
| データベース検索(シグナルが少ない) | templates/sequence-templates/database-search.md | {title}, {company}, {industry} |
| 採用シグナル | templates/sequence-templates/hiring-signal.md | {role_hiring_for}, {job_posting_detail} |
| イベント参加者 | templates/sequence-templates/event-attendee.md | {event_name}, {event_topic} |
トーンの調整
templates/tone-presets.jsonから選択したトーンプリセットを読み込みます- ユーザーが参考メッセージを提供した場合は、プリセットよりもそちらを優先します。参考メッセージのトーン、長さ、構成、語彙を分析してください
- トーンのガイドラインを生成するすべてのメッセージに適用します
調整の繰り返し
- まず 3~5 件のリード向けにサンプルメッセージを生成します(シグナルの情報量が異なるリードを選んでください)
- ユーザーに確認用として提示します。各サンプルリードのシーケンス全体を表示してください
- 承認されるまで調整します(最大 3 回)
- 承認後に、残りのメッセージをまとめて生成します
ライティングガイドライン
接続リクエスト(最大 300 文字):
- シグナルから書き始めます(関心を持った相手の行動や発言)
- 関連性を示す文を 1 つ添えます(なぜつながりたいのか)
- 売り込み、CTA、「ぜひ~したいです」などは入れません。承認してもらえるほど興味を引く内容にしてください
- 300 文字未満であることが必須です。すべての文字を数えてください
フォローアップ 1(価値を最優先):
- つながってくれたことへの感謝を簡潔に伝えます(文全体ではなく、短いフレーズにします)
- インサイト、記事、相手の会社や業界に関する所見など、本当に役立つ情報を共有します
- 売り込みではなく、質問で締めくくります
フォローアップ 2(ソーシャルプルーフ):
- 関連する顧客または事例を紹介します
- 相手の具体的な状況に結び付けます
- 具体的で、負担の少ない依頼をします(15 分の通話、非同期での質問など)
フォローアップ 3(最後の連絡):
- これまで連絡してきたことに触れます
- 価値を 1 行で振り返ります
- プレッシャーをかけず、いつでも話せることを伝えます
- シーケンス中で最も短いメッセージにします
InMail(単独の提案):
- 件名:最大 200 文字。売り込みではなく、興味を引く内容にします
- 本文:最大 1,900 文字。接続が承認されていないため、単独で成立する内容にする必要があります
- 連絡した理由(シグナル)を説明します
- 相手があなたのことをまったく知らなくても成立する内容にします
マージ変数
すべてのリードで使用できる標準変数:
{first_name}、{last_name}、{company}、{title}、{industry}、{location}
ソースに応じて使用できるシグナル固有の変数:
{comment_snippet}— LinkedIn のコメント本文{pain_topic}— エンゲージした課題{competitor_name}— 投稿にエンゲージした競合他社{kol_name}— 投稿にエンゲージした KOL{post_topic}— 投稿の内容{event_name}— 参加したイベント{role_hiring_for}— 採用予定の職種{job_posting_detail}— 求人情報の詳細
フェーズ 4:キャンペーンのエクスポート
ステップ 1:汎用 CSV を生成する
すべてのエクスポートに共通する基本列:
linkedin_url, first_name, last_name, company, title, connection_request, followup_1, followup_2, followup_3, inmail_subject, inmail_body
ステップ 2:選択したツールに合わせて形式を整える
Dripify:
- 列の対応付け:
Profile URL→ linkedin_url、Note→ connection_request、Message 1→ followup_1 など - Dripify では、メッセージを別々の列にすべて含め、リードごとに 1 行である必要があります
- エクスポート形式:Dripify のインポートテンプレートに一致するヘッダーを含む CSV
Botdog:
- 列の対応付け:
linkedin_profile_url→ linkedin_url、connection_note→ connection_request、message_1→ followup_1 など - エクスポート形式:CSV
Expandi:
- 列の対応付け:
LinkedIn URL→ linkedin_url、Connection message→ connection_request、Follow-up #1→ followup_1 など - InMail の列に対応:
InMail subject、InMail message - エクスポート形式:CSV
PhantomBuster:
- 列の対応付け:
profileUrl→ linkedin_url、message→ connection_request - PhantomBuster では通常、一度に 1 つのアクションを処理するため、コネクションとフォローアップで別々の CSV が必要になる場合があります
- エクスポート形式:CSV
手動/その他:
- 汎用 CSV 形式を使用する
- 列の説明と、ツールに依存しないインポート手順を提示する
- 使用するツールで必要な形式をユーザーに確認し、必要に応じて調整する
ステップ 3:ファイルを保存する
skills/linkedin-outreach/output/{campaign-name}-{YYYY-MM-DD}.csv
output/ ディレクトリが存在しない場合は作成します。
ステップ 4:Google スプレッドシート(任意)
ユーザーが確認用シートを希望する場合は、google-sheets-write 機能を使って次のシートを作成します:
- タブ 1:すべてのメッセージを含むリード一覧(リードごとに 1 行)
- タブ 2:シーケンステンプレート(使用したマスターテンプレート)
- タブ 3:キャンペーン設定の概要
フェーズ 5:確認と承認
キャンペーンの概要を提示します:
Campaign: {name}
Tool: {dripify/botdog/expandi/etc.}
Leads: {count}
Sequence: Connection + {followup_count} follow-ups + InMail
Timing: Day 0 → Day {last_day}
Tone: {preset_name}
Signal types: {breakdown by signal type}
Leads with rich signal: {count} ({percentage}%)
Leads with lean signal: {count} ({percentage}%)
Export file: {file_path}
必須の承認ゲート
ユーザーから明示的な確認を得るまでは、キャンペーンを準備完了としてマークしないでください。 概要を提示した後、次のように尋ねます:「最終確定しますか?『yes』と入力すると、インポートの準備完了としてマークされます。」
承認後:
- LinkedIn ツールへのインポート準備が完了したことをユーザーに伝える
- ファイルパスを提示する
- ツール固有のインポート手順を案内する(フェーズ 4 を参照)
- インポート後、最初の 5~10 件のメッセージが正しいことを確認するようユーザーに伝える
フェーズ 6:ログ記録
データベース書き込みポリシー
このフェーズでのデータベースへの書き込みには、すべてフェーズ 5 の最終確定ゲートでユーザーから事前承認を得る必要があります。 LinkedIn キャンペーンは必ずエクスポートされ(自動起動はされません)、ユーザーが LinkedIn ツールにキャンペーンを実際にインポートしていない可能性があるため、outreach_log に記録する前にユーザーに確認してください。ユーザーがエクスポートの最終確定を確認した後にのみ記録します。
Supabase に記録する
エクスポート後、ユーザーの確認を得てから、outreach_log にレコードを挿入します:
POST /rest/v1/outreach_log
Prefer: return=minimal
[
{
"person_id": "{person_uuid}",
"campaign_name": "{campaign_name}",
"channel": "linkedin",
"tool": "{dripify/botdog/expandi/phantombuster/manual}",
"sent_date": "{ISO timestamp}",
"status": "exported",
"client_name": "{client_name}"
},
...
]
または、共有クライアントを使用します:
client.log_outreach(entries)
ステータスは "sent" ではなく "exported" です。 LinkedIn ツールはブラウザー上で動作するため、配信を確認できません。ユーザーがツールでキャンペーンを開始したことを確認した時点で、ステータスを "sent" に変更します。
人物レコードを更新する
このキャンペーンに含まれるすべての人物について、people テーブルの last_contacted を更新します:
PATCH /rest/v1/people?id=in.({person_ids})
{ "last_contacted": "{ISO timestamp}" }
概要を提示する
Campaign: {name}
{count} people logged to outreach_log (channel: linkedin)
last_contacted updated for {count} people
Cooldown active until: {date + 84 days}
Next eligible re-contact: {date}
File ready: {file_path}
クールダウン適用ルール
このスキル全体で使用するクールダウンロジックの参照セクションです。cold-email-outreach と共有されています。
| ルール | 詳細 |
|---|---|
| デフォルトのクールダウン | sent_date から84日間(12週間) |
| チャネル間の適用 | クールダウンはメールと LinkedIn の両方に適用されます。最近メールを送信したリードは、LinkedIn についてもクールダウン中です。 |
| バウンスしたリード | クールダウンの対象外です(メールのみ。LinkedIn ではバウンスしません)。クールダウンの確認時のフィルター:status=neq.bounced |
| 進行中のキャンペーンのリード | 常に対象外です。いずれかのチャネルで進行中のキャンペーンに登録されているリードは、別のキャンペーンに追加できません。 |
| ユーザーによる上書き | ユーザーは特定のリードについてクールダウンを明示的に上書きできます。許可する前に確認してください。 |
last_contacted が null の場合 | 連絡したことのないリードは常に対象になります。 |
出力ディレクトリ
キャンペーンのエクスポートは次の場所に保存されます:
skills/linkedin-outreach/output/
このディレクトリが存在しない場合は作成してください。ファイル名は {campaign-name}-{YYYY-MM-DD}.csv です。