mcpField
S
sales-outreach

sales-outreach

github.com/crustdataマーケティング・グロース11309 回インストール2 週間前に更新
リポジトリ

sales-outreach について

sales-outreachは、営業担当者向けに、最近の個人または企業のシグナルを確認してから個別化した連絡文を作るスキルです。メール、電話、LinkedInメッセージ、返信に対応し、確かな材料がなければ作り話を避けて明示します。

主な機能

  1. 01個人や企業の最近のシグナルを検索
  2. 02メール、電話、LinkedInの文面を作成
  3. 03テスト用の異なる切り口を提示
  4. 04反論対応と返信文を準備
  5. 05根拠のないフックを作らない

活用シーン

  1. 01最近の動向に基づくコールドメールの作成
  2. 02電話スクリプトと反論対応の準備
  3. 03LinkedIn招待、メッセージ、フォローアップの作成
  4. 04受信内容に応じた返信の作成

MCP 対応

Crustdata MCPのCode Modeで個人の投稿、企業動向、資金調達、採用情報を取得し、連絡文の事実的な根拠として使います。

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/crustdata/skills --skill sales-outreach

インストール後、Claude Code で/sales-outreachを入力して読み込まれたか確認します。

同梱ファイルevals/evals.jsonREADME.md

詳細

営業アウトリーチ

やることは一つです。何を伝えるか――そして、本当に相手について調べたと伝わるか?

文章を書くのは簡単なほうです。メッセージが相手に響くのは、読んでいる本人に関する具体的で新しい、事実に基づく話題から始まっているからです。先週書いた投稿、先月まとめた資金調達ラウンド、新たに募集を始めた職種、採用したばかりの幹部などです。このスキルでは、まずCrustdataからそうした情報を取得してから、それを軸に文章を作成します。

文章より先に、フックを見つけます。必ずです。 「フィンテック業界にいると知りました」という内容をもとにどれほど巧みに書いたメールも、テンプレートにすぎません。「火曜日に投稿された、オンコール体制についての内容」という話題を盛り込んだ、率直な3行のメールには返信が来ます。対象者について実際のシグナルが見つからない場合は、その旨を伝えて別の切り口を提案してください。フックを決して捏造しないでください。


Code Modeの基本ルール(最初に一度読み、常に適用してください)

Crustdataの呼び出しはすべて、Crustdata MCPサーバーのexecuteツール内 (install.crustdata.com/mcp)で、プレーンなJavaScriptスクリプトとして実行します。以下のスニペットにはすべて、次のルールが適用されます。

  • プレーンなJavaScriptのみを使用します。 get_schemaから得られる型付きツールの仕様に沿って記述しますが、スクリプト本体には型注釈を一切含めません。: Type、as、ジェネリックなどがあると構文エラーになり、ツール呼び出しが行われる前に実行全体が失敗します。この場合、クレジットは消費されません。
  • すべてのスクリプトの冒頭に、ソースを示すコメントを付けます。ユーザーの文字どおりの依頼には// user query: ...、そこから導いた処理には// model query: ...を使います。これがないスクリプトは、実行前に拒否されます。
  • I/Oの手段は一つだけです。 const r = await callTool(name, params) → { ok: true, data }または{ ok: false, status, errorType, message }です。必ずr.okで分岐してください。 呼び出しに失敗してもスクリプトは中断されないため、失敗を確認しないまま処理を続けると、空のデータがそのまま使われ、「最近の活動はない」と誤って判断されます。これこそが、フックの捏造につながる失敗です。
  • fieldsはレスポンスの許可リストです。 結果には、指定したグループのデータだけが含まれます。指定しなかったグループを後で参照するとundefinedになり、データがないように見えます。読み取るグループはすべて指定してください。
  • 必要最小限のデータを返します。 スクリプトが返した内容だけがモデルに渡されます。アウトリーチの場合、必要なのはフック候補(日時、テキスト1行、リンク)のみで、投稿の生データやプロフィール全体ではありません。
  • 独立した処理はawait parallelMap(items, fn)で並列実行します。呼び出しごとに上限がある場合は、最初にchunkで分割してください。カーソルページネーションや依存関係のある段階は、決して並列化しないでください。コストのかかる段階の後にはcheckpoint(acc)を実行し、タイムアウトしても取得に費用をかけたフックが返るようにしてください。

