🟡 HubSpot Operations Hub(Data Hub)実践教科書 — 2026年版
Chapter 3

プログラマブル自動化
JavaScript / Python でワークフローを拡張する

ノーコードのワークフローアクションでは「あと一歩」届かない処理がある——複雑な計算ロジック・外部 API との通信・複数プロパティを組み合わせた条件判定・HubSpot の標準アクションにない独自ビジネスルール。本章では カスタムコードアクションの仕組み・JavaScript と Python(Beta)の使い方・実務で頻出の10パターンのコードサンプル・セキュリティとエラーハンドリング・本番運用のベストプラクティスを体系的に解説する。

📖 読了目安 30分
🎯 対象:HubSpot 管理者・RevOps エンジニア・開発者
🔧 必要プラン:Operations Hub Professional 以上

📋 この章の内容

  1. 3-1カスタムコードアクションの仕組みと JavaScript / Python の違い
  2. 3-2基本構文とプロパティの読み書き(JavaScript 編)
  3. 3-3Python サポート(Beta)——導入と基本パターン
  4. 3-4実務頻出10パターン——スコアリング・API 連携・日付計算ほか
  5. 3-5エラーハンドリング・セキュリティ・本番運用のベストプラクティス
Section 3-1

カスタムコードアクションの仕組みと JavaScript / Python の違い

カスタムコードアクション(Custom Code Action)はワークフローの中に JavaScript または Python のコードブロックを埋め込み、HubSpot のサーバーレス環境(AWS Lambda 相当)上で実行させる機能だ。外部サーバーを用意せず、HubSpot ポータルのワークフロー UI 上でコードを書くだけで高度な処理が実現できる。

⚙️ カスタムコードアクションの実行アーキテクチャ
🔔
ワークフロー
トリガー条件を満たす
📥
Input 設定
プロパティ値を変数に渡す
💻
コード実行
JS / Python(サーバーレス)
📤
Output 書き戻し
HubSpot プロパティを更新
➡️
次のアクションへ
WF 継続・分岐
実行環境は HubSpot マネージドのサーバーレス基盤(外部サーバー不要)。タイムアウト上限:20秒(JS)/ 30秒(Python)

JavaScript vs Python(Beta)——どちらを選ぶか

比較項目JavaScript(Node.js)Python(Beta)
提供状況 ✓ GA(正式リリース・安定) △ Beta(2025年〜、本番利用は要注意)
Node / Python バージョン Node.js 18.x Python 3.9
標準ライブラリ Node.js 標準モジュール(https, crypto など) Python 標準ライブラリ + requests, pandas, numpy
向いている用途 API 連携・JSON 操作・文字列処理・非同期処理 データ変換・統計計算・pandas を使ったデータ加工
推奨ユーザー フロントエンドエンジニア・RevOps 全般 データエンジニア・アナリスト・Python 経験者
タイムアウト上限 20秒 30秒
secrets(環境変数) ✓ 対応 ✓ 対応
💡 2026年時点での推奨:JavaScript を基本に、Python は数値計算に限定

Python サポートはまだ Beta のため、本番の重要なワークフローには JavaScript を推奨する。大量の数値データを pandas で加工したい・機械学習モデルと連携したいといった特定ニーズがある場合のみ Python を選択し、それ以外は JavaScript で統一するとトラブルシューティングが容易になる。

Section 3-2

基本構文とプロパティの読み書き(JavaScript 編)

カスタムコードアクションの JavaScript には決まった構造がある。入力(inputFields)からプロパティ値を受け取り、処理を行い、出力(outputFields)に書き戻すという3ステップだ。まずこの基本パターンを完全に理解することが出発点になる。

