🟢 HubSpot 開発者向け実践教科書 — 2026年版 Developer Edition
Chapter 4  ·  Custom Objects & Schema Design

カスタムオブジェクト &
スキーマ設計

標準オブジェクトでは表現できないビジネスデータを、カスタムオブジェクトとして HubSpot に定義する。 オブジェクト設計の考え方・プロパティ型の選択・アソシエーション設計・Enterprise 専用機能まで実践的に学ぶ。

Enterprise プラン必須
Schemas API v3
所要時間:約90分
4-1 カスタムオブジェクトとは

標準オブジェクトの限界を理解し、カスタムオブジェクトで解決できる課題を整理する。

📦 標準オブジェクト

HubSpot に組み込まれた固定のオブジェクト。

Contacts / Companies / Deals / Tickets / Products / Line Items / Quotes など。 ほとんどのビジネスに対応できるが、業種固有のデータ構造には限界がある。

✨ カスタムオブジェクト

開発者が自由に定義できるオブジェクト。

「物件」「車両」「サブスクリプション」「イベント」「資格」など、 業種・業務に合わせた独自のデータ構造を HubSpot CRM に追加できる。 Enterprise 必須

💡 カスタムオブジェクトが必要になる典型例

不動産業:「物件」オブジェクト → Contacts(オーナー・入居者)と紐づけて管理
自動車販売:「車両」オブジェクト → Deals(商談)・Contacts(オーナー)と関連付け
SaaS:「サブスクリプション」オブジェクト → Contacts・Deals と紐づけてライフサイクル管理
教育:「コース」「受講履歴」オブジェクト → Contacts の学習進捗を追跡
製造業:「設備」「メンテナンス記録」オブジェクト → Tickets と連携してサポート管理

制限事項: カスタムオブジェクトは Enterprise プランでのみ利用可能です。 1ポータルあたり最大 10個のカスタムオブジェクトを作成できます(プランによって異なる場合あり)。 各オブジェクトには最大 1,000個のプロパティを定義できます。
4-2 スキーマ設計の考え方

実装前にオブジェクト構造・プロパティ・アソシエーションを設計する。

🏗 設計のステップ

① 管理したいエンティティを洗い出す → ② 標準オブジェクトで代替できるか確認 → ③ カスタムオブジェクトの名前・プロパティを定義 → ④ 標準オブジェクトとのアソシエーションを設計 → ⑤ API で実装

設計例:SaaS プロダクトのスキーマ

標準
contacts
──────
カスタム
subscriptions
──────
カスタム
usage_logs
標準
companies
──────
カスタム
subscriptions
──────
標準
deals

subscriptions(カスタム)が contacts・companies・deals を橋渡しする中心オブジェクト

設計の判断基準標準オブジェクトを使うカスタムオブジェクトを使う
データの性質 人・会社・商談・サポートに近い 業種固有のエンティティ(物件・車両・契約など)
プロパティ数 既存プロパティで十分 専用プロパティが10個以上必要
レポート要件 標準レポートで対応可能 カスタムオブジェクト固有の集計が必要
ワークフロー連携 標準ワークフローで対応可能 カスタムオブジェクトをトリガーにしたい
4-3 Schemas API でカスタムオブジェクトを作成する

API を使ってカスタムオブジェクトのスキーマを定義・作成する。