ステップ0 — 事前確認(必ず、1文字でも書く前に行います)

  1. 設定ファイルを確認します。作業を止める必要はありません。 作業ディレクトリにconfig/persona-profile.mdまたは config/gtm-config.mdがある場合は、担当者の文体(トーン、文の長さ、署名、禁止事項)、販売するもの、購入者を確認します。ファイルがない場合は、チャット内で「何を販売していますか?署名には何を使いますか?」と一つ質問するか、LinkedInのURL一つから両方のファイルを作成するicp-builderスキルを案内してください。設定ファイルがなくても下書きは作成します。率直で簡潔な文体を使い、その旨を伝えてください。
  2. 依頼内容を1行で確認します:WHO(人物、アカウント、またはリスト)— CHANNEL(メール/電話/LinkedIn)— MOMENT(初回連絡/フォローアップ/返信/反論への対応)— GOAL(ミーティング?回答?紹介?)。
  3. フックを取得します(次のセクション)。鋭いフック一つは、弱いフック五つに勝ります。

フックのレイヤー(このスキルの中心です)

情報源は4つあり、最も低コストで具体的なものから順に確認します。一つだけ取得し、使える情報が見つかったらそこで止めてください。同じ人物について4つすべてを調べるのは、たいていクレジットの無駄です。

フック 1 — 本人が発信したこと(投稿)

最も有効なフックです。本人の投稿で、日付も付いているからです。本人の プロフェッショナルネットワークのプロフィール URL をキーにします。投稿 1 件につき 1 クレジットなので、limit が支出を調整するつまみです — フックを 1 つ見つけるには 5 件で十分です。

// user query: write a cold email to https://www.linkedin.com/in/example — find a hook first
const url = "https://www.linkedin.com/in/example";
const cutoffMs = Date.now() - 60 * 24 * 3600 * 1000; // hooks older than ~60 days read as stale

const r = await callTool("social_post_list_live", {
  professional_network_profile_url: url, // person key; company_domain / crustdata_company_id key the company feed
  limit: 5,                              // 1 CREDIT PER POST — set this deliberately
  fields: ["text", "date_posted", "post_type", "engagement", "hyperlinks", "share_url"], // whitelist
});
if (!r.ok) return { hooks: [], error: r.message };

const parse = (d) => { const t = Date.parse(d ?? ""); return Number.isNaN(t) ? null : t; };
const posts = (r.data.posts ?? []).map(p => ({
  date: p.date_posted,
  ms: parse(p.date_posted),
  type: p.post_type,
  url: p.share_url, // the link to the post itself — the rep needs it to verify in five seconds
  text: (p.text ?? "").slice(0, 400),
  reactions: p.engagement?.total_reactions,
  comments: p.engagement?.total_comments,
  links: p.hyperlinks?.other_urls ?? [],
}));
return {
  fresh: posts.filter(p => p.ms !== null && p.ms >= cutoffMs),
  stale: posts.filter(p => p.ms !== null && p.ms < cutoffMs).length,
  undated: posts.filter(p => p.ms === null), // never silently drop these — read them yourself
};

結果を読むときは、コメントのないシェアよりも本人が書いた投稿を、告知よりも意見を優先してください。実際の見解が書かれた投稿なら、賛成または反対できる一文が見つかり、それが返信を引き出します。

フック 2 — 会社に起きたこと(資金調達、ニュース、成長)

company_identify は無料ですが、精度は高くありません。1 つのドメインが複数の会社に一致する場合があります。最も高い confidence_score を選び、その後はID でエンリッチして、該当する 1 件だけに課金されるようにします(2 クレジット)。ドメインや名前でエンリッチすると、複数の会社にファジーマッチして、返された候補ごとに 2 クレジットが課金されることがあります。exact_match: true を指定すると候補を絞れますが、1 件だけになるとは限りません。

// model query: recent company events at acme.com worth opening an email with
const idr = await callTool("company_identify", { domains: ["acme.com"] }); // free, one identifier type per call
if (!idr.ok) return { error: idr.message };
const matches = idr.data[0]?.matches ?? [];
const best = matches.slice().sort((a, b) => (b.confidence_score ?? 0) - (a.confidence_score ?? 0))[0];
if (!best) return { error: "no company match for acme.com" };
const companyId = best.company_data?.basic_info?.crustdata_company_id ?? best.company_data?.crustdata_company_id;