JavaScript 基本構造テンプレート
// =================================================== // HubSpot カスタムコードアクション — 基本テンプレート // =================================================== // 必須:exports.main が実行エントリーポイント exports.main = async (event, callback) => { // --- INPUT: ワークフローの inputFields から値を取得 --- const email = event.inputFields['email']; const firstname = event.inputFields['firstname']; const company = event.inputFields['company']; const contactId = event.object.objectId; // レコードの ID // --- PROCESS: ビジネスロジックの実装 --- const domain = email.split('@')[1]; // メールからドメイン抽出 const greeting = `${firstname} 様、${company} のご担当者`; // --- OUTPUT: 結果を outputFields に書き出す --- callback({ outputFields: { email_domain: domain, // カスタムプロパティに書き込む greeting_text: greeting } }); };

Input / Output の設定方法(UI 側の操作)

コードを書く前に、ワークフローエディター上で「Input Fields(入力)」と「Output Fields(出力)」を事前に宣言する必要がある。Input Fields ではどのプロパティをコードに渡すかを選択し、Output Fields では「コードが返す変数名」と「書き込み先の HubSpot プロパティ」をマッピングする。

設定項目場所設定内容注意点
Input Fields コードエディター左パネル「Inputs」 コードに渡したいプロパティを選択(例:email, company, custom_score) 宣言していないプロパティは event.inputFields で取得できない
Output Fields コードエディター左パネル「Outputs」 変数名(例:total_score)と書き込み先プロパティ(例:カスタムプロパティ「total_score」)をマッピング コード内の outputFields のキー名と Output 宣言の変数名を完全一致させること
Secrets(環境変数) Settings → Private App / Secrets API キー等の機密情報を「シークレット名」で登録し、コード内で process.env.シークレット名 で参照 コード内に API キーを直書きしない。GitHub に push した瞬間に漏洩する。
Section 3-3

Python サポート(Beta)——導入と基本パターン

2025年から提供が始まった Python サポートは、データエンジニアやアナリストにとって大きな追加だ。pandas・numpy・requests などのライブラリがプリインストールされており、CSV データの変換やデータフレームを使った集計がワークフロー内で直接実行できる。

Python 基本構造テンプレート(Beta)
# =================================================== # HubSpot カスタムコードアクション — Python テンプレート(Beta) # =================================================== import os import json import requests # プリインストール済み # 必須:main 関数が実行エントリーポイント def main(event): # --- INPUT --- email = event["inputFields"].get("email", "") score = event["inputFields"].get("lead_score", 0) contact_id = event["object"]["objectId"] # --- PROCESS --- domain = email.split("@")[-1] if "@" in email else "" tier = "High" if int(score) >= 80 else ("Mid" if int(score) >= 50 else "Low") # --- OUTPUT --- return { "outputFields": { "email_domain": domain, "lead_tier": tier } }
JavaScript (Node.js 18)
実行制限と仕様
タイムアウト20秒
メモリ上限128 MB
実行上限(月)10万回(Pro)/ 無制限(Ent)
外部 HTTP 通信可(https モジュール / fetch)
ファイルシステム読み取り不可(/tmp のみ書き込み可)
npm パッケージ不可(標準モジュールのみ)
Python 3.9(Beta)
実行制限と仕様
タイムアウト30秒
メモリ上限256 MB
実行上限(月)10万回(Pro)/ 無制限(Ent)
外部 HTTP 通信可(requests ライブラリ)
プリインストールrequests, pandas, numpy, json
pip install不可(プリインストールのみ)
Section 3-4

実務頻出10パターン——スコアリング・API 連携・日付計算ほか

以下に、実際の HubSpot 運用で最もよく使われるカスタムコードアクションのパターンをまとめる。各パターンはそのままコピーして使える実用的なサンプルだ。

