HubSpot を運用していると必ず直面するのが重複リード(コンタクト)の問題です。同じ人がフォーム経由とインポート経由で別レコードになっている、表記揺れで名寄せされていない——こうした重複を放置するとメール配信もスコアリングも狂います。

結論から言うと、API を使って 2 つのコンタクトをマージすることは可能です。HubSpot の API にはコンタクトのマージ用エンドポイントが用意されており、マージ機能自体は Starter プランでも利用できます(API 実行には Private App の作成権限 = Super Admin が必要)。

ただし、「AI エージェントだけで全自動で重複を検知・マージまで完結させる」のは、安全性の観点から少しハードルが高いです。マージは一発勝負でやり直しが効かないからです。

現実的かつ賢いやり方は、「AI に重複を見つけさせてリスト化させ、マージ自体は API で行う(または人間が承認する)」というシステムを組むことです。本記事では具体的な仕組みと API の実装方法を解説します。

AI × API で実現する「自動重複マージ」の設計図

全自動で裏でマージしてしまうと、同姓同名の別人(例:「佐藤一郎」さんという別の会社の人)をマージしてしまうリスクがあります。そのため、以下のような 3 ステップの仕組みを作るのが一般的です。

AI が重複候補を検知してリスト化し、Slack 等で人間に確認を求め、承認されたペアだけ HubSpot API でマージを実行する 3 ステップのフロー図。全自動にしない理由としてマージが取り消し不可である点を注記

  1. Step 1: AI が検知 — AI(またはスクリプト)が「名前が一致、かつ会社名や電話番号が類似」しているリードを抽出
  2. Step 2: 人間が確認 — Slack や社内ツール、Google スプレッドシート等に「この 2 人、マージして大丈夫ですか?」と AI が通知
  3. Step 3: API で実行 — 人間が「承認(OK)」ボタンを押したら、システムが HubSpot API を叩いて安全にマージを実行

Step 1「AI が検知」を分解する

「AI が検知」は実際には 4 つの工程に分かれます。すべてを LLM にやらせるのではなく、機械的に絞り込めるところはスクリプトで処理し、あいまいな判断だけを LLM に渡すのがコストと精度のバランスを取るコツです。

1-1. コンタクトデータの取得

CRM API でコンタクトを比較対象プロパティ付きで取得します(ページネーション必須。また 1 クエリあたり 10,000 件の取得上限があるため、それを超える規模では createdate 等でのセグメント分割が必要です)。

1
2
3
curl --request GET \
  --url 'https://api.hubapi.com/crm/v3/objects/contacts?limit=100&properties=email,firstname,lastname,company,phone,hs_object_id' \
  --header 'authorization: Bearer YOUR_ACCESS_TOKEN'

前提として、HubSpot はコンタクトをメールアドレスで重複排除します(フォーム送信などはメール完全一致で自動名寄せ)。つまり AI が探すべきは「メールは違う(または片方欠損)が同一人物らしい」あいまい重複だけです。

1-2. 正規化と候補ペアの絞り込み(ブロッキング)

全組み合わせを比較すると 1 万件で約 5,000 万ペアになり破綻するため、先に決定的ルールで候補を絞ります。

  • 正規化: 全角/半角・大文字小文字・カナ/かなの統一、電話番号の記号除去(03-1234-56780312345678)、会社名の「株式会社/(株)」除去
  • ブロッキングキー: 「姓 + 会社名」「電話番号下 8 桁」「メールのドメイン + 名」などが一致するペアだけを候補に残す

ここまでは AI ではなく普通のスクリプト(Python の pandas + rapidfuzz 等)で十分かつ高速です。

1-3. LLM による同一人物判定

絞り込んだ候補ペアだけを LLM に渡し、構造化された判定をさせます。ここが「AI が検知」の核心です。

1
2
3
4
5
6
7
以下の 2 つの CRM コンタクトが同一人物か判定してください。

レコードA: {"id":"101","name":"佐藤 一郎","company":"株式会社ABC","email":"i.sato@abc.co.jp","phone":"03-1234-5678"}
レコードB: {"id":"202","name":"サトウ イチロウ","company":"ABC(株)","email":null,"phone":"0312345678"}

JSON で回答: {"is_duplicate": true/false, "confidence": 0-100,
"reason": "判定根拠", "suggested_primary": "残すべき ID と理由"}

LLM が効くのは、表記揺れ(漢字/カナ)、旧姓・転職、会社名の略記などルールで書き切れない判断です。逆に「同姓同名だが会社が違う」ペアに is_duplicate: false と根拠付きで答えられるのも利点です。コストは 1-2 の絞り込みで候補が数百ペア程度になっていれば現実的な範囲に収まります。

1-4. primary の提案とリスト化

マージは「どちらを残すか」の判断も必要なので、AI に primaryObjectId の提案までさせます。判断基準の例:

  • プロパティの充足度が高い方(メール・電話が埋まっている)
  • ライフサイクルステージが進んでいる方(商談あり > リード)
  • 直近のアクティビティがある方

最終出力は「ペア + confidence + 根拠 + primary 案」の表で、これがそのまま Step 2 の承認依頼(Slack 通知やスプレッドシートの行)になります。confidence でしきい値を切り、高確信のみ通知・低確信は無視とするとノイズが減ります。