Node.js — カスタムオブジェクト「サブスクリプション」を作成
import { Client } from '@hubspot/api-client'; const client = new Client({ accessToken: process.env.HUBSPOT_ACCESS_TOKEN }); const schema = await client.crm.schemas.coreApi.create({ name: 'subscriptions', // API 名(英小文字・アンダースコア) labels: { singular: 'Subscription', // 単数形ラベル plural: 'Subscriptions', // 複数形ラベル }, primaryDisplayProperty: 'plan_name', // レコード一覧に表示するプロパティ secondaryDisplayProperties: ['status', 'mrr'], properties: [ { name: 'plan_name', label: 'プラン名', type: 'string', fieldType: 'text', groupName: 'subscriptioninformation', }, { name: 'status', label: 'ステータス', type: 'enumeration', fieldType: 'select', groupName: 'subscriptioninformation', options: [ { label: 'アクティブ', value: 'active', displayOrder: 1, hidden: false }, { label: 'トライアル', value: 'trial', displayOrder: 2, hidden: false }, { label: 'キャンセル済み', value: 'cancelled', displayOrder: 3, hidden: false }, { label: '一時停止', value: 'paused', displayOrder: 4, hidden: false }, ], }, { name: 'mrr', label: 'MRR(月次収益)', type: 'number', fieldType: 'number', groupName: 'subscriptioninformation', }, { name: 'start_date', label: '契約開始日', type: 'date', fieldType: 'date', groupName: 'subscriptioninformation', }, { name: 'renewal_date', label: '更新日', type: 'date', fieldType: 'date', groupName: 'subscriptioninformation', }, { name: 'external_id', label: '外部システム ID', type: 'string', fieldType: 'text', groupName: 'subscriptioninformation', hasUniqueValue: true, // 一意制約 }, ], associatedObjects: ['CONTACT', 'COMPANY', 'DEAL'], }); console.log(`Schema created: ${schema.objectTypeId}`); // objectTypeId は後で API 呼び出しに使用(例: "2-12345678")
objectTypeId について: カスタムオブジェクト作成後、objectTypeId(例:2-12345678)が返されます。 以降の API 呼び出しではこの ID を使います。 /crm/v3/objects/2-12345678 のようにパスに含めてアクセスします。
4-4 プロパティ型の詳細と選択基準

プロパティの type・fieldType の組み合わせと、各型のユースケースを整理する。

typefieldType格納値ユースケース
string text テキスト 名前・メモ・URL・コードなど
string textarea 長文テキスト 説明・備考・メモ欄
number number 数値 金額・スコア・数量・座標
enumeration select 1つの選択肢 ステータス・ランク・カテゴリ
enumeration radio 1つの選択肢 Yes/No・優先度(見やすさ重視)
enumeration checkbox セミコロン区切り複数値 タグ・機能フラグ・複数選択
date date YYYY-MM-DD 契約日・期限・誕生日
datetime date Unix ミリ秒 タイムスタンプ・最終ログイン
bool booleancheckbox true / false フラグ・有効/無効・同意
phone_number phonenumber 電話番号文字列 電話番号(国際形式対応)
⚠ enumeration(checkbox)の値の扱い: checkbox 型は複数選択された値をセミコロン(;)区切りの文字列として格納します。 例:"feature_a;feature_b;feature_c" Search API でフィルターする場合は CONTAINS_TOKEN 演算子を使用してください。
Node.js — 後からプロパティを追加する
// 既存のカスタムオブジェクトにプロパティを追加 await client.crm.properties.coreApi.create( '2-12345678', // objectTypeId { name: 'seat_count', label: 'シート数', type: 'number', fieldType: 'number', groupName: 'subscriptioninformation', description: '契約ユーザー数(シート数)', } ); // プロパティグループを作成(関連プロパティをグルーピング) await client.crm.propertyGroups.coreApi.create( '2-12345678', { name: 'billing_info', displayOrder: 2, label: '請求情報', } );
4-5 アソシエーション設計

カスタムオブジェクトと標準・他カスタムオブジェクト間の関係を定義する。

🔗 アソシエーション設計のポイント

カスタムオブジェクト間のアソシエーションは Schemas API でスキーマ定義時に設定するか、 後から Associations Schema API で追加できます。 「1対多」「多対多」どちらの関係も表現可能です。 アソシエーションラベルを使うと、同じオブジェクト間でも「主担当」「副担当」のように関係の種類を区別できます。

Node.js — カスタムアソシエーションラベルの作成
// カスタムオブジェクト → Contacts 間に独自ラベルを追加 // 例:Subscription の「主担当者」「請求担当者」を区別する const assocDef = await client.crm.schemas.associationsApi.create( 'subscriptions', // fromObjectType 'contacts', // toObjectType { label: '主担当者', name: 'primary_contact', category: 'USER_DEFINED', } ); console.log(`Association type ID: ${assocDef.typeId}`); // 逆方向も作成(Contact → Subscription) await client.crm.schemas.associationsApi.create( 'contacts', 'subscriptions', { label: '担当サブスクリプション', name: 'managed_subscription', category: 'USER_DEFINED', } );
Node.js — カスタムアソシエーションラベルを使った関連付け
// サブスクリプションとコンタクトをカスタムラベルで関連付け await client.crm.associations.v4.basicApi.create( 'subscriptions', subscriptionId, 'contacts', contactId, [ { associationCategory: 'USER_DEFINED', associationTypeId: assocDef.typeId, // 上で作成した typeId }, ] ); // 関連するコンタクトをラベル付きで取得 const related = await client.crm.associations.v4.basicApi.getPage( 'subscriptions', subscriptionId, 'contacts' ); // result.associationTypes でラベルを確認できる
4-6 カスタムオブジェクトの CRUD 操作

標準オブジェクトと同じ API パターンで、カスタムオブジェクトのレコードを操作する。

Node.js — レコードの作成・取得・更新・削除
const OBJECT_TYPE = '2-12345678'; // または 'subscriptions'(名前でもOK) // ── 作成 ────────────────────────────── const record = await client.crm.objects.basicApi.create(OBJECT_TYPE, { properties: { plan_name: 'Proプラン', status: 'active', mrr: '49800', start_date: '2026-04-01', renewal_date: '2027-03-31', external_id: 'sub_001', }, associations: [ { to: { id: contactId }, types: [{ associationCategory: 'USER_DEFINED', associationTypeId: primaryTypeId }], }, ], }); // ── 取得 ────────────────────────────── const fetched = await client.crm.objects.basicApi.getById( OBJECT_TYPE, record.id, ['plan_name', 'status', 'mrr', 'renewal_date'] ); // ── 更新 ────────────────────────────── await client.crm.objects.basicApi.update(OBJECT_TYPE, record.id, { properties: { status: 'cancelled', mrr: '0', }, }); // ── Search API でフィルター ──────────── const activeSubscriptions = await client.crm.objects.searchApi.doSearch(OBJECT_TYPE, { filterGroups: [{ filters: [{ propertyName: 'status', operator: 'EQ', value: 'active', }], }], properties: ['plan_name', 'mrr', 'renewal_date'], sorts: [{ propertyName: 'renewal_date', direction: 'ASCENDING' }], limit: 100, });
Node.js — Batch API でカスタムオブジェクトを一括処理
// 外部 DB から全サブスクリプションを同期(upsert パターン) async function upsertSubscriptions(externalData) { const CHUNK = 100; for (let i = 0; i < externalData.length; i += CHUNK) { const chunk = externalData.slice(i, i + CHUNK); await client.crm.objects.batchApi.upsert(OBJECT_TYPE, { inputs: chunk.map(item => ({ idProperty: 'external_id', // unique プロパティで upsert properties: { external_id: item.id, plan_name: item.planName, status: item.status, mrr: String(item.mrr), renewal_date: item.renewalDate, }, })), }); await new Promise(r => setTimeout(r, 200)); } }
Upsert API: batchApi.upsert() は、指定した一意プロパティidProperty)の値が一致するレコードがあれば更新し、 なければ新規作成します。外部システムとの定期同期に非常に便利です。 hasUniqueValue: true のプロパティのみ idProperty に指定できます。
4-7 スキーマの取得・更新・削除

作成済みのカスタムオブジェクトのスキーマを管理する。

Node.js — スキーマ管理操作
// ポータル内の全カスタムオブジェクトスキーマを取得 const allSchemas = await client.crm.schemas.coreApi.getAll(); console.log(allSchemas.results.map(s => ({ name: s.name, objectTypeId: s.objectTypeId, properties: s.properties.length, }))); // 特定スキーマの詳細を取得 const schema = await client.crm.schemas.coreApi.getById('subscriptions'); // スキーマのラベルを更新 await client.crm.schemas.coreApi.update('subscriptions', { labels: { singular: 'サブスクリプション', plural: 'サブスクリプション一覧', }, primaryDisplayProperty: 'plan_name', }); // スキーマを削除(レコードが0件の場合のみ削除可能) await client.crm.schemas.coreApi.archive('subscriptions');
⚠ スキーマ削除の注意: カスタムオブジェクトのスキーマを削除するには、そのオブジェクトの全レコードを先に削除する必要があります。 また、削除したスキーマは復元できません。 本番環境での削除前に必ず Sandbox でテストしてください。
4-8 実践例:外部 DB との定期同期アーキテクチャ

Stripe などの外部サービスのサブスクリプションデータを HubSpot カスタムオブジェクトに同期する設計パターン。

🏛 アーキテクチャ概要

① Stripe Webhook → サブスクリプション変更イベントを受信
② Node.js サーバー(または Serverless Function)→ イベントを処理
③ HubSpot Upsert API → カスタムオブジェクト subscriptions を更新
④ Associations API → Contacts・Deals と紐づけ
⑤ HubSpot Workflow → ステータス変更をトリガーに通知・タスク作成

Node.js — Stripe → HubSpot 同期ハンドラ
import Stripe from 'stripe'; import { Client } from '@hubspot/api-client'; const stripe = new Stripe(process.env.STRIPE_SECRET_KEY); const hubspot = new Client({ accessToken: process.env.HUBSPOT_ACCESS_TOKEN }); async function handleSubscriptionUpdated(stripeSubscription) { const { id: externalId, status, items, current_period_end, customer: customerId, } = stripeSubscription; const mrr = items.data.reduce((sum, item) => sum + (item.price.unit_amount * item.quantity / 100), 0 ); // HubSpot カスタムオブジェクトを Upsert const result = await hubspot.crm.objects.batchApi.upsert('subscriptions', { inputs: [{ idProperty: 'external_id', properties: { external_id: externalId, status: status === 'active' ? 'active' : status === 'trialing' ? 'trial' : 'cancelled', mrr: String(mrr), renewal_date: new Date(current_period_end * 1000).toISOString().split('T')[0], }, }], }); // Stripe CustomerID で HubSpot コンタクトを検索して関連付け const contacts = await hubspot.crm.contacts.searchApi.doSearch({ filterGroups: [{ filters: [{ propertyName: 'stripe_customer_id', operator: 'EQ', value: customerId }], }], properties: ['email'], limit: 1, }); if (contacts.results.length > 0) { const subscriptionId = result.results[0].id; const contactId = contacts.results[0].id; await hubspot.crm.associations.v4.basicApi.create( 'subscriptions', subscriptionId, 'contacts', contactId, [{ associationCategory: 'HUBSPOT_DEFINED', associationTypeId: 1 }] ); } }
4-9 この章のまとめ

次章(CMS Hub 開発——テーマ & テンプレート)に進む前に確認する。

✅ Chapter 4 チェックリスト

  • カスタムオブジェクトが必要な状況(標準オブジェクトでは不十分なケース)を判断できる
  • スキーマ設計の5ステップ(洗い出し→確認→定義→アソシエーション→実装)を理解した
  • Schemas API でカスタムオブジェクトを作成できる
  • objectTypeId の使い方を理解した(2-XXXXXXXX形式)
  • プロパティ型(string/number/enumeration/date/bool)と fieldType の組み合わせを選択できる
  • hasUniqueValue を使った一意制約を設定できる
  • カスタムアソシエーションラベルを作成・適用できる
  • カスタムオブジェクトに対して CRUD・Search・Batch 操作ができる
  • Upsert API を使って外部システムとの差分同期を実装できる
  • Stripe などの外部サービスと HubSpot カスタムオブジェクトを Webhook 経由で同期できる
次章(Chapter 5)について: CMS Hub の開発側——テーマとテンプレートを学びます。HubSpot テーマの構造・HubL テンプレート言語の実践・ グローバルパーシャル・レスポンシブ設計まで、フロントエンド開発者向けの CMS 開発を体系的に解説します。