const er = await callTool("company_enrich", {
  crustdata_company_ids: [companyId],                       // one id = one match = exactly 2 credits
  exact_match: true,                                        // narrows the fuzzy match; by-name/by-domain can still return >1 -> N x 2 cr
  fields: ["basic_info", "funding", "headcount", "news"],    // whitelist: list EVERY group you read
});
if (!er.ok) return { error: er.message };
const cd = er.data[0]?.matches?.[0]?.company_data ?? {};
const news = (firstArray(cd.news) ?? []).slice(0, 10).map(n => ({
  title: n.article_title, url: n.article_url, date: n.article_publish_date,
}));
return {
  name: cd.basic_info?.name,
  lastRound: cd.funding?.last_round_type,
  lastRaise: cd.funding?.last_fundraise_date,
  lastRoundUsd: cd.funding?.last_round_amount_usd,
  investors: cd.funding?.investors,
  headcount: cd.headcount?.total,
  growth12m: cd.headcount?.growth_percent?.["12m"],
  news,
};

資金調達は有効期限のあるフックです。3 週間前に完了したラウンドなら、「おめでとうございます。次に通常どんな問題が起きるかご紹介します」と切り出せます。14 か月前のラウンドは背景情報であり、書き出しにはなりません。ニュース記事には実際の公開日があるので、文中でその日付を使い、具体的な主張に言及する場合は記事へのリンクを付けてください。

フック 3 — どんな人材を採用しているか

求人情報は、会社が公開する情報の中で最も率直なものです。募集を始めた職種は、会社が費用をかけると決めた課題を示しています。

// model query: what acme.com is hiring for, and whether their posts name our category
const domain = "acme.com";
const term = "<the tool you displace>"; // a brand/product/tech name -> ALWAYS [.] exact token, never (.)
const [fresh, named] = await parallelMap([
  {
    filters: eq("company.basic_info.primary_domain", domain),
    sorts: [{ field: "metadata.date_added", order: "desc" }],
    fields: ["job_details", "location", "metadata"],
    limit: 15,
  },
  {
    filters: and_(
      eq("company.basic_info.primary_domain", domain),
      exactToken("content.description", term) // [.] guarantees the literal token; (.) is typo-tolerant and pulls lookalikes
    ),
    fields: ["job_details"],
    limit: 1, // count query — read total_count, don't download rows
  },
], async (params) => await callTool("job_search", params));
if (!fresh.ok) return { error: fresh.message };
return {
  newest: fresh.data.job_listings.map(j => ({
    title: j.job_details?.title,
    city: j.location?.city,
    added: j.metadata?.date_added,
  })),
  total_open: fresh.data.total_count,
  postings_naming_term: named.ok ? named.data.total_count : null,
};

職種は job_details.category ではなく、タイトルで絞り込んでください — category フィールドは大まかで、ほとんどの求人が「その他」に分類されます。「データエンジニアリングの求人が 4 件あります」と伝えたいなら、タイトルを数えてください。

求人の構成からフックが見えてきます。SDR の求人が 3 件あるなら、引き上げたばかりのパイプライン目標を示しています。初めてセキュリティ担当者を採用するなら、セキュリティツールを購入しようとしているということです。実際の職種名と、求人の投稿日を挙げてください。

フック 4 — すでに発生したシグナル

最も良いケースは、作業がすでに済んでいることです。sales-prospecting のチャンピオントラッカー、拡大レーダー、アカウントランキングは、日付付きのシグナル行を出力します。1 行をそのまま受け取り、取得を丸ごと省略してください。

引き継がれる行の例は次のとおりです。人物 — [顧客企業] で [役職] を務めていた — 現在は [新しい会社] で [役職] — [日付] に着任。これがそのままフックになり、すでに日付も付いています。「2026-04-12 にシリーズ B を調達」や「6 週間前に VP Data を採用」も同様です。

引き継がれた情報を再取得しないでください。色を添える場合(たとえば、新しい仕事に就いてから最初の投稿を取得する場合)に限り、下書きに必要であればクレジットを使います。

シグナル → 切り口
シグナル切り口最初の一手
最近の資金調達(3 か月未満)拡大時の痛み — 次の人員増で何が破綻するか半文で祝意を伝え、そのラウンドによって生じる課題に触れる
自社の担当領域における新任役員新たな責務、90 日計画、新しい予算「就任したばかり」という雰囲気で、何を引き継いだのか尋ねる
自社のチャンピオンが新しい会社に転職すでに自社の仕事を知っている「おめでとうございます。これからどうなるかご存じですよね」(これ以上に温かい接点はありません)
関連チームでの採用急増課題が現実のものだと認めている求人に触れ、採用が決まるまでに何が起きるのか尋ねる
実際の意見が書かれた投稿賛成、補足、または丁寧な反論投稿された事実ではなく、考え方に応える
求人情報に記載されたテクノロジー技術スタックとの適合、または置き換えツール名を挙げ、接続部分を示す
会社のニュース/ローンチタイミングローンチと自社製品が解消する課題を結び付ける
フックがなければ、捏造しない