なお、コードを書かずに済ませたい場合は HubSpot 標準の「重複管理ツール」も類似レコードのサジェストを行います(個別の重複管理は Professional 以上、一括処理は Data Hub Professional 以上)。ただし判定根拠の説明・primary の自動提案・Slack 承認フローまで組めるのが自作 AI パイプラインの利点です。

開発者向け: マージを実行する HubSpot API

HubSpot の最新の CRM API(v3)には、コンタクトをマージするための専用エンドポイントがあります。これを使うと、管理画面で手動マージしたときとほぼ同等の処理(プロパティ・アクティビティ履歴の統合など)が行われます。なおマージは非同期処理で、200 応答は「受理」を意味し、完了まで数秒かかる場合があります。

エンドポイント(POST)

1
https://api.hubapi.com/crm/v3/objects/contacts/merge

リクエストボディ(JSON データ)

システムから HubSpot に送るデータ(ペイロード)の形式です。

1
2
3
4
{
  "primaryObjectId": "101",
  "objectIdToMerge": "202"
}
  • primaryObjectId: 最終的に「メインとして残す側」のコンタクト ID
  • objectIdToMerge: メインに吸収されて「消える側(重複側)」のコンタクト ID

cURL での実行例

1
2
3
4
5
6
7
8
curl --request POST \
  --url https://api.hubapi.com/crm/v3/objects/contacts/merge \
  --header 'authorization: Bearer YOUR_ACCESS_TOKEN' \
  --header 'content-type: application/json' \
  --data '{
    "primaryObjectId": "101",
    "objectIdToMerge": "202"
  }'

マージで何が統合されるか — アクティビティ・関連付け・プロパティ

「マージされたコンタクトのアクティビティはどうなるのか?」はよく聞かれるポイントです。結論: アクティビティもマージされます公式ヘルプ(Merge records)に基づく挙動は次のとおりです。

項目マージ後の挙動
タイムラインアクティビティメール・コール・ミーティング・メモ等、両レコードのアクティビティがすべて統合後レコードのタイムラインに表示される(同期完了まで最大 30 分かかることがある)
関連付け(アソシエーション)両者に紐づく会社・取引・チケット等はすべて引き継がれる。「Primary」ラベル付きの会社関連付けは primary レコード側が優先
メールアドレス消える側のメールは統合先のセカンダリメールとして保持される
プロパティ値原則 primary 側の値が優先され、primary 側が空のプロパティは secondary の値で補完。例外: ライフサイクルステージはより進んでいる方、作成日は古い方、フォーム送信数・コンバージョン数は合算、マーケティングコンタクトステータスは「より marketable」な方
Record IDマージ結果のレコードには新しい Record ID が発行される。旧 2 つの ID は Merged record IDs プロパティで参照できる
リスト(セグメント)secondary は全静的セグメントから除外される。アクティブリストはデータ変化に応じて再評価
ワークフロー両レコードとも全ワークフローから登録解除される。マージ起因のデータ変化では自動再登録されない(設定で許可は可能)
Salesforce 連携primary のみ同期を継続。secondary が同期していた Salesforce 側レコードは同期が切れる

システム設計上、特に注意したいのは 2 点です。

  • Record ID が変わる — Step 1 で作った重複候補リストの ID を長く持ち回す設計だと、マージ実行後に手元の ID が古くなります。マージ後は Merged record IDs で追跡するか、リストを都度作り直す前提にしましょう
  • マージは取り消し不可(unmerge 不可) — アクティビティが混ざった後で「別人だった」と分かっても自動分離の手段はありません。人間承認を挟む設計(Step 2)の最大の理由です

API や外部ツールを使う際の注意点

  1. マージ制限(上限)に注意 HubSpot の仕様上、マージしようとする 2 つのレコードの合算マージ回数が 250 回以上になるとエラーになります(例: 双方が 130 回ずつマージに関与していると合算 260 回で上限超過)。通常の運用では滅多に引っかかりませんが、テスト等で同じレコードを何度も使い回す際は注意してください。

  2. サードパーティツールの検討(ノーコード派向け) 自社で API を組むのが難しい場合、HubSpot のアプリマーケットプレイスにある『Koalify』(無料枠あり)や『Insycle』(有料)といった、重複管理に特化した外部アプリを連携させるのも一つの手です。柔軟な重複ルールを設定できます(対応する HubSpot プランは各アプリのマーケットプレイス掲載を確認してください)。

まとめ

  • HubSpot の重複コンタクトは CRM API v3 のマージエンドポイントでプログラムから統合できる(Starter プランでも利用可)
  • マージは取り消し不可の一発勝負なので、全自動化はリスクが高い
  • AI が検知 → 人間が承認 → API が実行」の 3 ステップ設計が安全性と効率のバランスが良い
  • マージ回数の合算上限(250 回)に注意。ノーコードで済ませたい場合は Koalify / Insycle などの専用アプリも選択肢

Python や Node.js での重複検知の実装、Zapier などの iPaaS での構成など、具体的な組み方はニーズに応じて発展させていけます。

参考リンク