パターン 1
複合スコアリング計算
複数プロパティ(業種スコア・会社規模スコア・エンゲージメントスコア)を重み付けして合算し、カスタムスコアプロパティに書き込む。ノーコードの「コンタクトスコア」では難しい複雑な加重計算に対応。
使いどころ:独自の ICP スコアリングモデルを実装したい・業種別に重み係数を変えたいとき
パターン 2
外部 API への問い合わせ(エンリッチメント)
Clearbit・ZoomInfo・Apollo などのエンリッチメント API に HTTP リクエストを送り、返ってきた業種・会社規模・LinkedIn URL などを HubSpot プロパティに書き込む。
使いどころ:HubSpot ネイティブのエンリッチメントでカバーされていない API と連携したいとき
パターン 3
日付計算・経過日数の算出
「契約開始日から何日経過したか」「トライアル終了まで何日か」「最終購入日から今日まで何日か」を計算してプロパティに書き込む。WF の「日数経過後」トリガーと組み合わせてタイムリーなアクションを実現。
使いどころ:チャーン予測・更新リマインダー・アップセルタイミング検知
パターン 4
Slack への直接通知
HubSpot ネイティブの Slack 連携より細かい制御が必要な場合に、Incoming Webhook URL に POST して特定チャンネルに構造化メッセージを送る。案件金額・担当者・リンクを含むリッチ通知を実現。
使いどころ:大型案件アラート・特定条件のコンタクト作成通知・日次サマリー送信
パターン 5
テキストの正規化・フォーマット
名前の敬称除去(「山田 様」→「山田」)・全角半角変換・URLから会社名推定・メールドメインからの業種分類など、ノーコードの「データ形式を整える」では対応できない複雑なテキスト変換を実装。
使いどころ:既存データの大規模クレンジング・フォーム入力の高度な正規化
パターン 6
HubSpot API を使ったレコード横断更新
コンタクトに紐づくすべての関連会社・商談・チケットを取得し、それらを一括更新する。標準アクションでは1つのレコードしか操作できないが、API 経由で関連レコードをまとめて処理できる。
使いどころ:顧客が解約したときに関連するすべての商談・チケットを一括クローズしたいとき
パターン 7
JSON プロパティのパースと展開
外部システムから送られてきた JSON 文字列プロパティをパースして、個々のフィールドを別プロパティに展開する。Webhook 受信データの構造化や、ERP から送られてきた構造化データの分解に使う。
使いどころ:Zapier・Make などのノーコードツールから JSON で送られてきたデータを扱うとき
パターン 8
ラウンドロビン担当者割り当て
コンタクト数・担当件数・テリトリーに基づいて次の担当者を決定し、コンタクトオーナーを設定する。HubSpot の標準ロールーティング WF より複雑な割り当てルール(シフト・スキル・地域)を実装できる。
使いどころ:営業チームの公平な割り当て・専門スキルが必要なリードの適切なルーティング
パターン 9
外部 DB / スプレッドシートとの照合
外部 API 経由で Google Sheets・Airtable・社内 DB から情報を取得し、HubSpot のコンタクト・会社情報と照合して差分を更新する。HubSpot に連携されていない情報ソースとのリアルタイム同期に使う。
使いどころ:価格表の参照・在庫確認・社内マスターデータとの整合チェック
パターン 10
条件付き複数プロパティの一括設定
複雑な IF-ELSE ロジック(業種×規模×地域の組み合わせに基づいてプラン推奨・担当チーム・SLA を一度に設定する)をコードで実装し、複数プロパティをまとめて更新する。
使いどころ:セグメントに基づく複数フィールドの同時設定・ICP マトリクスに基づく分類

パターン 1:複合スコアリングの実装例