取得結果が空だった場合(最近の投稿がない、資金調達が2年前、募集中の職種がないなど)は、 そのことを明確に伝え、参考情報をでっち上げるのではなく、代替案を提示してください。

  • 役職レベルのインサイト(「従業員300人の企業では、エンジニアリング担当VPなら誰もが直面します」など)をもとに書き、それが役職レベルの情報であることを明記します。偽の具体例より、優れた一般論のほうが役に立ちます。
  • 同じアカウントで活動している別の人物を対象にフックを探します。その人のプロフィールURLをすでに持っている場合(ユーザーのリスト、または人物の特定を行うsales-prospecting/account-researchから取得済みの場合)は、その人に対してHook 1を実行します。
  • 企業自身の発信内容を使います。個人の発信が少ない場合、company_domainをキーにしてsocial_post_list_liveを実行すると、企業のフィードを取得できます。
  • シグナルが現れるまで待つことを提案し、sales-prospectingのチャンピオントラッカーでウォッチャーを設定します。

フックは草案に入れる前に、次の3つのテストに合格する必要があります。本人のものか(業界全般の話ではないか)、日付が新しいか(投稿は60日以内、資金調達/ニュースは90日以内)、そして一読しただけで本人が思い当たるか。


タッチの種類

1. コールドメール

**90語未満。**件名は3~5語程度で、控えめな表記にし、釣り見出しや「簡単な質問」は避けます。

  • **1行目 — フック。**本人に関する内容で、日付が明確で、本人の言葉を使います。読んだ人が「そう、確かに自分がやったことだ」と思えるようにします。
  • **2行目 — つなぎ。**本人の話題と、こちらが解決する課題をつなぐ1文です。
  • **3行目 — 具体的な成果を1つ。**価値提案ではなく、数字か、顧客の具体的な状況を挙げます。
  • **4行目 — 依頼を1つ。**相手の負担が最も少なく、それでも次のアクションにつながるものにします(「木曜日に15分ほどお時間をいただけますか?」や「2行でまとめてお送りしましょうか?」など)。
  • ペルソナ設定に署名が含まれていれば、その署名を使います。icp-builderが作成する設定にはトーンやスタイルが記録されますが、署名は含まれないため、初回は文中で尋ね、以降は同じ署名を使います。

フックは1つ、アイデアは1つ、依頼は1つにします。テスト用に2~3種類の切り口(異なるフック、または同じフックで異なる課題)を提示し、どれを送るかも明記します。

2. 電話スクリプト
  • 切り出し+許可を得る一言(「突然のお電話で失礼します。30秒だけお時間をいただいて、不要なら切っていただいて構いませんか?」)。
  • 電話の理由。同じフックに結びつけます。「ご連絡しただけです」では、コールドコールはうまくいきません。
  • 価値仮説を1文で伝え、相手が訂正できる推測として述べます。
  • 自由回答形式のディスカバリー質問を2~3問 — 現在どのように対応しているか、どれだけのコストがかかっているかを尋ねます。
  • 依頼するのは商談であり、売り込みではありません。
  • 反論への対応を具体的に書きます。「興味がない」/「メールを送ってください」/「すでに何か使っています」/「予算がありません」/「受付担当者」。それぞれ、受け止める一言と、会話を続けるための質問を1つずつ用意します。
  • 15秒の留守番電話メッセージと1行のフォローアップテキストを用意します。どちらも同じフックに触れ、各タッチが相互につながるようにします。
3. LinkedInでのタッチ
  • つながり申請のメッセージ — 最大300文字で、売り込みはしません。フックと、つながりたい理由を伝えます。
  • 最初のDM — 価値のある情報を伝えるか、鋭い質問をします。ここでも売り込みはしません。
  • フォローアップ — 返信またはシグナルがあった後に限り、控えめに依頼します。
  • 最後のメッセージ — 1行で、礼儀正しく、今後の可能性を残します。

その前に、心からコメントする価値のある最近の投稿を1~2件提案します。Hook 1で取得済みなので、追加のコストはかかりません。つながり申請の3日前に本物のコメントを残すほうが、どんな書き出し文より効果があります。

4. 返信への対応

まず分類してから、次のアクションに進みます。相手の文の長さと温度感に合わせます。2行の返信には、2行で返します。

返信の種類対応
興味がある具体的な日時を2つ提案します。それ以外は何も言いません。売り込みをやり直してはいけません。
質問1段落で率直に答え、その後に依頼をします。
反論トークトラック(下記)に進みます。
別の担当者を紹介された(「Xに話してください」)お礼を伝え、紹介または名前を出す許可を求めます。その後、紹介をフックにして新しいスレッドを始めます。
今は無理時期を確認します。「第1四半期に改めてご連絡してもよろしいですか?」と尋ね、その日付を実際に記録します。
明確なお断り礼儀正しく1行で返し、終了します。最後の売り込みはしません。
配信停止ただちに尊重します。代替案を提示したり、「確認ですが」と尋ねたりしてはいけません。すべてのシーケンスから削除します。
5. 反論への対応トーク

まず、本当の反論を見極めます。「高すぎる」は、たいてい「価値が明確でない」か「社内に推進役がいない」という意味です。「タイミングが悪い」は、たいてい「優先事項ではない」という意味です。答える前に、本当の理由はどれだと思うかを伝えます。

次に、受け止める → 捉え直す → 実際の証拠を一つ示す → 控えめに再提案する。証拠は一つだけにし、実在するものに限ります。具体的な状況や、根拠を説明できる数値を使います。事例を捏造してはいけません。

2つのバリエーションを用意します。直接的なもの(率直な買い手向け)と、ソフトなもの(まだ関係を築いている相手向け)です。

プレイブックモード:チームでよくある反論の上位8~10件を一つのドキュメントにまとめ、それぞれに診断、両方のバリエーション、避けるべき落とし穴を記載します。これはページとしてレンダリングする価値があります。ルールの「成果物に関するルール」を参照してください。

6. シーケンスビルダー
  1. まず、接触計画の表を作成します。日、チャネル、切り口、パーソナライズのレベルを記載します。接触のたびに切り口を変えます。課題 → 根拠 → 別の担当者の悩み → 第三者の評価 → 最後の連絡、という流れです。同じ考えを言い換えて5回送ってはいけません。
  2. 次に、タイプ1~3を使って各接触文をすべて作成します。
  3. 各接触に 1:1(営業担当者がカスタム文を作成)か merge-field(リスト全体に展開可能)かを明記し、営業担当者だけが知っている情報をどこに追加すべきかを正確に示します。
  4. フックの鮮度:1回目の接触では、調べたフックを使います。4回目の接触では、そのフックは2週間前のものです。再調査するか、情報の新しさに依存しない切り口に切り替えます。どちらにするかを表に記載します。

一般的な構成(営業活動に合わせて調整してください。そのまま送信してはいけません):

日チャネル切り口パーソナライズ
1メールフックと、そこから読み取れる課題1:1
3LinkedIn同じフックを使ったつながりの申請。売り込みはしない1:1
5電話+留守番電話電話の理由をフックにする1:1
8メールスレッド内で根拠を示すmerge-field
12メール別の担当者の視点から見た課題merge-field
18メール最後の連絡を一文でmerge-field
7. シグナルをトリガーにした下書き

入力:発生したばかりのシグナル(新たな資金調達、役員の採用、推進役の転職、採用の急増)。通常は sales-prospecting から引き継がれます。出力:実際のトリガーを発生日とともに取り上げ、上記のシグナル → 切り口の表に当てはめた下書きです。

これを効果的にするルールは、シグナルが発生した週に送信できる下書きでなければならないことです。6週間遅れの「資金調達おめでとうございます」というメールは、何も送らないより悪い結果になります。シグナルがすでに古い場合は、そのことを伝え、切り口を変えます。

シグナルを自動的に継続して取得するには、ユーザーに sales-prospecting の推進役トラッカーを案内します。これを使うと、定期的に監視する仕組みを設定できます。


手抜き禁止ルール(すべての下書きに適用。例外なし)

すべてのコピーについて、提示する前にこの確認を行います。AIっぽさがにじむ文面は、送らないより悪い結果になります。相手に、自分がリストに載っていたと伝えてしまうからです。

見つけたらすぐに削除します。

  • **ダッシュ記号(—)。**ピリオドかカンマを使います。
  • 「お元気でお過ごしのことと思います」「ご連絡したくて」「簡単な質問です」「その後いかがですか」「念のため再度ご連絡しました」。
  • 「掘り下げる」「活用する」「効率化する」「実現する」「高める」「シームレスな」「堅牢な」。
  • 3つ並べる構文(「より速く、より安く、より信頼性が高い」など)。
  • 否定を使った対句(「Xだけでなく、Yも」など)。
  • 不要な副詞:「本当に」「実際に」「まさに」「非常に」。
  • 親しげに見せかけた表現(「絶好調だといいですね!」など)や、根拠のない緊急性。
  • 会社名を検索して置換しても通用する文。どの会社にも使えるなら、どの会社にも通用しません。