JavaScript 複合スコアリング計算(業種・規模・エンゲージメント)
exports.main = async (event, callback) => { // Input Fields: industry, num_employees, hs_email_open_count, num_contacted_notes const industry = event.inputFields['industry'] || ''; const employees = parseInt(event.inputFields['num_employees'] || 0); const emailOpens = parseInt(event.inputFields['hs_email_open_count'] || 0); const contactedNotes= parseInt(event.inputFields['num_contacted_notes'] || 0); // ① 業種スコア(0〜30点) const industryScores = { 'Technology': 30, 'Finance': 28, 'Healthcare': 25, 'Manufacturing': 20, 'Retail': 15, 'Education': 10 }; const industryScore = industryScores[industry] || 5; // ② 会社規模スコア(0〜30点) let sizeScore = 0; if (employees >= 1000) sizeScore = 30; else if (employees >= 200) sizeScore = 22; else if (employees >= 50) sizeScore = 15; else if (employees >= 10) sizeScore = 8; // ③ エンゲージメントスコア(0〜40点) const engagementScore = Math.min(40, (emailOpens * 3) + (contactedNotes * 5)); // ④ 合計スコアとティア分類 const totalScore = industryScore + sizeScore + engagementScore; const tier = totalScore >= 70 ? 'A' : totalScore >= 45 ? 'B' : 'C'; callback({ outputFields: { icp_score: totalScore, // カスタムプロパティ: icp_score(数値) icp_tier: tier // カスタムプロパティ: icp_tier(文字列) } }); };

パターン 4:Slack Webhook 通知の実装例

JavaScript Slack Incoming Webhook 通知(大型案件アラート)
const https = require('https'); exports.main = async (event, callback) => { const dealName = event.inputFields['dealname']; const amount = event.inputFields['amount']; const ownerName = event.inputFields['hubspot_owner_id']; const dealId = event.object.objectId; // Secrets に登録した Webhook URL を参照(コードに直書きしない!) const webhookUrl = process.env.SLACK_DEALS_WEBHOOK_URL; const payload = { text: `🎯 大型案件アラート`, blocks: [ { type: "section", text: { type: "mrkdwn", text: `*${dealName}* が Closed Won になりました!\n 💰 金額:¥${Number(amount).toLocaleString()}\n 👤 担当:${ownerName}\n 🔗 <https://app.hubspot.com/deals/${dealId}|HubSpot で確認>` } } ] }; // HTTPS POST で Slack に送信 await new Promise((resolve, reject) => { const body = JSON.stringify(payload); const url = new URL(webhookUrl); const req = https.request({ hostname: url.hostname, path: url.pathname, method: 'POST', headers: { 'Content-Type': 'application/json' }}, (res) => res.on('end', resolve)); req.on('error', reject); req.write(body); req.end(); }); callback({ outputFields: { slack_notified: 'true' } }); };
Section 3-5

エラーハンドリング・セキュリティ・本番運用のベストプラクティス

カスタムコードアクションは強力だが、エラーが発生したときにワークフロー全体が止まる・機密情報が漏洩する・実行コストが爆発するといったリスクも伴う。本番環境への展開前に以下のベストプラクティスを必ず確認する。

⚠️ よくあるエラーパターンと対処法
本番運用前に必ず把握しておくべき5つのエラー
タイムアウトエラー
Error: Task timed out after 20.00 seconds
外部 API のレスポンスが遅い・ループ処理が無限ループになっている場合に発生。対処:API 呼び出しに timeout(10秒以下)を設定する。ループには必ず終了条件を設け、最大イテレーション数(例:100回)を上限として設ける。
Null / Undefined エラー
TypeError: Cannot read property 'split' of null
Input Fields に宣言したプロパティが空欄のレコードでコードを実行したときに発生。対処:すべての入力値に「|| ''」や「|| 0」でデフォルト値を設定する。処理の前に必ず null チェックを行う。
API レート制限エラー
Error: 429 Too Many Requests
外部 API(Clearbit・ZoomInfo・HubSpot 自身の API など)に短時間で大量リクエストした場合に発生。対処:ワークフローに「コードアクションの前に1秒待つ」遅延アクションを挿入。コードアクションを一度に大量のレコードに実行するバルク適用を避ける。
JSON パースエラー
SyntaxError: Unexpected token in JSON
外部 API が JSON 以外(HTML エラーページなど)を返したときに JSON.parse() が失敗する。対処:try-catch で JSON.parse() を囲む。レスポンスのステータスコードを確認してから parse する。
シークレット未設定エラー
TypeError: Cannot read property of undefined (process.env.API_KEY)
Secrets に登録されていない環境変数を参照したときに発生。対処:コードをデプロイする前に Settings → Private App Tokens / Secrets で必要なシークレットをすべて登録する。開発・本番でシークレット名を統一する。

セキュリティのルール(必ず守ること)

ルール悪い例(NG)良い例(OK)
API キーの管理 コード内に直書き:
const key = "sk-abc123..."
Secrets に登録して参照:
process.env.OPENAI_KEY
HubSpot API トークン ハードコード or 広権限の Private App Token を使う 必要なスコープのみを持つ専用 Private App Token を作成して Secrets に保存
エラーログの内容 console.log(apiKey) でシークレット情報をログに出力 ログには変数名やエラーメッセージのみ出力。シークレット値は絶対にログに出さない
外部通信先 ユーザー入力値をそのまま URL に組み込む(SSRF のリスク) 通信先 URL はハードコードまたは許可リストと照合してから使用

本番運用のベストプラクティス

① テストは必ずサンドボックスで行う——本番ポータルでいきなりコードを実行するのは厳禁。HubSpot のサンドボックス(Developer Sandbox)でまず動作確認してから本番に適用する。

② WF 名・コードにコメントをつける——「誰が・いつ・なぜこのロジックを書いたか」をコメントで残す。3ヶ月後に自分でも忘れる。他の管理者が引き継いだときに解読不能になる。

③ 実行ログを定期的に確認する——ワークフローの「実行履歴」からカスタムコードアクションの成功・失敗・エラーメッセージを確認できる。月次でエラー率を確認し、エラー率 1% 超えたら原因調査する。

④ 実行回数の上限(10万回/月)に注意する——Professional プランはカスタムコードアクションの実行が月10万回まで。大量のレコードに一括適用する際は事前に件数を確認する。残り回数は「設定 → 使用状況」で確認できる。

✅ カスタムコードアクション導入前のチェックリスト

① Input / Output Fields を UI で宣言したか / ② すべての入力値に null チェック・デフォルト値を設定したか / ③ API キー等の機密情報を Secrets に登録して process.env で参照しているか / ④ try-catch でエラーをハンドリングしているか / ⑤ サンドボックスでテスト実行して意図どおりの結果が得られたか / ⑥ 本番適用する前にワークフローの「テストコンタクト」機能で1件だけ試したか——この6項目を全部クリアしてから本番展開する。

📌 第3章 まとめ

カスタムコードは「ノーコードの限界」を超える最後の手段

標準アクションでは対応できない複雑な計算・外部 API 通信・複数プロパティの条件分岐をコードで実装できる。ただし「まず標準アクションで実現できないか」を確認してから使う。メンテナンスコストが高いため乱用は避ける。

JavaScript(GA)を本番に使い、Python(Beta)は限定的に

JavaScript は安定して本番利用できる。Python は pandas を使った数値計算が必要なケースに限定して使う。Beta 機能のため重要なワークフローでの Python 利用はリスクを理解した上で判断する。両者ともタイムアウト・メモリ制限を把握しておく。

API キーは絶対に直書きしない——Secrets に必ず登録する

コードに API キーをハードコードすることは最大のセキュリティリスク。コードはワークフロー管理者全員が閲覧できる。すべての機密情報は Secrets に登録して process.env.XXX で参照する。これは「推奨事項」ではなく「絶対ルール」。

null チェックと try-catch はすべてのコードの必須要件

HubSpot には空欄プロパティのレコードが必ず存在する。null/undefined のまま処理すると TypeError が発生してワークフロー全体が止まる。全入力値のデフォルト値設定と、外部 API 呼び出しの try-catch を怠らないこと。

サンドボックスで確認してから本番へ——一括適用は慎重に

カスタムコードアクションのバグは、バルク適用した瞬間に数万件のレコードに誤った値を書き込む可能性がある。必ずサンドボックスで動作確認し、本番では1件→10件→100件と段階的に拡大して動作を確認してから全件に適用する。

実行10万回/月の上限を意識して設計する

Professional プランの月間実行上限(10万回)は意外に早く消費する。「すべてのコンタクト作成時にコードを実行する」設計だと、月1万件の新規流入だけで10万回に近づく。必要最低限のトリガー条件を絞り込んで、コードアクションの起動頻度を最小化する。

Next Chapter
第4章:Data Studio——ノーコードでデータを統合・変換する →