最後に、声に出して読みます。賢い同僚に営業担当者がメッセージを送っているように聞こえる文面にします。下書きがテンプレートのように見えるなら、書き直します。手抜きの文面を送ってはいけません。


リストモード — 多数のアカウントを一度に処理

入力がリスト(通常は sales-prospecting でスコアリングされたリスト)の場合、順序は 常に「フック → コピー → エクスポート」です。逆ではありません。

ステップ 1 — フックを並列処理します。 人ごとに独立しているため、parallelMap で処理します。実行前に コストを伝えます。投稿は 1 件につき 1 クレジットなので、40 人に対して limit: 3 の場合、最大 120 クレジットです。

// user query: draft outreach for these 40 accounts — pull a hook for each first
const people = inputs.people; // [{ name, profileUrl, company, domain }]
const cutoffMs = Date.now() - 60 * 24 * 3600 * 1000;

const hooks = await parallelMap(people, async (p) => {
  const r = await callTool("social_post_list_live", {
    professional_network_profile_url: p.profileUrl,
    limit: 3,                                        // 1 cr/post x 3 x N people — quote this before running
    fields: ["text", "date_posted", "post_type", "share_url"],
  });
  if (!r.ok) return { name: p.name, hook: null, reason: r.message };
  const fresh = (r.data.posts ?? [])
    .map(x => ({ date: x.date_posted, ms: Date.parse(x.date_posted ?? ""), url: x.share_url, text: (x.text ?? "").slice(0, 300) }))
    .filter(x => !Number.isNaN(x.ms) && x.ms >= cutoffMs);
  return { name: p.name, company: p.company, hook: fresh[0] ?? null, reason: fresh.length ? null : "no recent post" };
});
checkpoint(hooks);
return {
  withHook: hooks.filter(h => h.hook),
  needCompanyFallback: hooks.filter(h => !h.hook).map(h => ({ name: h.name, company: h.company, reason: h.reason })),
};

needCompanyFallback に該当する人には、アカウントレベルでフック 2 またはフック 3 を使うか、 誠実な差し込み項目テンプレートを使います。取得結果が空でも、決して一行目を捏造してはいけません。

ステップ 2 — 連絡先のエンリッチメントはオプトインで、実行前にコストを確認します。 実行前に 最大コストを伝えてから、処理を実行します。

「40 人分のビジネスメールアドレス:最大約 40 クレジット(照合できた人 1 人につき 1 クレジット)。個人用メールアドレスと電話番号を追加すると、上限は 1 人あたり 5 クレジット、つまり約 200 クレジットになります。ビジネス用のみでよろしいですか?」

// user query: get business emails for the 40 people on the list (worst case ~40 credits, confirmed)
const urls = inputs.profileUrls;
const batches = chunk(urls, 25); // hard cap: 25 URLs per call
const results = await parallelMap(batches, async (batch) => {
  const r = await callTool("person_contact_enrich", {
    professional_network_profile_urls: batch,
    fields: ["contact.business_emails"], // narrow the request; see the cost note below
  });
  return r.ok ? r.data : batch.map(u => ({ matched_on: u, matches: [], error: r.message }));
});
const rows = results.flat();
return {
  contacts: rows.map(m => ({
    url: m.matched_on,
    email: m.matches?.[0]?.person_data?.contact?.business_emails?.[0]?.email ?? null,
    status: m.matches?.[0]?.person_data?.contact?.business_emails?.[0]?.status ?? null,
  })),
  unmatched: rows.filter(m => !m.matches?.length).map(m => m.matched_on),
};

fields を省略すると、3 つすべての階層(ビジネスメールアドレス、個人用メールアドレス、電話番号)が 要求され、照合できた人 1 人あたりの上限は 5 クレジットになります。実際に必要な階層だけに ホワイトリストを絞り込み、レスポンスの credits_remaining を確認して実際の消費量を把握します。 照合できなかった行は正直に報告します。推測したメールアドレスのパターンでリストを埋めてはいけません。

ステップ 3 — 引き継ぎます。 成果物は 2 つ。認証情報は含めず、自動送信もしません。

  • キャンペーン CSV:email, first_name, last_name, company, title, linkedin_url, hook_text, hook_date, hook_url, personalization_line, tier。営業担当者が送信前に各行を確認できるよう、 フック、その日付、およびフックへのリンクをファイルに含めます。
  • ユーザーが使用するシーケンサーに貼り付けるキャンペーン仕様書:各ステップの全コピーを含む シーケンスのステップ、使用する差し込み項目、送信時間帯とタイムゾーン、1 日あたりの送信数の 段階的な増加(新しいドメインでは 1 日 20~30 件から開始)、スレッド化(フォローアップは同じスレッド内で送信)、 および返信があった場合の停止をオンにする設定。
  • 仕様書に記載する到達率を守るためのガードレール:ドメインをウォームアップする、最初のメールには リンクを 1 つまでにする、HTML よりプレーンテキストを優先する、配信停止の要求には即時に対応する。

ルール

  • フックは最優先です。実在するか、存在しないかのどちらかです。 投稿、引用、資金調達ラウンド、共通の知人、導入事例を決して捏造しないでください。「この人について最近のシグナルは見つかりませんでした。代わりに役職に基づいた切り口をご紹介します」は、正直な回答として有効です。
  • 下書きと、担当者向けのメモにある参照情報には、すべて日付を付けてください。担当者が5秒で確認できるようにします。
  • 1回のアプローチにつき、フックは1つ、アイデアは1つ、依頼は1つです。巧妙さより簡潔さを優先してください。
  • 何も自動送信しないでください。 メールは下書きのままにします。このスキルがCRMに書き込むことはありません。ログを残す場合は、書き込みの前に必ずあなた自身で記録してください。シーケンサーの設定は仕様書とCSVで用意し、担当者自身が読み込んで開始します。
  • オプトアウトは完全かつ即座に尊重してください。 コンプライアンス上の上限を、配信量で押し切ってはいけません。ユーザーがそれを望む場合は、その責任を明示的に負うのはユーザーです。
  • すべての成果物に手抜き禁止ルールを適用します(上記の全項目)。emダッシュ、「delve」/「leverage」/「streamline」、埋め草を使わず、会社名を検索して置換した際に残るような内容も入れないでください。
  • コストは実際にかかります。投稿の取得、リストの拡張、連絡先情報の補完を行う前に、予想される費用を伝えてください。すべてのexecute応答にはcreditsとcredits_remainingが含まれます。account_creditsは無料で残高を報告します。
  • すべての成果物の最後に、引き継ぎ方法を尋ねる質問を付けてください。チャットに貼り付けるか、シーケンサー用のCSVとキャンペーン仕様書にするか、レンダリングしたドキュメントにするかを確認します。
  • レイアウトはコンテンツに合わせて調整し、何も隠さないでください。 ブランドシステムは固定ですが、レイアウトは固定ではありません。実際のコンテンツが収まらない場合、長い会社名や人名、12語のタイトル、200行などに合わせてコンテンツを変更するのではなく、レイアウトを変更してください。カードを拡大する、省略せず折り返す、1列にする、列幅を広げる、上限を上げる、幅広のコンテンツ専用にスクロール領域を設けるなどの方法があります。カードを切り詰めたり、名前を省略したり、行を黙って削除したりして、表示上の問題を解決してはいけません。上限がどうしても必要な場合は、UIに「214件中、上位50件を表示」と明記してください。引き渡す前にレンダリング結果を確認し、途切れている部分を修正してください。
  • レンダリングした成果物のアイコン:ダッシュボードのアイコンセットであるLucideを、currentColorのストロークを使ったインラインSVGとして使用してください。成果物のUIに絵文字は使わないでください。
  • 写真とロゴは無料です。レンダリングした成果物に使用してください。 basic_profile.profile_picture_permalink(人物)は、渡される人物レコードのbasic_profileグループに含まれています。basic_info.logo_permalink(会社)は、company_identifyとcompany_enrichのbasic_infoから追加クレジットなしで取得できます。どちらもdata:image/jpeg;base64,... URIとしてBase64でインライン化してください。メディアCDNはbinary/octet-streamとして配信するため、リモートの<img src>は空白で表示されます。画像がない場合は、モノグラムを代わりに表示してください。
  • 成果物のブランディング:コンテンツはデフォルトでチャット内に表示し、CSVはプレーンなデータファイルのままにします。成果物は、レンダリングすること自体を目的にレンダリングしてはいけません。ただし、ページやドキュメント(異議対応プレイブック、シーケンス仕様書、キャンペーン概要など)としてレンダリングする場合は、ヘッダーまたはフッターにCrustdataのブランドロックアップを配置してください。小さな大文字の「Powered by」の上付きラベルと、crustdata.comにリンクする正式なCrustdataワードマークを含めます。ワードマークの組み合わせは、このスキルのassets/にあります。ライト背景用(濃色テキスト)のcrustdata-logo-light.pngと、ダーク背景用(白色テキスト)のcrustdata-logo-dark.pngです。テーマに合ったバリアントをBase64でインライン化し、高さは約17pxにしてください。ホットリンクは使わないでください。レンダリングした成果物からリモート画像を取得することはできません。ブランドアクセントカラーは#5547E2(ダーク背景では#8387FF)です。埋め込み可能な場合は本文フォントにGeistを使用し、それ以外の場合はシステムフォントスタックを使ってください。

エラー処理

  • 危険なのは明らかなケースではなく、呼び出しに失敗した場合です。 投稿の取得後に r.ok === false となった場合、「この人は投稿していない」ように見えます。必ず条件分岐してエラーとして報告し、Hook 2 または Hook 3 にフォールバックしてください。パーソナライズを装った汎用的な書き出しにしてはいけません。
  • フィールド名を示す403 =プランによって制限されたプロジェクションです。fields からそのフィールドを除外して再実行してください。エンドポイント名を示す403(よくあるのはコンタクト情報のエンリッチメントです)=そのプランには含まれていません。その旨を明確に伝え、メールアドレスを推測するのではなく、メールアドレスなしのリストを引き渡してください。
  • 投稿が空、ニュースが空、募集中の職種がないのは通常の結果であり、失敗ではありません。空だったソースを報告し、上記のフックなしの手順に進んでください。
  • 1つのドメインに対して company_identify が複数の候補を返すのは想定どおりです。ファジーマッチングだからです。最も高い confidence_score の候補を選んでください。2つの候補のスコアが近い場合は両方を挙げて確認を求めてください。
  • リスト処理で一致しなかった行は、削除したりテンプレート行に黙って差し替えたりせず、名前を挙げて報告してください。

コストの早見表

呼び出しコスト
company_identify、オートコンプリート、get_schema、account_credits無料
social_post_list_live投稿1件につき1 cr — limit で利用額を調整します
IDを指定した company_enrich + exact_match: true一致1件につき2 cr
job_search1件あたり約0.03 cr。limit: 1 + total_count なら約0.03 cr
person_contact_enrich基本料金なし。一致した人物ごとに返される連絡先の種類に応じて課金されます(業務用メールアドレス1、個人用メールアドレス2、電話番号2)。1人あたり最大5 cr。fields を絞ることで最大額の目安は設定できますが、上限が保証されるわけではありません。1つの階層のみをリクエストしたにもかかわらず、5クレジットの上限額が課金され、3つすべての階層が返されたケースが確認されています。そのため、1人あたり5 crを上限として見積もり、実際の利用額は credits_remaining で確認してください
執筆、分類、シーケンス作成無料 — すべてモデル処理です

フックを適切に盛り込んだコールドメール1通のコストは、通常3~5クレジットです。フックと業務用メールアドレスを含む40人分のシーケンスなら、約150~200クレジットです。

連携先

  • sales-prospecting — リストを作成し、このスキルが活用するシグナルを取得します。champion-trackerの行は、ここで最もコンバージョン率の高い入力です。
  • account-research — 大型案件のアプローチ前にアカウントを深掘りします。このスキルのアカウントプランは、ブリッジの文面に必要なペインマップを提供します。
  • icp-builder — LinkedIn URL 1つから config/persona-profile.md(文体)と config/gtm-config.md(販売するもの)を作成するため、最初の実行から担当者らしい文面になります。
  • warm-path-workspace — 紹介経由の温かいルートがある場合は活用してください。紹介を切り出しに使うほうが、どんなコールドメールよりも効果的です。

ツールの依存関係

このスキルには以下が必要です。

  • Crustdata MCP server(install.crustdata.com/mcp):list_tools、get_schema、execute を公開する単一の Code Mode MCP です。すべての Crustdata データツールは、await callTool(name, params) を介して execute({ code }) のプレーンな JavaScript スクリプト内で呼び出します。get_schema の型付きインターフェースに基づいて実装しますが、スクリプト本文には型注釈を一切含めないでください(型注釈があると構文解析エラーになり、実行全体が失敗します)。ここで使用するツール:social_post_list_live、company_identify、company_enrich、job_search、person_contact_enrich(オプトイン、コスト確認済み)、account_credits。
  • その他は不要です。 シーケンサーの認証情報、メール送信へのアクセス、CRMへの書き込みアクセスは必要ありません。このスキルからのアウトバウンドは、文案、CSV、記述されたキャンペーン仕様として出力されます。担当者が読み込んで開始します。

コメント